2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = (
9 Roo.each(document.styleSheets, function(s) {
10 if (s.href.match(/css-bootstrap4/)) {
18 * base class for bootstrap elements.
22 Roo.bootstrap = Roo.bootstrap || {};
24 * @class Roo.bootstrap.Component
25 * @extends Roo.Component
26 * Bootstrap Component base class
27 * @cfg {String} cls css class
28 * @cfg {String} style any extra css
29 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
31 * @cfg {string} dataId cutomer id
32 * @cfg {string} name Specifies name attribute
33 * @cfg {string} tooltip Text for the tooltip
34 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
35 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
38 * Do not use directly - it does not do anything..
39 * @param {Object} config The config object
44 Roo.bootstrap.Component = function(config){
45 Roo.bootstrap.Component.superclass.constructor.call(this, config);
49 * @event childrenrendered
50 * Fires when the children have been rendered..
51 * @param {Roo.bootstrap.Component} this
53 "childrenrendered" : true
61 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
64 allowDomMove : false, // to stop relocations in parent onRender...
74 * Initialize Events for the element
76 initEvents : function() { },
82 can_build_overlaid : true,
84 container_method : false,
91 // returns the parent component..
92 return Roo.ComponentMgr.get(this.parentId)
98 onRender : function(ct, position)
100 // Roo.log("Call onRender: " + this.xtype);
102 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
105 if (this.el.attr('xtype')) {
106 this.el.attr('xtypex', this.el.attr('xtype'));
107 this.el.dom.removeAttribute('xtype');
117 var cfg = Roo.apply({}, this.getAutoCreate());
119 cfg.id = this.id || Roo.id();
121 // fill in the extra attributes
122 if (this.xattr && typeof(this.xattr) =='object') {
123 for (var i in this.xattr) {
124 cfg[i] = this.xattr[i];
129 cfg.dataId = this.dataId;
133 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
136 if (this.style) { // fixme needs to support more complex style data.
137 cfg.style = this.style;
141 cfg.name = this.name;
144 this.el = ct.createChild(cfg, position);
147 this.tooltipEl().attr('tooltip', this.tooltip);
150 if(this.tabIndex !== undefined){
151 this.el.dom.setAttribute('tabIndex', this.tabIndex);
158 * Fetch the element to add children to
159 * @return {Roo.Element} defaults to this.el
161 getChildContainer : function()
166 * Fetch the element to display the tooltip on.
167 * @return {Roo.Element} defaults to this.el
169 tooltipEl : function()
174 * This is really a wrapper for addxtypeChild
175 * it handles stuff relating to flexy:foreach / flexy:if
176 * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
177 * -- this is a bit of a nightmare... and is even more confusing to debug..
182 addxtype : function(tree,cntr)
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186 var cn = Roo.factory(tree); // this is posibly the first of two times that the ctor get's called...
187 cn.parentType = this.xtype; //??
188 cn.parentId = this.id;
189 if (typeof(cn.container_method) == 'string') {
190 cntr = cn.container_method;
194 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
196 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
198 var build_from_html = Roo.XComponent.build_from_html;
200 var is_body = (tree.xtype == 'Body') ;
202 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
204 var self_cntr_el = Roo.get(this[cntr](false));
206 // do not try and build conditional elements
207 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
211 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
212 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
213 //return this.addxtypeChild(tree,cntr, is_body);
214 return this.addxtypeChild(tree, cntr, is_body);
217 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
220 return this.addxtypeChild(Roo.apply({}, tree),cntr);
223 Roo.log('skipping render');
229 if (!build_from_html) {
233 // this i think handles overlaying multiple children of the same type
234 // with the sam eelement.. - which might be buggy..
236 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
242 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
246 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
252 * add a child to this element
253 * - turn the child.cfg into a child_instance
254 * - call child_instance.render( this { getContainerMethod()} )
255 * - loop through the children, and call addxtype.. (reall this) on newly created child.
259 addxtypeChild : function (tree, cntr, is_body)
261 Roo.debug && Roo.log('addxtypeChild:' + cntr);
263 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
266 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
267 (typeof(tree['flexy:foreach']) != 'undefined');
271 skip_children = false;
272 // render the element if it's not BODY.
275 // if parent was disabled, then do not try and create the children..
276 if(!this[cntr](true)){
281 cn = Roo.factory(tree);
283 cn.parentType = this.xtype; //??
284 cn.parentId = this.id;
286 var build_from_html = Roo.XComponent.build_from_html;
289 // does the container contain child eleemnts with 'xtype' attributes.
290 // that match this xtype..
291 // note - when we render we create these as well..
292 // so we should check to see if body has xtype set.
293 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
295 var self_cntr_el = Roo.get(this[cntr](false));
296 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
298 //Roo.log(Roo.XComponent.build_from_html);
299 //Roo.log("got echild:");
302 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
303 // and are not displayed -this causes this to use up the wrong element when matching.
304 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
307 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
308 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
314 //echild.dom.removeAttribute('xtype');
316 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
317 Roo.debug && Roo.log(self_cntr_el);
318 Roo.debug && Roo.log(echild);
319 Roo.debug && Roo.log(cn);
325 // if object has flexy:if - then it may or may not be rendered.
326 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
327 // skip a flexy if element.
328 Roo.debug && Roo.log('skipping render');
329 Roo.debug && Roo.log(tree);
331 Roo.debug && Roo.log('skipping all children');
332 skip_children = true;
337 // actually if flexy:foreach is found, we really want to create
338 // multiple copies here...
340 //Roo.log(this[cntr]());
341 // some elements do not have render methods.. like the layouts...
343 if(this[cntr](true) === false){
348 cn.render && cn.render(this[cntr](true));
351 // then add the element..
358 cn.addxtypeChildren(tree.items, skip_children);
364 * add a number of children to this object,
365 * which in turn calls render...
369 addxtypeChildren: function(child_array, skip_children)
372 if (!child_array || !child_array.length ) {
377 for(var i =0;i < child_array.length;i++) {
381 // Roo.log(['add child', items[i]]);
382 nitems.push(this.addxtype(Roo.apply({}, child_array[i])));
386 this.fireEvent('childrenrendered', this);
396 * xAddChildren - the 'sub-compentized' version of the above idea..
398 xAddChildren: function(child_array, skip_children)
401 if (!child_array || !child_array.length ) {
406 for(var i =0;i < child_array.length;i++) {
410 // Roo.log(['add child', items[i]]);
411 nitems.push(this.xAdd(Roo.apply({}, child_array[i])));
415 this.fireEvent('childrenrendered', this);
421 * add a child to this element
422 * - turn the child.cfg into a child_instance
423 * - call child_instance.render( this { getContainerMethod()} )
424 * - loop through the children, and call addxtype.. (reall this) on newly created child.
430 xAdd : function (tree, cntr, is_body)
432 Roo.debug && Roo.log('xadd:' + cntr);
434 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
436 var parent_ctnr = this[cntr](true);
437 if(parent_ctnr === false){
438 return false; // getChildContainer an return false explicitly to block children being added?
441 throw new Exception("could not find parent Container for item");
444 var cn = Roo.factory(tree);
445 // at this point items[] array may be set..
446 // constructors should not really be building their children?
447 cn.parentType = this.xtype; //??
448 cn.parentId = this.id;
451 cn.render && cn.render(parent_ctnr);
453 cn.xAddChildren(tree.items);
454 delete tree.items; // not really needed?
460 * Set the element that will be used to show or hide
462 setVisibilityEl : function(el)
464 this.visibilityEl = el;
468 * Get the element that will be used to show or hide
470 getVisibilityEl : function()
472 if (typeof(this.visibilityEl) == 'object') {
473 return this.visibilityEl;
476 if (typeof(this.visibilityEl) == 'string') {
477 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
484 * Show a component - removes 'hidden' class
488 if(!this.getVisibilityEl()){
492 this.getVisibilityEl().removeClass(['hidden','d-none']);
494 this.fireEvent('show', this);
499 * Hide a component - adds 'hidden' class
503 if(!this.getVisibilityEl()){
507 this.getVisibilityEl().addClass(['hidden','d-none']);
509 this.fireEvent('hide', this);
522 * @class Roo.bootstrap.Body
523 * @extends Roo.bootstrap.Component
524 * Bootstrap Body class
528 * @param {Object} config The config object
529 * @cfg {DomElement} do_render - if this is set, then the constructor will try and initialize render, using this as the start point
533 Roo.bootstrap.Body = function(config){
535 config = config || {};
537 Roo.bootstrap.Body.superclass.constructor.call(this, config);
538 this.el = Roo.get(config.el ? config.el : document.body );
539 if (this.cls && this.cls.length) {
540 Roo.get(document.body).addClass(this.cls);
542 if (config.do_render) {
543 this.onRender(config.do_render, '');
544 this.xAddChildren(config.items);
549 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
552 is_body : true,// just to make sure it's constructed?
557 onRender : function(ct, position)
559 if (!this.do_render) {
562 this.el = Roo.get(this.do_render);
579 * @class Roo.bootstrap.ButtonGroup
580 * @extends Roo.bootstrap.Component
581 * Bootstrap ButtonGroup class
582 * @cfg {String} size lg | sm | xs (default empty normal)
583 * @cfg {String} align vertical | justified (default none)
584 * @cfg {String} direction up | down (default down)
585 * @cfg {Boolean} toolbar false | true
586 * @cfg {Boolean} btn true | false
591 * @param {Object} config The config object
594 Roo.bootstrap.ButtonGroup = function(config){
595 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
598 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
606 getAutoCreate : function(){
612 cfg.html = this.html || cfg.html;
623 if (['vertical','justified'].indexOf(this.align)!==-1) {
624 cfg.cls = 'btn-group-' + this.align;
626 if (this.align == 'justified') {
627 console.log(this.items);
631 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
632 cfg.cls += ' btn-group-' + this.size;
635 if (this.direction == 'up') {
636 cfg.cls += ' dropup' ;
642 * Add a button to the group (similar to NavItem API.)
644 addItem : function(cfg)
646 var cn = new Roo.bootstrap.Button(cfg);
648 cn.parentId = this.id;
649 cn.onRender(this.el, null);
663 * @class Roo.bootstrap.Button
664 * @extends Roo.bootstrap.Component
665 * Bootstrap Button class
666 * @cfg {String} html The button content
667 * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
668 * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
669 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
670 * @cfg {String} size ( lg | sm | xs)
671 * @cfg {String} tag ( a | input | submit)
672 * @cfg {String} href empty or href
673 * @cfg {Boolean} disabled default false;
674 * @cfg {Boolean} isClose default false;
675 * @cfg {String} glyphicon depricated - use fa
676 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
677 * @cfg {String} badge text for badge
678 * @cfg {String} theme (default|glow)
679 * @cfg {Boolean} inverse dark themed version
680 * @cfg {Boolean} toggle is it a slidy toggle button
681 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
682 * @cfg {String} ontext text for on slidy toggle state
683 * @cfg {String} offtext text for off slidy toggle state
684 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
685 * @cfg {Boolean} removeClass remove the standard class..
686 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
689 * Create a new button
690 * @param {Object} config The config object
694 Roo.bootstrap.Button = function(config){
695 Roo.bootstrap.Button.superclass.constructor.call(this, config);
696 this.weightClass = ["btn-default btn-outline-secondary",
708 * When a butotn is pressed
709 * @param {Roo.bootstrap.Button} btn
710 * @param {Roo.EventObject} e
715 * After the button has been toggles
716 * @param {Roo.bootstrap.Button} btn
717 * @param {Roo.EventObject} e
718 * @param {boolean} pressed (also available as button.pressed)
724 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
745 preventDefault: true,
753 getAutoCreate : function(){
761 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
762 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
767 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
769 if (this.toggle == true) {
772 cls: 'slider-frame roo-button',
777 'data-off-text':'OFF',
778 cls: 'slider-button',
784 if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
785 cfg.cls += ' '+this.weight;
794 cfg["aria-hidden"] = true;
796 cfg.html = "×";
802 if (this.theme==='default') {
803 cfg.cls = 'btn roo-button';
805 //if (this.parentType != 'Navbar') {
806 this.weight = this.weight.length ? this.weight : 'default';
808 if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
810 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
811 var weight = this.weight == 'default' ? 'secondary' : this.weight;
812 cfg.cls += ' btn-' + outline + weight;
813 if (this.weight == 'default') {
815 cfg.cls += ' btn-' + this.weight;
818 } else if (this.theme==='glow') {
821 cfg.cls = 'btn-glow roo-button';
823 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
825 cfg.cls += ' ' + this.weight;
831 this.cls += ' inverse';
835 if (this.active || this.pressed === true) {
836 cfg.cls += ' active';
840 cfg.disabled = 'disabled';
844 Roo.log('changing to ul' );
846 this.glyphicon = 'caret';
847 if (Roo.bootstrap.version == 4) {
848 this.fa = 'caret-down';
853 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
855 //gsRoo.log(this.parentType);
856 if (this.parentType === 'Navbar' && !this.parent().bar) {
857 Roo.log('changing to li?');
866 href : this.href || '#'
869 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
870 cfg.cls += ' dropdown';
877 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
879 if (this.glyphicon) {
880 cfg.html = ' ' + cfg.html;
885 cls: 'glyphicon glyphicon-' + this.glyphicon
890 cfg.html = ' ' + cfg.html;
895 cls: 'fa fas fa-' + this.fa
905 // cfg.cls='btn roo-button';
909 var value = cfg.html;
914 cls: 'glyphicon glyphicon-' + this.glyphicon,
921 cls: 'fa fas fa-' + this.fa,
926 var bw = this.badge_weight.length ? this.badge_weight :
927 (this.weight.length ? this.weight : 'secondary');
928 bw = bw == 'default' ? 'secondary' : bw;
934 cls: 'badge badge-' + bw,
943 cfg.cls += ' dropdown';
944 cfg.html = typeof(cfg.html) != 'undefined' ?
945 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
948 if (cfg.tag !== 'a' && this.href !== '') {
949 throw "Tag must be a to set href.";
950 } else if (this.href.length > 0) {
951 cfg.href = this.href;
954 if(this.removeClass){
959 cfg.target = this.target;
964 initEvents: function() {
965 // Roo.log('init events?');
966 // Roo.log(this.el.dom);
969 if (typeof (this.menu) != 'undefined') {
970 this.menu.parentType = this.xtype;
971 this.menu.triggerEl = this.el;
972 this.addxtype(Roo.apply({}, this.menu));
976 if (this.el.hasClass('roo-button')) {
977 this.el.on('click', this.onClick, this);
979 this.el.select('.roo-button').on('click', this.onClick, this);
982 if(this.removeClass){
983 this.el.on('click', this.onClick, this);
986 this.el.enableDisplayMode();
989 onClick : function(e)
995 Roo.log('button on click ');
996 if(this.preventDefault){
1000 if (this.pressed === true || this.pressed === false) {
1001 this.toggleActive(e);
1005 this.fireEvent('click', this, e);
1009 * Enables this button
1013 this.disabled = false;
1014 this.el.removeClass('disabled');
1018 * Disable this button
1020 disable : function()
1022 this.disabled = true;
1023 this.el.addClass('disabled');
1026 * sets the active state on/off,
1027 * @param {Boolean} state (optional) Force a particular state
1029 setActive : function(v) {
1031 this.el[v ? 'addClass' : 'removeClass']('active');
1035 * toggles the current active state
1037 toggleActive : function(e)
1039 this.setActive(!this.pressed);
1040 this.fireEvent('toggle', this, e, !this.pressed);
1043 * get the current active state
1044 * @return {boolean} true if it's active
1046 isActive : function()
1048 return this.el.hasClass('active');
1051 * set the text of the first selected button
1053 setText : function(str)
1055 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1058 * get the text of the first selected button
1060 getText : function()
1062 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1065 setWeight : function(str)
1067 this.el.removeClass(this.weightClass);
1069 var outline = this.outline ? 'outline-' : '';
1070 if (str == 'default') {
1071 this.el.addClass('btn-default btn-outline-secondary');
1074 this.el.addClass('btn-' + outline + str);
1088 * @class Roo.bootstrap.Column
1089 * @extends Roo.bootstrap.Component
1090 * Bootstrap Column class
1091 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1092 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1093 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1094 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1095 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1096 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1097 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1098 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1101 * @cfg {Boolean} hidden (true|false) hide the element
1102 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1103 * @cfg {String} fa (ban|check|...) font awesome icon
1104 * @cfg {Number} fasize (1|2|....) font awsome size
1106 * @cfg {String} icon (info-sign|check|...) glyphicon name
1108 * @cfg {String} html content of column.
1111 * Create a new Column
1112 * @param {Object} config The config object
1115 Roo.bootstrap.Column = function(config){
1116 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1119 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1137 getAutoCreate : function(){
1138 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1146 ['xs','sm','md','lg'].map(function(size){
1147 //Roo.log( size + ':' + settings[size]);
1149 if (settings[size+'off'] !== false) {
1150 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1153 if (settings[size] === false) {
1157 if (!settings[size]) { // 0 = hidden
1158 cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
1161 cfg.cls += ' col-' + size + '-' + settings[size] + (
1162 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1168 cfg.cls += ' hidden';
1171 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1172 cfg.cls +=' alert alert-' + this.alert;
1176 if (this.html.length) {
1177 cfg.html = this.html;
1181 if (this.fasize > 1) {
1182 fasize = ' fa-' + this.fasize + 'x';
1184 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1189 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1208 * @class Roo.bootstrap.Container
1209 * @extends Roo.bootstrap.Component
1210 * Bootstrap Container class
1211 * @cfg {Boolean} jumbotron is it a jumbotron element
1212 * @cfg {String} html content of element
1213 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1214 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1215 * @cfg {String} header content of header (for panel)
1216 * @cfg {String} footer content of footer (for panel)
1217 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1218 * @cfg {String} tag (header|aside|section) type of HTML tag.
1219 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1220 * @cfg {String} fa font awesome icon
1221 * @cfg {String} icon (info-sign|check|...) glyphicon name
1222 * @cfg {Boolean} hidden (true|false) hide the element
1223 * @cfg {Boolean} expandable (true|false) default false
1224 * @cfg {Boolean} expanded (true|false) default true
1225 * @cfg {String} rheader contet on the right of header
1226 * @cfg {Boolean} clickable (true|false) default false
1230 * Create a new Container
1231 * @param {Object} config The config object
1234 Roo.bootstrap.Container = function(config){
1235 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1241 * After the panel has been expand
1243 * @param {Roo.bootstrap.Container} this
1248 * After the panel has been collapsed
1250 * @param {Roo.bootstrap.Container} this
1255 * When a element is chick
1256 * @param {Roo.bootstrap.Container} this
1257 * @param {Roo.EventObject} e
1263 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1281 getChildContainer : function() {
1287 if (this.panel.length) {
1288 return this.el.select('.panel-body',true).first();
1295 getAutoCreate : function(){
1298 tag : this.tag || 'div',
1302 if (this.jumbotron) {
1303 cfg.cls = 'jumbotron';
1308 // - this is applied by the parent..
1310 // cfg.cls = this.cls + '';
1313 if (this.sticky.length) {
1315 var bd = Roo.get(document.body);
1316 if (!bd.hasClass('bootstrap-sticky')) {
1317 bd.addClass('bootstrap-sticky');
1318 Roo.select('html',true).setStyle('height', '100%');
1321 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1325 if (this.well.length) {
1326 switch (this.well) {
1329 cfg.cls +=' well well-' +this.well;
1338 cfg.cls += ' hidden';
1342 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1343 cfg.cls +=' alert alert-' + this.alert;
1348 if (this.panel.length) {
1349 cfg.cls += ' panel panel-' + this.panel;
1351 if (this.header.length) {
1355 if(this.expandable){
1357 cfg.cls = cfg.cls + ' expandable';
1361 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1369 cls : 'panel-title',
1370 html : (this.expandable ? ' ' : '') + this.header
1374 cls: 'panel-header-right',
1380 cls : 'panel-heading',
1381 style : this.expandable ? 'cursor: pointer' : '',
1389 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1394 if (this.footer.length) {
1396 cls : 'panel-footer',
1405 body.html = this.html || cfg.html;
1406 // prefix with the icons..
1408 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1411 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1416 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1417 cfg.cls = 'container';
1423 initEvents: function()
1425 if(this.expandable){
1426 var headerEl = this.headerEl();
1429 headerEl.on('click', this.onToggleClick, this);
1434 this.el.on('click', this.onClick, this);
1439 onToggleClick : function()
1441 var headerEl = this.headerEl();
1457 if(this.fireEvent('expand', this)) {
1459 this.expanded = true;
1461 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1463 this.el.select('.panel-body',true).first().removeClass('hide');
1465 var toggleEl = this.toggleEl();
1471 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1476 collapse : function()
1478 if(this.fireEvent('collapse', this)) {
1480 this.expanded = false;
1482 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1483 this.el.select('.panel-body',true).first().addClass('hide');
1485 var toggleEl = this.toggleEl();
1491 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1495 toggleEl : function()
1497 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1501 return this.el.select('.panel-heading .fa',true).first();
1504 headerEl : function()
1506 if(!this.el || !this.panel.length || !this.header.length){
1510 return this.el.select('.panel-heading',true).first()
1515 if(!this.el || !this.panel.length){
1519 return this.el.select('.panel-body',true).first()
1522 titleEl : function()
1524 if(!this.el || !this.panel.length || !this.header.length){
1528 return this.el.select('.panel-title',true).first();
1531 setTitle : function(v)
1533 var titleEl = this.titleEl();
1539 titleEl.dom.innerHTML = v;
1542 getTitle : function()
1545 var titleEl = this.titleEl();
1551 return titleEl.dom.innerHTML;
1554 setRightTitle : function(v)
1556 var t = this.el.select('.panel-header-right',true).first();
1562 t.dom.innerHTML = v;
1565 onClick : function(e)
1569 this.fireEvent('click', this, e);
1582 * @class Roo.bootstrap.Img
1583 * @extends Roo.bootstrap.Component
1584 * Bootstrap Img class
1585 * @cfg {Boolean} imgResponsive false | true
1586 * @cfg {String} border rounded | circle | thumbnail
1587 * @cfg {String} src image source
1588 * @cfg {String} alt image alternative text
1589 * @cfg {String} href a tag href
1590 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1591 * @cfg {String} xsUrl xs image source
1592 * @cfg {String} smUrl sm image source
1593 * @cfg {String} mdUrl md image source
1594 * @cfg {String} lgUrl lg image source
1597 * Create a new Input
1598 * @param {Object} config The config object
1601 Roo.bootstrap.Img = function(config){
1602 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1608 * The img click event for the img.
1609 * @param {Roo.EventObject} e
1615 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1617 imgResponsive: true,
1627 getAutoCreate : function()
1629 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1630 return this.createSingleImg();
1635 cls: 'roo-image-responsive-group',
1640 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1642 if(!_this[size + 'Url']){
1648 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1649 html: _this.html || cfg.html,
1650 src: _this[size + 'Url']
1653 img.cls += ' roo-image-responsive-' + size;
1655 var s = ['xs', 'sm', 'md', 'lg'];
1657 s.splice(s.indexOf(size), 1);
1659 Roo.each(s, function(ss){
1660 img.cls += ' hidden-' + ss;
1663 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1664 cfg.cls += ' img-' + _this.border;
1668 cfg.alt = _this.alt;
1681 a.target = _this.target;
1685 cfg.cn.push((_this.href) ? a : img);
1692 createSingleImg : function()
1696 cls: (this.imgResponsive) ? 'img-responsive' : '',
1698 src : 'about:blank' // just incase src get's set to undefined?!?
1701 cfg.html = this.html || cfg.html;
1703 cfg.src = this.src || cfg.src;
1705 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1706 cfg.cls += ' img-' + this.border;
1723 a.target = this.target;
1728 return (this.href) ? a : cfg;
1731 initEvents: function()
1734 this.el.on('click', this.onClick, this);
1739 onClick : function(e)
1741 Roo.log('img onclick');
1742 this.fireEvent('click', this, e);
1745 * Sets the url of the image - used to update it
1746 * @param {String} url the url of the image
1749 setSrc : function(url)
1753 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1754 this.el.dom.src = url;
1758 this.el.select('img', true).first().dom.src = url;
1774 * @class Roo.bootstrap.Link
1775 * @extends Roo.bootstrap.Component
1776 * Bootstrap Link Class
1777 * @cfg {String} alt image alternative text
1778 * @cfg {String} href a tag href
1779 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1780 * @cfg {String} html the content of the link.
1781 * @cfg {String} anchor name for the anchor link
1782 * @cfg {String} fa - favicon
1784 * @cfg {Boolean} preventDefault (true | false) default false
1788 * Create a new Input
1789 * @param {Object} config The config object
1792 Roo.bootstrap.Link = function(config){
1793 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1799 * The img click event for the img.
1800 * @param {Roo.EventObject} e
1806 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1810 preventDefault: false,
1816 getAutoCreate : function()
1818 var html = this.html || '';
1820 if (this.fa !== false) {
1821 html = '<i class="fa fa-' + this.fa + '"></i>';
1826 // anchor's do not require html/href...
1827 if (this.anchor === false) {
1829 cfg.href = this.href || '#';
1831 cfg.name = this.anchor;
1832 if (this.html !== false || this.fa !== false) {
1835 if (this.href !== false) {
1836 cfg.href = this.href;
1840 if(this.alt !== false){
1845 if(this.target !== false) {
1846 cfg.target = this.target;
1852 initEvents: function() {
1854 if(!this.href || this.preventDefault){
1855 this.el.on('click', this.onClick, this);
1859 onClick : function(e)
1861 if(this.preventDefault){
1864 //Roo.log('img onclick');
1865 this.fireEvent('click', this, e);
1878 * @class Roo.bootstrap.Header
1879 * @extends Roo.bootstrap.Component
1880 * Bootstrap Header class
1881 * @cfg {String} html content of header
1882 * @cfg {Number} level (1|2|3|4|5|6) default 1
1885 * Create a new Header
1886 * @param {Object} config The config object
1890 Roo.bootstrap.Header = function(config){
1891 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1894 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1902 getAutoCreate : function(){
1907 tag: 'h' + (1 *this.level),
1908 html: this.html || ''
1920 * Ext JS Library 1.1.1
1921 * Copyright(c) 2006-2007, Ext JS, LLC.
1923 * Originally Released Under LGPL - original licence link has changed is not relivant.
1926 * <script type="text/javascript">
1930 * @class Roo.bootstrap.MenuMgr
1931 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1934 Roo.bootstrap.MenuMgr = function(){
1935 var menus, active, groups = {}, attached = false, lastShow = new Date();
1937 // private - called when first menu is created
1940 active = new Roo.util.MixedCollection();
1941 Roo.get(document).addKeyListener(27, function(){
1942 if(active.length > 0){
1950 if(active && active.length > 0){
1951 var c = active.clone();
1961 if(active.length < 1){
1962 Roo.get(document).un("mouseup", onMouseDown);
1970 var last = active.last();
1971 lastShow = new Date();
1974 Roo.get(document).on("mouseup", onMouseDown);
1979 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1980 m.parentMenu.activeChild = m;
1981 }else if(last && last.isVisible()){
1982 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1987 function onBeforeHide(m){
1989 m.activeChild.hide();
1991 if(m.autoHideTimer){
1992 clearTimeout(m.autoHideTimer);
1993 delete m.autoHideTimer;
1998 function onBeforeShow(m){
1999 var pm = m.parentMenu;
2000 if(!pm && !m.allowOtherMenus){
2002 }else if(pm && pm.activeChild && active != m){
2003 pm.activeChild.hide();
2007 // private this should really trigger on mouseup..
2008 function onMouseDown(e){
2009 Roo.log("on Mouse Up");
2011 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
2012 Roo.log("MenuManager hideAll");
2021 function onBeforeCheck(mi, state){
2023 var g = groups[mi.group];
2024 for(var i = 0, l = g.length; i < l; i++){
2026 g[i].setChecked(false);
2035 * Hides all menus that are currently visible
2037 hideAll : function(){
2042 register : function(menu){
2046 menus[menu.id] = menu;
2047 menu.on("beforehide", onBeforeHide);
2048 menu.on("hide", onHide);
2049 menu.on("beforeshow", onBeforeShow);
2050 menu.on("show", onShow);
2052 if(g && menu.events["checkchange"]){
2056 groups[g].push(menu);
2057 menu.on("checkchange", onCheck);
2062 * Returns a {@link Roo.menu.Menu} object
2063 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
2064 * be used to generate and return a new Menu instance.
2066 get : function(menu){
2067 if(typeof menu == "string"){ // menu id
2069 }else if(menu.events){ // menu instance
2072 /*else if(typeof menu.length == 'number'){ // array of menu items?
2073 return new Roo.bootstrap.Menu({items:menu});
2074 }else{ // otherwise, must be a config
2075 return new Roo.bootstrap.Menu(menu);
2082 unregister : function(menu){
2083 delete menus[menu.id];
2084 menu.un("beforehide", onBeforeHide);
2085 menu.un("hide", onHide);
2086 menu.un("beforeshow", onBeforeShow);
2087 menu.un("show", onShow);
2089 if(g && menu.events["checkchange"]){
2090 groups[g].remove(menu);
2091 menu.un("checkchange", onCheck);
2096 registerCheckable : function(menuItem){
2097 var g = menuItem.group;
2102 groups[g].push(menuItem);
2103 menuItem.on("beforecheckchange", onBeforeCheck);
2108 unregisterCheckable : function(menuItem){
2109 var g = menuItem.group;
2111 groups[g].remove(menuItem);
2112 menuItem.un("beforecheckchange", onBeforeCheck);
2124 * @class Roo.bootstrap.Menu
2125 * @extends Roo.bootstrap.Component
2126 * Bootstrap Menu class - container for MenuItems
2127 * @cfg {String} type (dropdown|treeview|submenu) type of menu
2128 * @cfg {bool} hidden if the menu should be hidden when rendered.
2129 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
2130 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
2134 * @param {Object} config The config object
2138 Roo.bootstrap.Menu = function(config){
2139 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2140 if (this.registerMenu && this.type != 'treeview') {
2141 Roo.bootstrap.MenuMgr.register(this);
2148 * Fires before this menu is displayed (return false to block)
2149 * @param {Roo.menu.Menu} this
2154 * Fires before this menu is hidden (return false to block)
2155 * @param {Roo.menu.Menu} this
2160 * Fires after this menu is displayed
2161 * @param {Roo.menu.Menu} this
2166 * Fires after this menu is hidden
2167 * @param {Roo.menu.Menu} this
2172 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2173 * @param {Roo.menu.Menu} this
2174 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2175 * @param {Roo.EventObject} e
2180 * Fires when the mouse is hovering over this menu
2181 * @param {Roo.menu.Menu} this
2182 * @param {Roo.EventObject} e
2183 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2188 * Fires when the mouse exits this menu
2189 * @param {Roo.menu.Menu} this
2190 * @param {Roo.EventObject} e
2191 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2196 * Fires when a menu item contained in this menu is clicked
2197 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2198 * @param {Roo.EventObject} e
2202 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2205 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2209 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2212 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2214 registerMenu : true,
2216 menuItems :false, // stores the menu items..
2226 getChildContainer : function() {
2230 getAutoCreate : function(){
2232 //if (['right'].indexOf(this.align)!==-1) {
2233 // cfg.cn[1].cls += ' pull-right'
2239 cls : 'dropdown-menu' ,
2240 style : 'z-index:1000'
2244 if (this.type === 'submenu') {
2245 cfg.cls = 'submenu active';
2247 if (this.type === 'treeview') {
2248 cfg.cls = 'treeview-menu';
2253 initEvents : function() {
2255 // Roo.log("ADD event");
2256 // Roo.log(this.triggerEl.dom);
2258 this.triggerEl.on('click', this.onTriggerClick, this);
2260 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2263 if (this.triggerEl.hasClass('nav-item')) {
2264 // dropdown toggle on the 'a' in BS4?
2265 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2267 this.triggerEl.addClass('dropdown-toggle');
2270 this.el.on('touchstart' , this.onTouch, this);
2272 this.el.on('click' , this.onClick, this);
2274 this.el.on("mouseover", this.onMouseOver, this);
2275 this.el.on("mouseout", this.onMouseOut, this);
2279 findTargetItem : function(e)
2281 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2285 //Roo.log(t); Roo.log(t.id);
2287 //Roo.log(this.menuitems);
2288 return this.menuitems.get(t.id);
2290 //return this.items.get(t.menuItemId);
2296 onTouch : function(e)
2298 Roo.log("menu.onTouch");
2299 //e.stopEvent(); this make the user popdown broken
2303 onClick : function(e)
2305 Roo.log("menu.onClick");
2307 var t = this.findTargetItem(e);
2308 if(!t || t.isContainer){
2313 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2314 if(t == this.activeItem && t.shouldDeactivate(e)){
2315 this.activeItem.deactivate();
2316 delete this.activeItem;
2320 this.setActiveItem(t, true);
2328 Roo.log('pass click event');
2332 this.fireEvent("click", this, t, e);
2336 if(!t.href.length || t.href == '#'){
2337 (function() { _this.hide(); }).defer(100);
2342 onMouseOver : function(e){
2343 var t = this.findTargetItem(e);
2346 // if(t.canActivate && !t.disabled){
2347 // this.setActiveItem(t, true);
2351 this.fireEvent("mouseover", this, e, t);
2353 isVisible : function(){
2354 return !this.hidden;
2356 onMouseOut : function(e){
2357 var t = this.findTargetItem(e);
2360 // if(t == this.activeItem && t.shouldDeactivate(e)){
2361 // this.activeItem.deactivate();
2362 // delete this.activeItem;
2365 this.fireEvent("mouseout", this, e, t);
2370 * Displays this menu relative to another element
2371 * @param {String/HTMLElement/Roo.Element} element The element to align to
2372 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2373 * the element (defaults to this.defaultAlign)
2374 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2376 show : function(el, pos, parentMenu)
2378 if (false === this.fireEvent("beforeshow", this)) {
2379 Roo.log("show canceled");
2382 this.parentMenu = parentMenu;
2387 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2390 * Displays this menu at a specific xy position
2391 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2392 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2394 showAt : function(xy, parentMenu, /* private: */_e){
2395 this.parentMenu = parentMenu;
2400 this.fireEvent("beforeshow", this);
2401 //xy = this.el.adjustForConstraints(xy);
2405 this.hideMenuItems();
2406 this.hidden = false;
2407 this.triggerEl.addClass('open');
2408 this.el.addClass('show');
2410 // reassign x when hitting right
2411 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2412 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2415 // reassign y when hitting bottom
2416 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2417 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2420 // but the list may align on trigger left or trigger top... should it be a properity?
2422 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2427 this.fireEvent("show", this);
2433 this.doFocus.defer(50, this);
2437 doFocus : function(){
2439 this.focusEl.focus();
2444 * Hides this menu and optionally all parent menus
2445 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2447 hide : function(deep)
2449 if (false === this.fireEvent("beforehide", this)) {
2450 Roo.log("hide canceled");
2453 this.hideMenuItems();
2454 if(this.el && this.isVisible()){
2456 if(this.activeItem){
2457 this.activeItem.deactivate();
2458 this.activeItem = null;
2460 this.triggerEl.removeClass('open');;
2461 this.el.removeClass('show');
2463 this.fireEvent("hide", this);
2465 if(deep === true && this.parentMenu){
2466 this.parentMenu.hide(true);
2470 onTriggerClick : function(e)
2472 Roo.log('trigger click');
2474 var target = e.getTarget();
2476 Roo.log(target.nodeName.toLowerCase());
2478 if(target.nodeName.toLowerCase() === 'i'){
2484 onTriggerPress : function(e)
2486 Roo.log('trigger press');
2487 //Roo.log(e.getTarget());
2488 // Roo.log(this.triggerEl.dom);
2490 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2491 var pel = Roo.get(e.getTarget());
2492 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2493 Roo.log('is treeview or dropdown?');
2497 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2501 if (this.isVisible()) {
2506 this.show(this.triggerEl, '?', false);
2509 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2516 hideMenuItems : function()
2518 Roo.log("hide Menu Items");
2523 this.el.select('.open',true).each(function(aa) {
2525 aa.removeClass('open');
2529 addxtypeChild : function (tree, cntr) {
2530 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2532 this.menuitems.add(comp);
2544 this.getEl().dom.innerHTML = '';
2545 this.menuitems.clear();
2559 * @class Roo.bootstrap.MenuItem
2560 * @extends Roo.bootstrap.Component
2561 * Bootstrap MenuItem class
2562 * @cfg {String} html the menu label
2563 * @cfg {String} href the link
2564 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2565 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2566 * @cfg {Boolean} active used on sidebars to highlight active itesm
2567 * @cfg {String} fa favicon to show on left of menu item.
2568 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2572 * Create a new MenuItem
2573 * @param {Object} config The config object
2577 Roo.bootstrap.MenuItem = function(config){
2578 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2583 * The raw click event for the entire grid.
2584 * @param {Roo.bootstrap.MenuItem} this
2585 * @param {Roo.EventObject} e
2591 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2595 preventDefault: false,
2596 isContainer : false,
2600 getAutoCreate : function(){
2602 if(this.isContainer){
2605 cls: 'dropdown-menu-item '
2615 cls : 'dropdown-item',
2620 if (this.fa !== false) {
2623 cls : 'fa fa-' + this.fa
2632 cls: 'dropdown-menu-item',
2635 if (this.parent().type == 'treeview') {
2636 cfg.cls = 'treeview-menu';
2639 cfg.cls += ' active';
2644 anc.href = this.href || cfg.cn[0].href ;
2645 ctag.html = this.html || cfg.cn[0].html ;
2649 initEvents: function()
2651 if (this.parent().type == 'treeview') {
2652 this.el.select('a').on('click', this.onClick, this);
2656 this.menu.parentType = this.xtype;
2657 this.menu.triggerEl = this.el;
2658 this.menu = this.addxtype(Roo.apply({}, this.menu));
2662 onClick : function(e)
2664 Roo.log('item on click ');
2666 if(this.preventDefault){
2669 //this.parent().hideMenuItems();
2671 this.fireEvent('click', this, e);
2690 * @class Roo.bootstrap.MenuSeparator
2691 * @extends Roo.bootstrap.Component
2692 * Bootstrap MenuSeparator class
2695 * Create a new MenuItem
2696 * @param {Object} config The config object
2700 Roo.bootstrap.MenuSeparator = function(config){
2701 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2704 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2706 getAutoCreate : function(){
2725 * @class Roo.bootstrap.Modal
2726 * @extends Roo.bootstrap.Component
2727 * Bootstrap Modal class
2728 * @cfg {String} title Title of dialog
2729 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2730 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2731 * @cfg {Boolean} specificTitle default false
2732 * @cfg {Array} buttons Array of buttons or standard button set..
2733 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2734 * @cfg {Boolean} animate default true
2735 * @cfg {Boolean} allow_close default true
2736 * @cfg {Boolean} fitwindow default false
2737 * @cfg {String} size (sm|lg) default empty
2738 * @cfg {Number} max_width set the max width of modal
2742 * Create a new Modal Dialog
2743 * @param {Object} config The config object
2746 Roo.bootstrap.Modal = function(config){
2747 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2752 * The raw btnclick event for the button
2753 * @param {Roo.EventObject} e
2758 * Fire when dialog resize
2759 * @param {Roo.bootstrap.Modal} this
2760 * @param {Roo.EventObject} e
2764 this.buttons = this.buttons || [];
2767 this.tmpl = Roo.factory(this.tmpl);
2772 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2774 title : 'test dialog',
2784 specificTitle: false,
2786 buttonPosition: 'right',
2809 onRender : function(ct, position)
2811 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2814 var cfg = Roo.apply({}, this.getAutoCreate());
2817 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2819 //if (!cfg.name.length) {
2823 cfg.cls += ' ' + this.cls;
2826 cfg.style = this.style;
2828 this.el = Roo.get(document.body).createChild(cfg, position);
2830 //var type = this.el.dom.type;
2833 if(this.tabIndex !== undefined){
2834 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2837 this.dialogEl = this.el.select('.modal-dialog',true).first();
2838 this.bodyEl = this.el.select('.modal-body',true).first();
2839 this.closeEl = this.el.select('.modal-header .close', true).first();
2840 this.headerEl = this.el.select('.modal-header',true).first();
2841 this.titleEl = this.el.select('.modal-title',true).first();
2842 this.footerEl = this.el.select('.modal-footer',true).first();
2844 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2846 //this.el.addClass("x-dlg-modal");
2848 if (this.buttons.length) {
2849 Roo.each(this.buttons, function(bb) {
2850 var b = Roo.apply({}, bb);
2851 b.xns = b.xns || Roo.bootstrap;
2852 b.xtype = b.xtype || 'Button';
2853 if (typeof(b.listeners) == 'undefined') {
2854 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2857 var btn = Roo.factory(b);
2859 btn.render(this.getButtonContainer());
2863 // render the children.
2866 if(typeof(this.items) != 'undefined'){
2867 var items = this.items;
2870 for(var i =0;i < items.length;i++) {
2871 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2875 this.items = nitems;
2877 // where are these used - they used to be body/close/footer
2881 //this.el.addClass([this.fieldClass, this.cls]);
2885 getAutoCreate : function()
2889 html : this.html || ''
2894 cls : 'modal-title',
2898 if(this.specificTitle){
2904 if (this.allow_close && Roo.bootstrap.version == 3) {
2914 if (this.allow_close && Roo.bootstrap.version == 4) {
2924 if(this.size.length){
2925 size = 'modal-' + this.size;
2928 var footer = Roo.bootstrap.version == 3 ?
2930 cls : 'modal-footer',
2934 cls: 'btn-' + this.buttonPosition
2939 { // BS4 uses mr-auto on left buttons....
2940 cls : 'modal-footer'
2951 cls: "modal-dialog " + size,
2954 cls : "modal-content",
2957 cls : 'modal-header',
2972 modal.cls += ' fade';
2978 getChildContainer : function() {
2983 getButtonContainer : function() {
2985 return Roo.bootstrap.version == 4 ?
2986 this.el.select('.modal-footer',true).first()
2987 : this.el.select('.modal-footer div',true).first();
2990 initEvents : function()
2992 if (this.allow_close) {
2993 this.closeEl.on('click', this.hide, this);
2995 Roo.EventManager.onWindowResize(this.resize, this, true);
3003 this.maskEl.setSize(
3004 Roo.lib.Dom.getViewWidth(true),
3005 Roo.lib.Dom.getViewHeight(true)
3008 if (this.fitwindow) {
3012 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
3013 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
3018 if(this.max_width !== 0) {
3020 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
3023 this.setSize(w, this.height);
3027 if(this.max_height) {
3028 this.setSize(w,Math.min(
3030 Roo.lib.Dom.getViewportHeight(true) - 60
3036 if(!this.fit_content) {
3037 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
3041 this.setSize(w, Math.min(
3043 this.headerEl.getHeight() +
3044 this.footerEl.getHeight() +
3045 this.getChildHeight(this.bodyEl.dom.childNodes),
3046 Roo.lib.Dom.getViewportHeight(true) - 60)
3052 setSize : function(w,h)
3063 if (!this.rendered) {
3067 //this.el.setStyle('display', 'block');
3068 this.el.removeClass('hideing');
3069 this.el.dom.style.display='block';
3071 Roo.get(document.body).addClass('modal-open');
3073 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
3076 this.el.addClass('show');
3077 this.el.addClass('in');
3080 this.el.addClass('show');
3081 this.el.addClass('in');
3084 // not sure how we can show data in here..
3086 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3089 Roo.get(document.body).addClass("x-body-masked");
3091 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3092 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3093 this.maskEl.dom.style.display = 'block';
3094 this.maskEl.addClass('show');
3099 this.fireEvent('show', this);
3101 // set zindex here - otherwise it appears to be ignored...
3102 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3105 this.items.forEach( function(e) {
3106 e.layout ? e.layout() : false;
3114 if(this.fireEvent("beforehide", this) !== false){
3116 this.maskEl.removeClass('show');
3118 this.maskEl.dom.style.display = '';
3119 Roo.get(document.body).removeClass("x-body-masked");
3120 this.el.removeClass('in');
3121 this.el.select('.modal-dialog', true).first().setStyle('transform','');
3123 if(this.animate){ // why
3124 this.el.addClass('hideing');
3125 this.el.removeClass('show');
3127 if (!this.el.hasClass('hideing')) {
3128 return; // it's been shown again...
3131 this.el.dom.style.display='';
3133 Roo.get(document.body).removeClass('modal-open');
3134 this.el.removeClass('hideing');
3138 this.el.removeClass('show');
3139 this.el.dom.style.display='';
3140 Roo.get(document.body).removeClass('modal-open');
3143 this.fireEvent('hide', this);
3146 isVisible : function()
3149 return this.el.hasClass('show') && !this.el.hasClass('hideing');
3153 addButton : function(str, cb)
3157 var b = Roo.apply({}, { html : str } );
3158 b.xns = b.xns || Roo.bootstrap;
3159 b.xtype = b.xtype || 'Button';
3160 if (typeof(b.listeners) == 'undefined') {
3161 b.listeners = { click : cb.createDelegate(this) };
3164 var btn = Roo.factory(b);
3166 btn.render(this.getButtonContainer());
3172 setDefaultButton : function(btn)
3174 //this.el.select('.modal-footer').()
3177 resizeTo: function(w,h)
3179 this.dialogEl.setWidth(w);
3181 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
3183 this.bodyEl.setHeight(h - diff);
3185 this.fireEvent('resize', this);
3188 setContentSize : function(w, h)
3192 onButtonClick: function(btn,e)
3195 this.fireEvent('btnclick', btn.name, e);
3198 * Set the title of the Dialog
3199 * @param {String} str new Title
3201 setTitle: function(str) {
3202 this.titleEl.dom.innerHTML = str;
3205 * Set the body of the Dialog
3206 * @param {String} str new Title
3208 setBody: function(str) {
3209 this.bodyEl.dom.innerHTML = str;
3212 * Set the body of the Dialog using the template
3213 * @param {Obj} data - apply this data to the template and replace the body contents.
3215 applyBody: function(obj)
3218 Roo.log("Error - using apply Body without a template");
3221 this.tmpl.overwrite(this.bodyEl, obj);
3224 getChildHeight : function(child_nodes)
3228 child_nodes.length == 0
3233 var child_height = 0;
3235 for(var i = 0; i < child_nodes.length; i++) {
3238 * for modal with tabs...
3239 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3241 var layout_childs = child_nodes[i].childNodes;
3243 for(var j = 0; j < layout_childs.length; j++) {
3245 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3247 var layout_body_childs = layout_childs[j].childNodes;
3249 for(var k = 0; k < layout_body_childs.length; k++) {
3251 if(layout_body_childs[k].classList.contains('navbar')) {
3252 child_height += layout_body_childs[k].offsetHeight;
3256 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3258 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3260 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3262 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3263 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3278 child_height += child_nodes[i].offsetHeight;
3279 // Roo.log(child_nodes[i].offsetHeight);
3282 return child_height;
3288 Roo.apply(Roo.bootstrap.Modal, {
3290 * Button config that displays a single OK button
3299 * Button config that displays Yes and No buttons
3315 * Button config that displays OK and Cancel buttons
3330 * Button config that displays Yes, No and Cancel buttons
3354 * messagebox - can be used as a replace
3358 * @class Roo.MessageBox
3359 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3363 Roo.Msg.alert('Status', 'Changes saved successfully.');
3365 // Prompt for user data:
3366 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3368 // process text value...
3372 // Show a dialog using config options:
3374 title:'Save Changes?',
3375 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3376 buttons: Roo.Msg.YESNOCANCEL,
3383 Roo.bootstrap.MessageBox = function(){
3384 var dlg, opt, mask, waitTimer;
3385 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3386 var buttons, activeTextEl, bwidth;
3390 var handleButton = function(button){
3392 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3396 var handleHide = function(){
3398 dlg.el.removeClass(opt.cls);
3401 // Roo.TaskMgr.stop(waitTimer);
3402 // waitTimer = null;
3407 var updateButtons = function(b){
3410 buttons["ok"].hide();
3411 buttons["cancel"].hide();
3412 buttons["yes"].hide();
3413 buttons["no"].hide();
3414 dlg.footerEl.hide();
3418 dlg.footerEl.show();
3419 for(var k in buttons){
3420 if(typeof buttons[k] != "function"){
3423 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3424 width += buttons[k].el.getWidth()+15;
3434 var handleEsc = function(d, k, e){
3435 if(opt && opt.closable !== false){
3445 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3446 * @return {Roo.BasicDialog} The BasicDialog element
3448 getDialog : function(){
3450 dlg = new Roo.bootstrap.Modal( {
3453 //constraintoviewport:false,
3455 //collapsible : false,
3460 //buttonAlign:"center",
3461 closeClick : function(){
3462 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3465 handleButton("cancel");
3470 dlg.on("hide", handleHide);
3472 //dlg.addKeyListener(27, handleEsc);
3474 this.buttons = buttons;
3475 var bt = this.buttonText;
3476 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3477 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3478 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3479 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3481 bodyEl = dlg.bodyEl.createChild({
3483 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3484 '<textarea class="roo-mb-textarea"></textarea>' +
3485 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3487 msgEl = bodyEl.dom.firstChild;
3488 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3489 textboxEl.enableDisplayMode();
3490 textboxEl.addKeyListener([10,13], function(){
3491 if(dlg.isVisible() && opt && opt.buttons){
3494 }else if(opt.buttons.yes){
3495 handleButton("yes");
3499 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3500 textareaEl.enableDisplayMode();
3501 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3502 progressEl.enableDisplayMode();
3504 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3505 var pf = progressEl.dom.firstChild;
3507 pp = Roo.get(pf.firstChild);
3508 pp.setHeight(pf.offsetHeight);
3516 * Updates the message box body text
3517 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3518 * the XHTML-compliant non-breaking space character '&#160;')
3519 * @return {Roo.MessageBox} This message box
3521 updateText : function(text)
3523 if(!dlg.isVisible() && !opt.width){
3524 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3525 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3527 msgEl.innerHTML = text || ' ';
3529 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3530 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3532 Math.min(opt.width || cw , this.maxWidth),
3533 Math.max(opt.minWidth || this.minWidth, bwidth)
3536 activeTextEl.setWidth(w);
3538 if(dlg.isVisible()){
3539 dlg.fixedcenter = false;
3541 // to big, make it scroll. = But as usual stupid IE does not support
3544 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3545 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3546 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3548 bodyEl.dom.style.height = '';
3549 bodyEl.dom.style.overflowY = '';
3552 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3554 bodyEl.dom.style.overflowX = '';
3557 dlg.setContentSize(w, bodyEl.getHeight());
3558 if(dlg.isVisible()){
3559 dlg.fixedcenter = true;
3565 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3566 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3567 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3568 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3569 * @return {Roo.MessageBox} This message box
3571 updateProgress : function(value, text){
3573 this.updateText(text);
3576 if (pp) { // weird bug on my firefox - for some reason this is not defined
3577 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3578 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3584 * Returns true if the message box is currently displayed
3585 * @return {Boolean} True if the message box is visible, else false
3587 isVisible : function(){
3588 return dlg && dlg.isVisible();
3592 * Hides the message box if it is displayed
3595 if(this.isVisible()){
3601 * Displays a new message box, or reinitializes an existing message box, based on the config options
3602 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3603 * The following config object properties are supported:
3605 Property Type Description
3606 ---------- --------------- ------------------------------------------------------------------------------------
3607 animEl String/Element An id or Element from which the message box should animate as it opens and
3608 closes (defaults to undefined)
3609 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3610 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3611 closable Boolean False to hide the top-right close button (defaults to true). Note that
3612 progress and wait dialogs will ignore this property and always hide the
3613 close button as they can only be closed programmatically.
3614 cls String A custom CSS class to apply to the message box element
3615 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3616 displayed (defaults to 75)
3617 fn Function A callback function to execute after closing the dialog. The arguments to the
3618 function will be btn (the name of the button that was clicked, if applicable,
3619 e.g. "ok"), and text (the value of the active text field, if applicable).
3620 Progress and wait dialogs will ignore this option since they do not respond to
3621 user actions and can only be closed programmatically, so any required function
3622 should be called by the same code after it closes the dialog.
3623 icon String A CSS class that provides a background image to be used as an icon for
3624 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3625 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3626 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3627 modal Boolean False to allow user interaction with the page while the message box is
3628 displayed (defaults to true)
3629 msg String A string that will replace the existing message box body text (defaults
3630 to the XHTML-compliant non-breaking space character ' ')
3631 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3632 progress Boolean True to display a progress bar (defaults to false)
3633 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3634 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3635 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3636 title String The title text
3637 value String The string value to set into the active textbox element if displayed
3638 wait Boolean True to display a progress bar (defaults to false)
3639 width Number The width of the dialog in pixels
3646 msg: 'Please enter your address:',
3648 buttons: Roo.MessageBox.OKCANCEL,
3651 animEl: 'addAddressBtn'
3654 * @param {Object} config Configuration options
3655 * @return {Roo.MessageBox} This message box
3657 show : function(options)
3660 // this causes nightmares if you show one dialog after another
3661 // especially on callbacks..
3663 if(this.isVisible()){
3666 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3667 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3668 Roo.log("New Dialog Message:" + options.msg )
3669 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3670 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3673 var d = this.getDialog();
3675 d.setTitle(opt.title || " ");
3676 d.closeEl.setDisplayed(opt.closable !== false);
3677 activeTextEl = textboxEl;
3678 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3683 textareaEl.setHeight(typeof opt.multiline == "number" ?
3684 opt.multiline : this.defaultTextHeight);
3685 activeTextEl = textareaEl;
3694 progressEl.setDisplayed(opt.progress === true);
3696 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3698 this.updateProgress(0);
3699 activeTextEl.dom.value = opt.value || "";
3701 dlg.setDefaultButton(activeTextEl);
3703 var bs = opt.buttons;
3707 }else if(bs && bs.yes){
3708 db = buttons["yes"];
3710 dlg.setDefaultButton(db);
3712 bwidth = updateButtons(opt.buttons);
3713 this.updateText(opt.msg);
3715 d.el.addClass(opt.cls);
3717 d.proxyDrag = opt.proxyDrag === true;
3718 d.modal = opt.modal !== false;
3719 d.mask = opt.modal !== false ? mask : false;
3721 // force it to the end of the z-index stack so it gets a cursor in FF
3722 document.body.appendChild(dlg.el.dom);
3723 d.animateTarget = null;
3724 d.show(options.animEl);
3730 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3731 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3732 * and closing the message box when the process is complete.
3733 * @param {String} title The title bar text
3734 * @param {String} msg The message box body text
3735 * @return {Roo.MessageBox} This message box
3737 progress : function(title, msg){
3744 minWidth: this.minProgressWidth,
3751 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3752 * If a callback function is passed it will be called after the user clicks the button, and the
3753 * id of the button that was clicked will be passed as the only parameter to the callback
3754 * (could also be the top-right close button).
3755 * @param {String} title The title bar text
3756 * @param {String} msg The message box body text
3757 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3758 * @param {Object} scope (optional) The scope of the callback function
3759 * @return {Roo.MessageBox} This message box
3761 alert : function(title, msg, fn, scope)
3776 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3777 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3778 * You are responsible for closing the message box when the process is complete.
3779 * @param {String} msg The message box body text
3780 * @param {String} title (optional) The title bar text
3781 * @return {Roo.MessageBox} This message box
3783 wait : function(msg, title){
3794 waitTimer = Roo.TaskMgr.start({
3796 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3804 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3805 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3806 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3807 * @param {String} title The title bar text
3808 * @param {String} msg The message box body text
3809 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3810 * @param {Object} scope (optional) The scope of the callback function
3811 * @return {Roo.MessageBox} This message box
3813 confirm : function(title, msg, fn, scope){
3817 buttons: this.YESNO,
3826 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3827 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3828 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3829 * (could also be the top-right close button) and the text that was entered will be passed as the two
3830 * parameters to the callback.
3831 * @param {String} title The title bar text
3832 * @param {String} msg The message box body text
3833 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3834 * @param {Object} scope (optional) The scope of the callback function
3835 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3836 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3837 * @return {Roo.MessageBox} This message box
3839 prompt : function(title, msg, fn, scope, multiline){
3843 buttons: this.OKCANCEL,
3848 multiline: multiline,
3855 * Button config that displays a single OK button
3860 * Button config that displays Yes and No buttons
3863 YESNO : {yes:true, no:true},
3865 * Button config that displays OK and Cancel buttons
3868 OKCANCEL : {ok:true, cancel:true},
3870 * Button config that displays Yes, No and Cancel buttons
3873 YESNOCANCEL : {yes:true, no:true, cancel:true},
3876 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3879 defaultTextHeight : 75,
3881 * The maximum width in pixels of the message box (defaults to 600)
3886 * The minimum width in pixels of the message box (defaults to 100)
3891 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3892 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3895 minProgressWidth : 250,
3897 * An object containing the default button text strings that can be overriden for localized language support.
3898 * Supported properties are: ok, cancel, yes and no.
3899 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3912 * Shorthand for {@link Roo.MessageBox}
3914 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3915 Roo.Msg = Roo.Msg || Roo.MessageBox;
3924 * @class Roo.bootstrap.Navbar
3925 * @extends Roo.bootstrap.Component
3926 * Bootstrap Navbar class
3929 * Create a new Navbar
3930 * @param {Object} config The config object
3934 Roo.bootstrap.Navbar = function(config){
3935 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3939 * @event beforetoggle
3940 * Fire before toggle the menu
3941 * @param {Roo.EventObject} e
3943 "beforetoggle" : true
3947 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3956 getAutoCreate : function(){
3959 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3963 initEvents :function ()
3965 //Roo.log(this.el.select('.navbar-toggle',true));
3966 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3973 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3975 var size = this.el.getSize();
3976 this.maskEl.setSize(size.width, size.height);
3977 this.maskEl.enableDisplayMode("block");
3986 getChildContainer : function()
3988 if (this.el && this.el.select('.collapse').getCount()) {
3989 return this.el.select('.collapse',true).first();
4004 onToggle : function()
4007 if(this.fireEvent('beforetoggle', this) === false){
4010 var ce = this.el.select('.navbar-collapse',true).first();
4012 if (!ce.hasClass('show')) {
4022 * Expand the navbar pulldown
4024 expand : function ()
4027 var ce = this.el.select('.navbar-collapse',true).first();
4028 if (ce.hasClass('collapsing')) {
4031 ce.dom.style.height = '';
4033 ce.addClass('in'); // old...
4034 ce.removeClass('collapse');
4035 ce.addClass('show');
4036 var h = ce.getHeight();
4038 ce.removeClass('show');
4039 // at this point we should be able to see it..
4040 ce.addClass('collapsing');
4042 ce.setHeight(0); // resize it ...
4043 ce.on('transitionend', function() {
4044 //Roo.log('done transition');
4045 ce.removeClass('collapsing');
4046 ce.addClass('show');
4047 ce.removeClass('collapse');
4049 ce.dom.style.height = '';
4050 }, this, { single: true} );
4052 ce.dom.scrollTop = 0;
4055 * Collapse the navbar pulldown
4057 collapse : function()
4059 var ce = this.el.select('.navbar-collapse',true).first();
4061 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
4062 // it's collapsed or collapsing..
4065 ce.removeClass('in'); // old...
4066 ce.setHeight(ce.getHeight());
4067 ce.removeClass('show');
4068 ce.addClass('collapsing');
4070 ce.on('transitionend', function() {
4071 ce.dom.style.height = '';
4072 ce.removeClass('collapsing');
4073 ce.addClass('collapse');
4074 }, this, { single: true} );
4094 * @class Roo.bootstrap.NavSimplebar
4095 * @extends Roo.bootstrap.Navbar
4096 * Bootstrap Sidebar class
4098 * @cfg {Boolean} inverse is inverted color
4100 * @cfg {String} type (nav | pills | tabs)
4101 * @cfg {Boolean} arrangement stacked | justified
4102 * @cfg {String} align (left | right) alignment
4104 * @cfg {Boolean} main (true|false) main nav bar? default false
4105 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4107 * @cfg {String} tag (header|footer|nav|div) default is nav
4109 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4113 * Create a new Sidebar
4114 * @param {Object} config The config object
4118 Roo.bootstrap.NavSimplebar = function(config){
4119 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4122 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
4138 getAutoCreate : function(){
4142 tag : this.tag || 'div',
4143 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4145 if (['light','white'].indexOf(this.weight) > -1) {
4146 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4148 cfg.cls += ' bg-' + this.weight;
4151 cfg.cls += ' navbar-inverse';
4155 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4157 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4166 cls: 'nav nav-' + this.xtype,
4172 this.type = this.type || 'nav';
4173 if (['tabs','pills'].indexOf(this.type) != -1) {
4174 cfg.cn[0].cls += ' nav-' + this.type
4178 if (this.type!=='nav') {
4179 Roo.log('nav type must be nav/tabs/pills')
4181 cfg.cn[0].cls += ' navbar-nav'
4187 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4188 cfg.cn[0].cls += ' nav-' + this.arrangement;
4192 if (this.align === 'right') {
4193 cfg.cn[0].cls += ' navbar-right';
4218 * navbar-expand-md fixed-top
4222 * @class Roo.bootstrap.NavHeaderbar
4223 * @extends Roo.bootstrap.NavSimplebar
4224 * Bootstrap Sidebar class
4226 * @cfg {String} brand what is brand
4227 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4228 * @cfg {String} brand_href href of the brand
4229 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
4230 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4231 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4232 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4235 * Create a new Sidebar
4236 * @param {Object} config The config object
4240 Roo.bootstrap.NavHeaderbar = function(config){
4241 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4245 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
4252 desktopCenter : false,
4255 getAutoCreate : function(){
4258 tag: this.nav || 'nav',
4259 cls: 'navbar navbar-expand-md',
4265 if (this.desktopCenter) {
4266 cn.push({cls : 'container', cn : []});
4274 cls: 'navbar-toggle navbar-toggler',
4275 'data-toggle': 'collapse',
4280 html: 'Toggle navigation'
4284 cls: 'icon-bar navbar-toggler-icon'
4297 cn.push( Roo.bootstrap.version == 4 ? btn : {
4299 cls: 'navbar-header',
4308 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4312 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4314 if (['light','white'].indexOf(this.weight) > -1) {
4315 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4317 cfg.cls += ' bg-' + this.weight;
4320 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4321 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4323 // tag can override this..
4325 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4328 if (this.brand !== '') {
4329 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4330 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4332 href: this.brand_href ? this.brand_href : '#',
4333 cls: 'navbar-brand',
4341 cfg.cls += ' main-nav';
4349 getHeaderChildContainer : function()
4351 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4352 return this.el.select('.navbar-header',true).first();
4355 return this.getChildContainer();
4359 initEvents : function()
4361 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4363 if (this.autohide) {
4368 Roo.get(document).on('scroll',function(e) {
4369 var ns = Roo.get(document).getScroll().top;
4370 var os = prevScroll;
4374 ft.removeClass('slideDown');
4375 ft.addClass('slideUp');
4378 ft.removeClass('slideUp');
4379 ft.addClass('slideDown');
4400 * @class Roo.bootstrap.NavSidebar
4401 * @extends Roo.bootstrap.Navbar
4402 * Bootstrap Sidebar class
4405 * Create a new Sidebar
4406 * @param {Object} config The config object
4410 Roo.bootstrap.NavSidebar = function(config){
4411 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4414 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4416 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4418 getAutoCreate : function(){
4423 cls: 'sidebar sidebar-nav'
4445 * @class Roo.bootstrap.NavGroup
4446 * @extends Roo.bootstrap.Component
4447 * Bootstrap NavGroup class
4448 * @cfg {String} align (left|right)
4449 * @cfg {Boolean} inverse
4450 * @cfg {String} type (nav|pills|tab) default nav
4451 * @cfg {String} navId - reference Id for navbar.
4455 * Create a new nav group
4456 * @param {Object} config The config object
4459 Roo.bootstrap.NavGroup = function(config){
4460 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4463 Roo.bootstrap.NavGroup.register(this);
4467 * Fires when the active item changes
4468 * @param {Roo.bootstrap.NavGroup} this
4469 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4470 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4477 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4488 getAutoCreate : function()
4490 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4496 if (Roo.bootstrap.version == 4) {
4497 if (['tabs','pills'].indexOf(this.type) != -1) {
4498 cfg.cls += ' nav-' + this.type;
4500 // trying to remove so header bar can right align top?
4501 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4502 // do not use on header bar...
4503 cfg.cls += ' navbar-nav';
4508 if (['tabs','pills'].indexOf(this.type) != -1) {
4509 cfg.cls += ' nav-' + this.type
4511 if (this.type !== 'nav') {
4512 Roo.log('nav type must be nav/tabs/pills')
4514 cfg.cls += ' navbar-nav'
4518 if (this.parent() && this.parent().sidebar) {
4521 cls: 'dashboard-menu sidebar-menu'
4527 if (this.form === true) {
4530 cls: 'navbar-form form-inline'
4532 //nav navbar-right ml-md-auto
4533 if (this.align === 'right') {
4534 cfg.cls += ' navbar-right ml-md-auto';
4536 cfg.cls += ' navbar-left';
4540 if (this.align === 'right') {
4541 cfg.cls += ' navbar-right ml-md-auto';
4543 cfg.cls += ' mr-auto';
4547 cfg.cls += ' navbar-inverse';
4555 * sets the active Navigation item
4556 * @param {Roo.bootstrap.NavItem} the new current navitem
4558 setActiveItem : function(item)
4561 Roo.each(this.navItems, function(v){
4566 v.setActive(false, true);
4573 item.setActive(true, true);
4574 this.fireEvent('changed', this, item, prev);
4579 * gets the active Navigation item
4580 * @return {Roo.bootstrap.NavItem} the current navitem
4582 getActive : function()
4586 Roo.each(this.navItems, function(v){
4597 indexOfNav : function()
4601 Roo.each(this.navItems, function(v,i){
4612 * adds a Navigation item
4613 * @param {Roo.bootstrap.NavItem} the navitem to add
4615 addItem : function(cfg)
4617 if (this.form && Roo.bootstrap.version == 4) {
4620 var cn = new Roo.bootstrap.NavItem(cfg);
4622 cn.parentId = this.id;
4623 cn.onRender(this.el, null);
4627 * register a Navigation item
4628 * @param {Roo.bootstrap.NavItem} the navitem to add
4630 register : function(item)
4632 this.navItems.push( item);
4633 item.navId = this.navId;
4638 * clear all the Navigation item
4641 clearAll : function()
4644 this.el.dom.innerHTML = '';
4647 getNavItem: function(tabId)
4650 Roo.each(this.navItems, function(e) {
4651 if (e.tabId == tabId) {
4661 setActiveNext : function()
4663 var i = this.indexOfNav(this.getActive());
4664 if (i > this.navItems.length) {
4667 this.setActiveItem(this.navItems[i+1]);
4669 setActivePrev : function()
4671 var i = this.indexOfNav(this.getActive());
4675 this.setActiveItem(this.navItems[i-1]);
4677 clearWasActive : function(except) {
4678 Roo.each(this.navItems, function(e) {
4679 if (e.tabId != except.tabId && e.was_active) {
4680 e.was_active = false;
4687 getWasActive : function ()
4690 Roo.each(this.navItems, function(e) {
4705 Roo.apply(Roo.bootstrap.NavGroup, {
4709 * register a Navigation Group
4710 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4712 register : function(navgrp)
4714 this.groups[navgrp.navId] = navgrp;
4718 * fetch a Navigation Group based on the navigation ID
4719 * @param {string} the navgroup to add
4720 * @returns {Roo.bootstrap.NavGroup} the navgroup
4722 get: function(navId) {
4723 if (typeof(this.groups[navId]) == 'undefined') {
4725 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4727 return this.groups[navId] ;
4742 * @class Roo.bootstrap.NavItem
4743 * @extends Roo.bootstrap.Component
4744 * Bootstrap Navbar.NavItem class
4745 * @cfg {String} href link to
4746 * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4748 * @cfg {String} html content of button
4749 * @cfg {String} badge text inside badge
4750 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4751 * @cfg {String} glyphicon DEPRICATED - use fa
4752 * @cfg {String} icon DEPRICATED - use fa
4753 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4754 * @cfg {Boolean} active Is item active
4755 * @cfg {Boolean} disabled Is item disabled
4757 * @cfg {Boolean} preventDefault (true | false) default false
4758 * @cfg {String} tabId the tab that this item activates.
4759 * @cfg {String} tagtype (a|span) render as a href or span?
4760 * @cfg {Boolean} animateRef (true|false) link to element default false
4763 * Create a new Navbar Item
4764 * @param {Object} config The config object
4766 Roo.bootstrap.NavItem = function(config){
4767 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4772 * The raw click event for the entire grid.
4773 * @param {Roo.EventObject} e
4778 * Fires when the active item active state changes
4779 * @param {Roo.bootstrap.NavItem} this
4780 * @param {boolean} state the new state
4786 * Fires when scroll to element
4787 * @param {Roo.bootstrap.NavItem} this
4788 * @param {Object} options
4789 * @param {Roo.EventObject} e
4797 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4806 preventDefault : false,
4814 button_outline : false,
4818 getAutoCreate : function(){
4826 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4828 if (this.disabled) {
4829 cfg.cls += ' disabled';
4833 if (this.button_weight.length) {
4834 cfg.tag = this.href ? 'a' : 'button';
4835 cfg.html = this.html || '';
4836 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4838 cfg.href = this.href;
4841 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4844 // menu .. should add dropdown-menu class - so no need for carat..
4846 if (this.badge !== '') {
4848 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4853 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4857 href : this.href || "#",
4858 html: this.html || ''
4861 if (this.tagtype == 'a') {
4862 cfg.cn[0].cls = 'nav-link';
4865 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4868 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4870 if(this.glyphicon) {
4871 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4876 cfg.cn[0].html += " <span class='caret'></span>";
4880 if (this.badge !== '') {
4882 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4890 onRender : function(ct, position)
4892 // Roo.log("Call onRender: " + this.xtype);
4893 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4897 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4898 this.navLink = this.el.select('.nav-link',true).first();
4903 initEvents: function()
4905 if (typeof (this.menu) != 'undefined') {
4906 this.menu.parentType = this.xtype;
4907 this.menu.triggerEl = this.el;
4908 this.menu = this.addxtype(Roo.apply({}, this.menu));
4911 this.el.select('a',true).on('click', this.onClick, this);
4913 if(this.tagtype == 'span'){
4914 this.el.select('span',true).on('click', this.onClick, this);
4917 // at this point parent should be available..
4918 this.parent().register(this);
4921 onClick : function(e)
4923 if (e.getTarget('.dropdown-menu-item')) {
4924 // did you click on a menu itemm.... - then don't trigger onclick..
4929 this.preventDefault ||
4932 Roo.log("NavItem - prevent Default?");
4936 if (this.disabled) {
4940 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4941 if (tg && tg.transition) {
4942 Roo.log("waiting for the transitionend");
4948 //Roo.log("fire event clicked");
4949 if(this.fireEvent('click', this, e) === false){
4953 if(this.tagtype == 'span'){
4957 //Roo.log(this.href);
4958 var ael = this.el.select('a',true).first();
4961 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4962 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4963 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4964 return; // ignore... - it's a 'hash' to another page.
4966 Roo.log("NavItem - prevent Default?");
4968 this.scrollToElement(e);
4972 var p = this.parent();
4974 if (['tabs','pills'].indexOf(p.type)!==-1) {
4975 if (typeof(p.setActiveItem) !== 'undefined') {
4976 p.setActiveItem(this);
4980 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4981 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4982 // remove the collapsed menu expand...
4983 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
4987 isActive: function () {
4990 setActive : function(state, fire, is_was_active)
4992 if (this.active && !state && this.navId) {
4993 this.was_active = true;
4994 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4996 nv.clearWasActive(this);
5000 this.active = state;
5003 this.el.removeClass('active');
5004 this.navLink ? this.navLink.removeClass('active') : false;
5005 } else if (!this.el.hasClass('active')) {
5007 this.el.addClass('active');
5008 if (Roo.bootstrap.version == 4 && this.navLink ) {
5009 this.navLink.addClass('active');
5014 this.fireEvent('changed', this, state);
5017 // show a panel if it's registered and related..
5019 if (!this.navId || !this.tabId || !state || is_was_active) {
5023 var tg = Roo.bootstrap.TabGroup.get(this.navId);
5027 var pan = tg.getPanelByName(this.tabId);
5031 // if we can not flip to new panel - go back to old nav highlight..
5032 if (false == tg.showPanel(pan)) {
5033 var nv = Roo.bootstrap.NavGroup.get(this.navId);
5035 var onav = nv.getWasActive();
5037 onav.setActive(true, false, true);
5046 // this should not be here...
5047 setDisabled : function(state)
5049 this.disabled = state;
5051 this.el.removeClass('disabled');
5052 } else if (!this.el.hasClass('disabled')) {
5053 this.el.addClass('disabled');
5059 * Fetch the element to display the tooltip on.
5060 * @return {Roo.Element} defaults to this.el
5062 tooltipEl : function()
5064 return this.el.select('' + this.tagtype + '', true).first();
5067 scrollToElement : function(e)
5069 var c = document.body;
5072 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
5074 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
5075 c = document.documentElement;
5078 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
5084 var o = target.calcOffsetsTo(c);
5091 this.fireEvent('scrollto', this, options, e);
5093 Roo.get(c).scrollTo('top', options.value, true);
5106 * <span> icon </span>
5107 * <span> text </span>
5108 * <span>badge </span>
5112 * @class Roo.bootstrap.NavSidebarItem
5113 * @extends Roo.bootstrap.NavItem
5114 * Bootstrap Navbar.NavSidebarItem class
5115 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5116 * {Boolean} open is the menu open
5117 * {Boolean} buttonView use button as the tigger el rather that a (default false)
5118 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5119 * {String} buttonSize (sm|md|lg)the extra classes for the button
5120 * {Boolean} showArrow show arrow next to the text (default true)
5122 * Create a new Navbar Button
5123 * @param {Object} config The config object
5125 Roo.bootstrap.NavSidebarItem = function(config){
5126 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5131 * The raw click event for the entire grid.
5132 * @param {Roo.EventObject} e
5137 * Fires when the active item active state changes
5138 * @param {Roo.bootstrap.NavSidebarItem} this
5139 * @param {boolean} state the new state
5147 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
5149 badgeWeight : 'default',
5155 buttonWeight : 'default',
5161 getAutoCreate : function(){
5166 href : this.href || '#',
5172 if(this.buttonView){
5175 href : this.href || '#',
5176 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5189 cfg.cls += ' active';
5192 if (this.disabled) {
5193 cfg.cls += ' disabled';
5196 cfg.cls += ' open x-open';
5199 if (this.glyphicon || this.icon) {
5200 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
5201 a.cn.push({ tag : 'i', cls : c }) ;
5204 if(!this.buttonView){
5207 html : this.html || ''
5214 if (this.badge !== '') {
5215 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
5221 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5224 a.cls += ' dropdown-toggle treeview' ;
5230 initEvents : function()
5232 if (typeof (this.menu) != 'undefined') {
5233 this.menu.parentType = this.xtype;
5234 this.menu.triggerEl = this.el;
5235 this.menu = this.addxtype(Roo.apply({}, this.menu));
5238 this.el.on('click', this.onClick, this);
5240 if(this.badge !== ''){
5241 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5246 onClick : function(e)
5253 if(this.preventDefault){
5257 this.fireEvent('click', this, e);
5260 disable : function()
5262 this.setDisabled(true);
5267 this.setDisabled(false);
5270 setDisabled : function(state)
5272 if(this.disabled == state){
5276 this.disabled = state;
5279 this.el.addClass('disabled');
5283 this.el.removeClass('disabled');
5288 setActive : function(state)
5290 if(this.active == state){
5294 this.active = state;
5297 this.el.addClass('active');
5301 this.el.removeClass('active');
5306 isActive: function ()
5311 setBadge : function(str)
5317 this.badgeEl.dom.innerHTML = str;
5334 * @class Roo.bootstrap.Row
5335 * @extends Roo.bootstrap.Component
5336 * Bootstrap Row class (contains columns...)
5340 * @param {Object} config The config object
5343 Roo.bootstrap.Row = function(config){
5344 Roo.bootstrap.Row.superclass.constructor.call(this, config);
5347 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
5349 getAutoCreate : function(){
5368 * @class Roo.bootstrap.Element
5369 * @extends Roo.bootstrap.Component
5370 * Bootstrap Element class
5371 * @cfg {String} html contents of the element
5372 * @cfg {String} tag tag of the element
5373 * @cfg {String} cls class of the element
5374 * @cfg {Boolean} preventDefault (true|false) default false
5375 * @cfg {Boolean} clickable (true|false) default false
5378 * Create a new Element
5379 * @param {Object} config The config object
5382 Roo.bootstrap.Element = function(config){
5383 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5389 * When a element is chick
5390 * @param {Roo.bootstrap.Element} this
5391 * @param {Roo.EventObject} e
5397 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5402 preventDefault: false,
5405 getAutoCreate : function(){
5409 // cls: this.cls, double assign in parent class Component.js :: onRender
5416 initEvents: function()
5418 Roo.bootstrap.Element.superclass.initEvents.call(this);
5421 this.el.on('click', this.onClick, this);
5426 onClick : function(e)
5428 if(this.preventDefault){
5432 this.fireEvent('click', this, e);
5435 getValue : function()
5437 return this.el.dom.innerHTML;
5440 setValue : function(value)
5442 this.el.dom.innerHTML = value;
5457 * @class Roo.bootstrap.Pagination
5458 * @extends Roo.bootstrap.Component
5459 * Bootstrap Pagination class
5460 * @cfg {String} size xs | sm | md | lg
5461 * @cfg {Boolean} inverse false | true
5464 * Create a new Pagination
5465 * @param {Object} config The config object
5468 Roo.bootstrap.Pagination = function(config){
5469 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5472 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5478 getAutoCreate : function(){
5484 cfg.cls += ' inverse';
5490 cfg.cls += " " + this.cls;
5508 * @class Roo.bootstrap.PaginationItem
5509 * @extends Roo.bootstrap.Component
5510 * Bootstrap PaginationItem class
5511 * @cfg {String} html text
5512 * @cfg {String} href the link
5513 * @cfg {Boolean} preventDefault (true | false) default true
5514 * @cfg {Boolean} active (true | false) default false
5515 * @cfg {Boolean} disabled default false
5519 * Create a new PaginationItem
5520 * @param {Object} config The config object
5524 Roo.bootstrap.PaginationItem = function(config){
5525 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5530 * The raw click event for the entire grid.
5531 * @param {Roo.EventObject} e
5537 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5541 preventDefault: true,
5546 getAutoCreate : function(){
5552 href : this.href ? this.href : '#',
5553 html : this.html ? this.html : ''
5563 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5567 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5573 initEvents: function() {
5575 this.el.on('click', this.onClick, this);
5578 onClick : function(e)
5580 Roo.log('PaginationItem on click ');
5581 if(this.preventDefault){
5589 this.fireEvent('click', this, e);
5605 * @class Roo.bootstrap.Slider
5606 * @extends Roo.bootstrap.Component
5607 * Bootstrap Slider class
5610 * Create a new Slider
5611 * @param {Object} config The config object
5614 Roo.bootstrap.Slider = function(config){
5615 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5618 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5620 getAutoCreate : function(){
5624 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5628 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5640 * Ext JS Library 1.1.1
5641 * Copyright(c) 2006-2007, Ext JS, LLC.
5643 * Originally Released Under LGPL - original licence link has changed is not relivant.
5646 * <script type="text/javascript">
5651 * @class Roo.grid.ColumnModel
5652 * @extends Roo.util.Observable
5653 * This is the default implementation of a ColumnModel used by the Grid. It defines
5654 * the columns in the grid.
5657 var colModel = new Roo.grid.ColumnModel([
5658 {header: "Ticker", width: 60, sortable: true, locked: true},
5659 {header: "Company Name", width: 150, sortable: true},
5660 {header: "Market Cap.", width: 100, sortable: true},
5661 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5662 {header: "Employees", width: 100, sortable: true, resizable: false}
5667 * The config options listed for this class are options which may appear in each
5668 * individual column definition.
5669 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5671 * @param {Object} config An Array of column config objects. See this class's
5672 * config objects for details.
5674 Roo.grid.ColumnModel = function(config){
5676 * The config passed into the constructor
5678 this.config = config;
5681 // if no id, create one
5682 // if the column does not have a dataIndex mapping,
5683 // map it to the order it is in the config
5684 for(var i = 0, len = config.length; i < len; i++){
5686 if(typeof c.dataIndex == "undefined"){
5689 if(typeof c.renderer == "string"){
5690 c.renderer = Roo.util.Format[c.renderer];
5692 if(typeof c.id == "undefined"){
5695 if(c.editor && c.editor.xtype){
5696 c.editor = Roo.factory(c.editor, Roo.grid);
5698 if(c.editor && c.editor.isFormField){
5699 c.editor = new Roo.grid.GridEditor(c.editor);
5701 this.lookup[c.id] = c;
5705 * The width of columns which have no width specified (defaults to 100)
5708 this.defaultWidth = 100;
5711 * Default sortable of columns which have no sortable specified (defaults to false)
5714 this.defaultSortable = false;
5718 * @event widthchange
5719 * Fires when the width of a column changes.
5720 * @param {ColumnModel} this
5721 * @param {Number} columnIndex The column index
5722 * @param {Number} newWidth The new width
5724 "widthchange": true,
5726 * @event headerchange
5727 * Fires when the text of a header changes.
5728 * @param {ColumnModel} this
5729 * @param {Number} columnIndex The column index
5730 * @param {Number} newText The new header text
5732 "headerchange": true,
5734 * @event hiddenchange
5735 * Fires when a column is hidden or "unhidden".
5736 * @param {ColumnModel} this
5737 * @param {Number} columnIndex The column index
5738 * @param {Boolean} hidden true if hidden, false otherwise
5740 "hiddenchange": true,
5742 * @event columnmoved
5743 * Fires when a column is moved.
5744 * @param {ColumnModel} this
5745 * @param {Number} oldIndex
5746 * @param {Number} newIndex
5748 "columnmoved" : true,
5750 * @event columlockchange
5751 * Fires when a column's locked state is changed
5752 * @param {ColumnModel} this
5753 * @param {Number} colIndex
5754 * @param {Boolean} locked true if locked
5756 "columnlockchange" : true
5758 Roo.grid.ColumnModel.superclass.constructor.call(this);
5760 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5762 * @cfg {String} header The header text to display in the Grid view.
5765 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5766 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5767 * specified, the column's index is used as an index into the Record's data Array.
5770 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5771 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5774 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5775 * Defaults to the value of the {@link #defaultSortable} property.
5776 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5779 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5782 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5785 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5788 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5791 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5792 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5793 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5794 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5797 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5800 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5803 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5806 * @cfg {String} cursor (Optional)
5809 * @cfg {String} tooltip (Optional)
5812 * @cfg {Number} xs (Optional)
5815 * @cfg {Number} sm (Optional)
5818 * @cfg {Number} md (Optional)
5821 * @cfg {Number} lg (Optional)
5824 * Returns the id of the column at the specified index.
5825 * @param {Number} index The column index
5826 * @return {String} the id
5828 getColumnId : function(index){
5829 return this.config[index].id;
5833 * Returns the column for a specified id.
5834 * @param {String} id The column id
5835 * @return {Object} the column
5837 getColumnById : function(id){
5838 return this.lookup[id];
5843 * Returns the column for a specified dataIndex.
5844 * @param {String} dataIndex The column dataIndex
5845 * @return {Object|Boolean} the column or false if not found
5847 getColumnByDataIndex: function(dataIndex){
5848 var index = this.findColumnIndex(dataIndex);
5849 return index > -1 ? this.config[index] : false;
5853 * Returns the index for a specified column id.
5854 * @param {String} id The column id
5855 * @return {Number} the index, or -1 if not found
5857 getIndexById : function(id){
5858 for(var i = 0, len = this.config.length; i < len; i++){
5859 if(this.config[i].id == id){
5867 * Returns the index for a specified column dataIndex.
5868 * @param {String} dataIndex The column dataIndex
5869 * @return {Number} the index, or -1 if not found
5872 findColumnIndex : function(dataIndex){
5873 for(var i = 0, len = this.config.length; i < len; i++){
5874 if(this.config[i].dataIndex == dataIndex){
5882 moveColumn : function(oldIndex, newIndex){
5883 var c = this.config[oldIndex];
5884 this.config.splice(oldIndex, 1);
5885 this.config.splice(newIndex, 0, c);
5886 this.dataMap = null;
5887 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5890 isLocked : function(colIndex){
5891 return this.config[colIndex].locked === true;
5894 setLocked : function(colIndex, value, suppressEvent){
5895 if(this.isLocked(colIndex) == value){
5898 this.config[colIndex].locked = value;
5900 this.fireEvent("columnlockchange", this, colIndex, value);
5904 getTotalLockedWidth : function(){
5906 for(var i = 0; i < this.config.length; i++){
5907 if(this.isLocked(i) && !this.isHidden(i)){
5908 this.totalWidth += this.getColumnWidth(i);
5914 getLockedCount : function(){
5915 for(var i = 0, len = this.config.length; i < len; i++){
5916 if(!this.isLocked(i)){
5921 return this.config.length;
5925 * Returns the number of columns.
5928 getColumnCount : function(visibleOnly){
5929 if(visibleOnly === true){
5931 for(var i = 0, len = this.config.length; i < len; i++){
5932 if(!this.isHidden(i)){
5938 return this.config.length;
5942 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5943 * @param {Function} fn
5944 * @param {Object} scope (optional)
5945 * @return {Array} result
5947 getColumnsBy : function(fn, scope){
5949 for(var i = 0, len = this.config.length; i < len; i++){
5950 var c = this.config[i];
5951 if(fn.call(scope||this, c, i) === true){
5959 * Returns true if the specified column is sortable.
5960 * @param {Number} col The column index
5963 isSortable : function(col){
5964 if(typeof this.config[col].sortable == "undefined"){
5965 return this.defaultSortable;
5967 return this.config[col].sortable;
5971 * Returns the rendering (formatting) function defined for the column.
5972 * @param {Number} col The column index.
5973 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5975 getRenderer : function(col){
5976 if(!this.config[col].renderer){
5977 return Roo.grid.ColumnModel.defaultRenderer;
5979 return this.config[col].renderer;
5983 * Sets the rendering (formatting) function for a column.
5984 * @param {Number} col The column index
5985 * @param {Function} fn The function to use to process the cell's raw data
5986 * to return HTML markup for the grid view. The render function is called with
5987 * the following parameters:<ul>
5988 * <li>Data value.</li>
5989 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5990 * <li>css A CSS style string to apply to the table cell.</li>
5991 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5992 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5993 * <li>Row index</li>
5994 * <li>Column index</li>
5995 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5997 setRenderer : function(col, fn){
5998 this.config[col].renderer = fn;
6002 * Returns the width for the specified column.
6003 * @param {Number} col The column index
6006 getColumnWidth : function(col){
6007 return this.config[col].width * 1 || this.defaultWidth;
6011 * Sets the width for a column.
6012 * @param {Number} col The column index
6013 * @param {Number} width The new width
6015 setColumnWidth : function(col, width, suppressEvent){
6016 this.config[col].width = width;
6017 this.totalWidth = null;
6019 this.fireEvent("widthchange", this, col, width);
6024 * Returns the total width of all columns.
6025 * @param {Boolean} includeHidden True to include hidden column widths
6028 getTotalWidth : function(includeHidden){
6029 if(!this.totalWidth){
6030 this.totalWidth = 0;
6031 for(var i = 0, len = this.config.length; i < len; i++){
6032 if(includeHidden || !this.isHidden(i)){
6033 this.totalWidth += this.getColumnWidth(i);
6037 return this.totalWidth;
6041 * Returns the header for the specified column.
6042 * @param {Number} col The column index
6045 getColumnHeader : function(col){
6046 return this.config[col].header;
6050 * Sets the header for a column.
6051 * @param {Number} col The column index
6052 * @param {String} header The new header
6054 setColumnHeader : function(col, header){
6055 this.config[col].header = header;
6056 this.fireEvent("headerchange", this, col, header);
6060 * Returns the tooltip for the specified column.
6061 * @param {Number} col The column index
6064 getColumnTooltip : function(col){
6065 return this.config[col].tooltip;
6068 * Sets the tooltip for a column.
6069 * @param {Number} col The column index
6070 * @param {String} tooltip The new tooltip
6072 setColumnTooltip : function(col, tooltip){
6073 this.config[col].tooltip = tooltip;
6077 * Returns the dataIndex for the specified column.
6078 * @param {Number} col The column index
6081 getDataIndex : function(col){
6082 return this.config[col].dataIndex;
6086 * Sets the dataIndex for a column.
6087 * @param {Number} col The column index
6088 * @param {Number} dataIndex The new dataIndex
6090 setDataIndex : function(col, dataIndex){
6091 this.config[col].dataIndex = dataIndex;
6097 * Returns true if the cell is editable.
6098 * @param {Number} colIndex The column index
6099 * @param {Number} rowIndex The row index - this is nto actually used..?
6102 isCellEditable : function(colIndex, rowIndex){
6103 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6107 * Returns the editor defined for the cell/column.
6108 * return false or null to disable editing.
6109 * @param {Number} colIndex The column index
6110 * @param {Number} rowIndex The row index
6113 getCellEditor : function(colIndex, rowIndex){
6114 return this.config[colIndex].editor;
6118 * Sets if a column is editable.
6119 * @param {Number} col The column index
6120 * @param {Boolean} editable True if the column is editable
6122 setEditable : function(col, editable){
6123 this.config[col].editable = editable;
6128 * Returns true if the column is hidden.
6129 * @param {Number} colIndex The column index
6132 isHidden : function(colIndex){
6133 return this.config[colIndex].hidden;
6138 * Returns true if the column width cannot be changed
6140 isFixed : function(colIndex){
6141 return this.config[colIndex].fixed;
6145 * Returns true if the column can be resized
6148 isResizable : function(colIndex){
6149 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6152 * Sets if a column is hidden.
6153 * @param {Number} colIndex The column index
6154 * @param {Boolean} hidden True if the column is hidden
6156 setHidden : function(colIndex, hidden){
6157 this.config[colIndex].hidden = hidden;
6158 this.totalWidth = null;
6159 this.fireEvent("hiddenchange", this, colIndex, hidden);
6163 * Sets the editor for a column.
6164 * @param {Number} col The column index
6165 * @param {Object} editor The editor object
6167 setEditor : function(col, editor){
6168 this.config[col].editor = editor;
6172 Roo.grid.ColumnModel.defaultRenderer = function(value)
6174 if(typeof value == "object") {
6177 if(typeof value == "string" && value.length < 1){
6181 return String.format("{0}", value);
6184 // Alias for backwards compatibility
6185 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6188 * Ext JS Library 1.1.1
6189 * Copyright(c) 2006-2007, Ext JS, LLC.
6191 * Originally Released Under LGPL - original licence link has changed is not relivant.
6194 * <script type="text/javascript">
6198 * @class Roo.LoadMask
6199 * A simple utility class for generically masking elements while loading data. If the element being masked has
6200 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6201 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
6202 * element's UpdateManager load indicator and will be destroyed after the initial load.
6204 * Create a new LoadMask
6205 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6206 * @param {Object} config The config object
6208 Roo.LoadMask = function(el, config){
6209 this.el = Roo.get(el);
6210 Roo.apply(this, config);
6212 this.store.on('beforeload', this.onBeforeLoad, this);
6213 this.store.on('load', this.onLoad, this);
6214 this.store.on('loadexception', this.onLoadException, this);
6215 this.removeMask = false;
6217 var um = this.el.getUpdateManager();
6218 um.showLoadIndicator = false; // disable the default indicator
6219 um.on('beforeupdate', this.onBeforeLoad, this);
6220 um.on('update', this.onLoad, this);
6221 um.on('failure', this.onLoad, this);
6222 this.removeMask = true;
6226 Roo.LoadMask.prototype = {
6228 * @cfg {Boolean} removeMask
6229 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6230 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
6234 * The text to display in a centered loading message box (defaults to 'Loading...')
6238 * @cfg {String} msgCls
6239 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6241 msgCls : 'x-mask-loading',
6244 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6250 * Disables the mask to prevent it from being displayed
6252 disable : function(){
6253 this.disabled = true;
6257 * Enables the mask so that it can be displayed
6259 enable : function(){
6260 this.disabled = false;
6263 onLoadException : function()
6267 if (typeof(arguments[3]) != 'undefined') {
6268 Roo.MessageBox.alert("Error loading",arguments[3]);
6272 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6273 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6280 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6285 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6289 onBeforeLoad : function(){
6291 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6296 destroy : function(){
6298 this.store.un('beforeload', this.onBeforeLoad, this);
6299 this.store.un('load', this.onLoad, this);
6300 this.store.un('loadexception', this.onLoadException, this);
6302 var um = this.el.getUpdateManager();
6303 um.un('beforeupdate', this.onBeforeLoad, this);
6304 um.un('update', this.onLoad, this);
6305 um.un('failure', this.onLoad, this);
6316 * @class Roo.bootstrap.Table
6317 * @extends Roo.bootstrap.Component
6318 * Bootstrap Table class
6319 * @cfg {String} cls table class
6320 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6321 * @cfg {String} bgcolor Specifies the background color for a table
6322 * @cfg {Number} border Specifies whether the table cells should have borders or not
6323 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6324 * @cfg {Number} cellspacing Specifies the space between cells
6325 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6326 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6327 * @cfg {String} sortable Specifies that the table should be sortable
6328 * @cfg {String} summary Specifies a summary of the content of a table
6329 * @cfg {Number} width Specifies the width of a table
6330 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6332 * @cfg {boolean} striped Should the rows be alternative striped
6333 * @cfg {boolean} bordered Add borders to the table
6334 * @cfg {boolean} hover Add hover highlighting
6335 * @cfg {boolean} condensed Format condensed
6336 * @cfg {boolean} responsive Format condensed
6337 * @cfg {Boolean} loadMask (true|false) default false
6338 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6339 * @cfg {Boolean} headerShow (true|false) generate thead, default true
6340 * @cfg {Boolean} rowSelection (true|false) default false
6341 * @cfg {Boolean} cellSelection (true|false) default false
6342 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6343 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
6344 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
6345 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
6349 * Create a new Table
6350 * @param {Object} config The config object
6353 Roo.bootstrap.Table = function(config){
6354 Roo.bootstrap.Table.superclass.constructor.call(this, config);
6359 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6360 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6361 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6362 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6364 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6366 this.sm.grid = this;
6367 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6368 this.sm = this.selModel;
6369 this.sm.xmodule = this.xmodule || false;
6372 if (this.cm && typeof(this.cm.config) == 'undefined') {
6373 this.colModel = new Roo.grid.ColumnModel(this.cm);
6374 this.cm = this.colModel;
6375 this.cm.xmodule = this.xmodule || false;
6378 this.store= Roo.factory(this.store, Roo.data);
6379 this.ds = this.store;
6380 this.ds.xmodule = this.xmodule || false;
6383 if (this.footer && this.store) {
6384 this.footer.dataSource = this.ds;
6385 this.footer = Roo.factory(this.footer);
6392 * Fires when a cell is clicked
6393 * @param {Roo.bootstrap.Table} this
6394 * @param {Roo.Element} el
6395 * @param {Number} rowIndex
6396 * @param {Number} columnIndex
6397 * @param {Roo.EventObject} e
6401 * @event celldblclick
6402 * Fires when a cell is double clicked
6403 * @param {Roo.bootstrap.Table} this
6404 * @param {Roo.Element} el
6405 * @param {Number} rowIndex
6406 * @param {Number} columnIndex
6407 * @param {Roo.EventObject} e
6409 "celldblclick" : true,
6412 * Fires when a row is clicked
6413 * @param {Roo.bootstrap.Table} this
6414 * @param {Roo.Element} el
6415 * @param {Number} rowIndex
6416 * @param {Roo.EventObject} e
6420 * @event rowdblclick
6421 * Fires when a row is double clicked
6422 * @param {Roo.bootstrap.Table} this
6423 * @param {Roo.Element} el
6424 * @param {Number} rowIndex
6425 * @param {Roo.EventObject} e
6427 "rowdblclick" : true,
6430 * Fires when a mouseover occur
6431 * @param {Roo.bootstrap.Table} this
6432 * @param {Roo.Element} el
6433 * @param {Number} rowIndex
6434 * @param {Number} columnIndex
6435 * @param {Roo.EventObject} e
6440 * Fires when a mouseout occur
6441 * @param {Roo.bootstrap.Table} this
6442 * @param {Roo.Element} el
6443 * @param {Number} rowIndex
6444 * @param {Number} columnIndex
6445 * @param {Roo.EventObject} e
6450 * Fires when a row is rendered, so you can change add a style to it.
6451 * @param {Roo.bootstrap.Table} this
6452 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6456 * @event rowsrendered
6457 * Fires when all the rows have been rendered
6458 * @param {Roo.bootstrap.Table} this
6460 'rowsrendered' : true,
6462 * @event contextmenu
6463 * The raw contextmenu event for the entire grid.
6464 * @param {Roo.EventObject} e
6466 "contextmenu" : true,
6468 * @event rowcontextmenu
6469 * Fires when a row is right clicked
6470 * @param {Roo.bootstrap.Table} this
6471 * @param {Number} rowIndex
6472 * @param {Roo.EventObject} e
6474 "rowcontextmenu" : true,
6476 * @event cellcontextmenu
6477 * Fires when a cell is right clicked
6478 * @param {Roo.bootstrap.Table} this
6479 * @param {Number} rowIndex
6480 * @param {Number} cellIndex
6481 * @param {Roo.EventObject} e
6483 "cellcontextmenu" : true,
6485 * @event headercontextmenu
6486 * Fires when a header is right clicked
6487 * @param {Roo.bootstrap.Table} this
6488 * @param {Number} columnIndex
6489 * @param {Roo.EventObject} e
6491 "headercontextmenu" : true
6495 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6521 rowSelection : false,
6522 cellSelection : false,
6525 // Roo.Element - the tbody
6527 // Roo.Element - thead element
6530 container: false, // used by gridpanel...
6536 auto_hide_footer : false,
6538 getAutoCreate : function()
6540 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6547 if (this.scrollBody) {
6548 cfg.cls += ' table-body-fixed';
6551 cfg.cls += ' table-striped';
6555 cfg.cls += ' table-hover';
6557 if (this.bordered) {
6558 cfg.cls += ' table-bordered';
6560 if (this.condensed) {
6561 cfg.cls += ' table-condensed';
6563 if (this.responsive) {
6564 cfg.cls += ' table-responsive';
6568 cfg.cls+= ' ' +this.cls;
6571 // this lot should be simplifed...
6584 ].forEach(function(k) {
6592 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6595 if(this.store || this.cm){
6596 if(this.headerShow){
6597 cfg.cn.push(this.renderHeader());
6600 cfg.cn.push(this.renderBody());
6602 if(this.footerShow){
6603 cfg.cn.push(this.renderFooter());
6605 // where does this come from?
6606 //cfg.cls+= ' TableGrid';
6609 return { cn : [ cfg ] };
6612 initEvents : function()
6614 if(!this.store || !this.cm){
6617 if (this.selModel) {
6618 this.selModel.initEvents();
6622 //Roo.log('initEvents with ds!!!!');
6624 this.mainBody = this.el.select('tbody', true).first();
6625 this.mainHead = this.el.select('thead', true).first();
6626 this.mainFoot = this.el.select('tfoot', true).first();
6632 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6633 e.on('click', _this.sort, _this);
6636 this.mainBody.on("click", this.onClick, this);
6637 this.mainBody.on("dblclick", this.onDblClick, this);
6639 // why is this done????? = it breaks dialogs??
6640 //this.parent().el.setStyle('position', 'relative');
6644 this.footer.parentId = this.id;
6645 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6648 this.el.select('tfoot tr td').first().addClass('hide');
6653 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6656 this.store.on('load', this.onLoad, this);
6657 this.store.on('beforeload', this.onBeforeLoad, this);
6658 this.store.on('update', this.onUpdate, this);
6659 this.store.on('add', this.onAdd, this);
6660 this.store.on("clear", this.clear, this);
6662 this.el.on("contextmenu", this.onContextMenu, this);
6664 this.mainBody.on('scroll', this.onBodyScroll, this);
6666 this.cm.on("headerchange", this.onHeaderChange, this);
6668 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6672 onContextMenu : function(e, t)
6674 this.processEvent("contextmenu", e);
6677 processEvent : function(name, e)
6679 if (name != 'touchstart' ) {
6680 this.fireEvent(name, e);
6683 var t = e.getTarget();
6685 var cell = Roo.get(t);
6691 if(cell.findParent('tfoot', false, true)){
6695 if(cell.findParent('thead', false, true)){
6697 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6698 cell = Roo.get(t).findParent('th', false, true);
6700 Roo.log("failed to find th in thead?");
6701 Roo.log(e.getTarget());
6706 var cellIndex = cell.dom.cellIndex;
6708 var ename = name == 'touchstart' ? 'click' : name;
6709 this.fireEvent("header" + ename, this, cellIndex, e);
6714 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6715 cell = Roo.get(t).findParent('td', false, true);
6717 Roo.log("failed to find th in tbody?");
6718 Roo.log(e.getTarget());
6723 var row = cell.findParent('tr', false, true);
6724 var cellIndex = cell.dom.cellIndex;
6725 var rowIndex = row.dom.rowIndex - 1;
6729 this.fireEvent("row" + name, this, rowIndex, e);
6733 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6739 onMouseover : function(e, el)
6741 var cell = Roo.get(el);
6747 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6748 cell = cell.findParent('td', false, true);
6751 var row = cell.findParent('tr', false, true);
6752 var cellIndex = cell.dom.cellIndex;
6753 var rowIndex = row.dom.rowIndex - 1; // start from 0
6755 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6759 onMouseout : function(e, el)
6761 var cell = Roo.get(el);
6767 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6768 cell = cell.findParent('td', false, true);
6771 var row = cell.findParent('tr', false, true);
6772 var cellIndex = cell.dom.cellIndex;
6773 var rowIndex = row.dom.rowIndex - 1; // start from 0
6775 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6779 onClick : function(e, el)
6781 var cell = Roo.get(el);
6783 if(!cell || (!this.cellSelection && !this.rowSelection)){
6787 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6788 cell = cell.findParent('td', false, true);
6791 if(!cell || typeof(cell) == 'undefined'){
6795 var row = cell.findParent('tr', false, true);
6797 if(!row || typeof(row) == 'undefined'){
6801 var cellIndex = cell.dom.cellIndex;
6802 var rowIndex = this.getRowIndex(row);
6804 // why??? - should these not be based on SelectionModel?
6805 if(this.cellSelection){
6806 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6809 if(this.rowSelection){
6810 this.fireEvent('rowclick', this, row, rowIndex, e);
6816 onDblClick : function(e,el)
6818 var cell = Roo.get(el);
6820 if(!cell || (!this.cellSelection && !this.rowSelection)){
6824 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6825 cell = cell.findParent('td', false, true);
6828 if(!cell || typeof(cell) == 'undefined'){
6832 var row = cell.findParent('tr', false, true);
6834 if(!row || typeof(row) == 'undefined'){
6838 var cellIndex = cell.dom.cellIndex;
6839 var rowIndex = this.getRowIndex(row);
6841 if(this.cellSelection){
6842 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6845 if(this.rowSelection){
6846 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6850 sort : function(e,el)
6852 var col = Roo.get(el);
6854 if(!col.hasClass('sortable')){
6858 var sort = col.attr('sort');
6861 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6865 this.store.sortInfo = {field : sort, direction : dir};
6868 Roo.log("calling footer first");
6869 this.footer.onClick('first');
6872 this.store.load({ params : { start : 0 } });
6876 renderHeader : function()
6884 this.totalWidth = 0;
6886 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6888 var config = cm.config[i];
6892 cls : 'x-hcol-' + i,
6894 html: cm.getColumnHeader(i)
6899 if(typeof(config.sortable) != 'undefined' && config.sortable){
6901 c.html = '<i class="glyphicon"></i>' + c.html;
6904 // could use BS4 hidden-..-down
6906 if(typeof(config.lgHeader) != 'undefined'){
6907 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6910 if(typeof(config.mdHeader) != 'undefined'){
6911 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6914 if(typeof(config.smHeader) != 'undefined'){
6915 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6918 if(typeof(config.xsHeader) != 'undefined'){
6919 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6926 if(typeof(config.tooltip) != 'undefined'){
6927 c.tooltip = config.tooltip;
6930 if(typeof(config.colspan) != 'undefined'){
6931 c.colspan = config.colspan;
6934 if(typeof(config.hidden) != 'undefined' && config.hidden){
6935 c.style += ' display:none;';
6938 if(typeof(config.dataIndex) != 'undefined'){
6939 c.sort = config.dataIndex;
6944 if(typeof(config.align) != 'undefined' && config.align.length){
6945 c.style += ' text-align:' + config.align + ';';
6948 if(typeof(config.width) != 'undefined'){
6949 c.style += ' width:' + config.width + 'px;';
6950 this.totalWidth += config.width;
6952 this.totalWidth += 100; // assume minimum of 100 per column?
6955 if(typeof(config.cls) != 'undefined'){
6956 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6959 ['xs','sm','md','lg'].map(function(size){
6961 if(typeof(config[size]) == 'undefined'){
6965 if (!config[size]) { // 0 = hidden
6966 // BS 4 '0' is treated as hide that column and below.
6967 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6971 c.cls += ' col-' + size + '-' + config[size] + (
6972 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6984 renderBody : function()
6994 colspan : this.cm.getColumnCount()
7004 renderFooter : function()
7014 colspan : this.cm.getColumnCount()
7028 // Roo.log('ds onload');
7033 var ds = this.store;
7035 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7036 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
7037 if (_this.store.sortInfo) {
7039 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
7040 e.select('i', true).addClass(['glyphicon-arrow-up']);
7043 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
7044 e.select('i', true).addClass(['glyphicon-arrow-down']);
7049 var tbody = this.mainBody;
7051 if(ds.getCount() > 0){
7052 ds.data.each(function(d,rowIndex){
7053 var row = this.renderRow(cm, ds, rowIndex);
7055 tbody.createChild(row);
7059 if(row.cellObjects.length){
7060 Roo.each(row.cellObjects, function(r){
7061 _this.renderCellObject(r);
7068 var tfoot = this.el.select('tfoot', true).first();
7070 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
7072 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
7074 var total = this.ds.getTotalCount();
7076 if(this.footer.pageSize < total){
7077 this.mainFoot.show();
7081 Roo.each(this.el.select('tbody td', true).elements, function(e){
7082 e.on('mouseover', _this.onMouseover, _this);
7085 Roo.each(this.el.select('tbody td', true).elements, function(e){
7086 e.on('mouseout', _this.onMouseout, _this);
7088 this.fireEvent('rowsrendered', this);
7094 onUpdate : function(ds,record)
7096 this.refreshRow(record);
7100 onRemove : function(ds, record, index, isUpdate){
7101 if(isUpdate !== true){
7102 this.fireEvent("beforerowremoved", this, index, record);
7104 var bt = this.mainBody.dom;
7106 var rows = this.el.select('tbody > tr', true).elements;
7108 if(typeof(rows[index]) != 'undefined'){
7109 bt.removeChild(rows[index].dom);
7112 // if(bt.rows[index]){
7113 // bt.removeChild(bt.rows[index]);
7116 if(isUpdate !== true){
7117 //this.stripeRows(index);
7118 //this.syncRowHeights(index, index);
7120 this.fireEvent("rowremoved", this, index, record);
7124 onAdd : function(ds, records, rowIndex)
7126 //Roo.log('on Add called');
7127 // - note this does not handle multiple adding very well..
7128 var bt = this.mainBody.dom;
7129 for (var i =0 ; i < records.length;i++) {
7130 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7131 //Roo.log(records[i]);
7132 //Roo.log(this.store.getAt(rowIndex+i));
7133 this.insertRow(this.store, rowIndex + i, false);
7140 refreshRow : function(record){
7141 var ds = this.store, index;
7142 if(typeof record == 'number'){
7144 record = ds.getAt(index);
7146 index = ds.indexOf(record);
7148 this.insertRow(ds, index, true);
7150 this.onRemove(ds, record, index+1, true);
7152 //this.syncRowHeights(index, index);
7154 this.fireEvent("rowupdated", this, index, record);
7157 insertRow : function(dm, rowIndex, isUpdate){
7160 this.fireEvent("beforerowsinserted", this, rowIndex);
7162 //var s = this.getScrollState();
7163 var row = this.renderRow(this.cm, this.store, rowIndex);
7164 // insert before rowIndex..
7165 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7169 if(row.cellObjects.length){
7170 Roo.each(row.cellObjects, function(r){
7171 _this.renderCellObject(r);
7176 this.fireEvent("rowsinserted", this, rowIndex);
7177 //this.syncRowHeights(firstRow, lastRow);
7178 //this.stripeRows(firstRow);
7185 getRowDom : function(rowIndex)
7187 var rows = this.el.select('tbody > tr', true).elements;
7189 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7192 // returns the object tree for a tr..
7195 renderRow : function(cm, ds, rowIndex)
7197 var d = ds.getAt(rowIndex);
7201 cls : 'x-row-' + rowIndex,
7205 var cellObjects = [];
7207 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7208 var config = cm.config[i];
7210 var renderer = cm.getRenderer(i);
7214 if(typeof(renderer) !== 'undefined'){
7215 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7217 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7218 // and are rendered into the cells after the row is rendered - using the id for the element.
7220 if(typeof(value) === 'object'){
7230 rowIndex : rowIndex,
7235 this.fireEvent('rowclass', this, rowcfg);
7239 cls : rowcfg.rowClass + ' x-col-' + i,
7241 html: (typeof(value) === 'object') ? '' : value
7248 if(typeof(config.colspan) != 'undefined'){
7249 td.colspan = config.colspan;
7252 if(typeof(config.hidden) != 'undefined' && config.hidden){
7253 td.style += ' display:none;';
7256 if(typeof(config.align) != 'undefined' && config.align.length){
7257 td.style += ' text-align:' + config.align + ';';
7259 if(typeof(config.valign) != 'undefined' && config.valign.length){
7260 td.style += ' vertical-align:' + config.valign + ';';
7263 if(typeof(config.width) != 'undefined'){
7264 td.style += ' width:' + config.width + 'px;';
7267 if(typeof(config.cursor) != 'undefined'){
7268 td.style += ' cursor:' + config.cursor + ';';
7271 if(typeof(config.cls) != 'undefined'){
7272 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7275 ['xs','sm','md','lg'].map(function(size){
7277 if(typeof(config[size]) == 'undefined'){
7283 if (!config[size]) { // 0 = hidden
7284 // BS 4 '0' is treated as hide that column and below.
7285 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7289 td.cls += ' col-' + size + '-' + config[size] + (
7290 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
7300 row.cellObjects = cellObjects;
7308 onBeforeLoad : function()
7317 this.el.select('tbody', true).first().dom.innerHTML = '';
7320 * Show or hide a row.
7321 * @param {Number} rowIndex to show or hide
7322 * @param {Boolean} state hide
7324 setRowVisibility : function(rowIndex, state)
7326 var bt = this.mainBody.dom;
7328 var rows = this.el.select('tbody > tr', true).elements;
7330 if(typeof(rows[rowIndex]) == 'undefined'){
7333 rows[rowIndex].dom.style.display = state ? '' : 'none';
7337 getSelectionModel : function(){
7339 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7341 return this.selModel;
7344 * Render the Roo.bootstrap object from renderder
7346 renderCellObject : function(r)
7350 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7352 var t = r.cfg.render(r.container);
7355 Roo.each(r.cfg.cn, function(c){
7357 container: t.getChildContainer(),
7360 _this.renderCellObject(child);
7365 getRowIndex : function(row)
7369 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7380 * Returns the grid's underlying element = used by panel.Grid
7381 * @return {Element} The element
7383 getGridEl : function(){
7387 * Forces a resize - used by panel.Grid
7388 * @return {Element} The element
7390 autoSize : function()
7392 //var ctr = Roo.get(this.container.dom.parentElement);
7393 var ctr = Roo.get(this.el.dom);
7395 var thd = this.getGridEl().select('thead',true).first();
7396 var tbd = this.getGridEl().select('tbody', true).first();
7397 var tfd = this.getGridEl().select('tfoot', true).first();
7399 var cw = ctr.getWidth();
7403 tbd.setSize(ctr.getWidth(),
7404 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7406 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7409 cw = Math.max(cw, this.totalWidth);
7410 this.getGridEl().select('tr',true).setWidth(cw);
7411 // resize 'expandable coloumn?
7413 return; // we doe not have a view in this design..
7416 onBodyScroll: function()
7418 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7420 this.mainHead.setStyle({
7421 'position' : 'relative',
7422 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7428 var scrollHeight = this.mainBody.dom.scrollHeight;
7430 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7432 var height = this.mainBody.getHeight();
7434 if(scrollHeight - height == scrollTop) {
7436 var total = this.ds.getTotalCount();
7438 if(this.footer.cursor + this.footer.pageSize < total){
7440 this.footer.ds.load({
7442 start : this.footer.cursor + this.footer.pageSize,
7443 limit : this.footer.pageSize
7453 onHeaderChange : function()
7455 var header = this.renderHeader();
7456 var table = this.el.select('table', true).first();
7458 this.mainHead.remove();
7459 this.mainHead = table.createChild(header, this.mainBody, false);
7462 onHiddenChange : function(colModel, colIndex, hidden)
7464 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7465 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7467 this.CSS.updateRule(thSelector, "display", "");
7468 this.CSS.updateRule(tdSelector, "display", "");
7471 this.CSS.updateRule(thSelector, "display", "none");
7472 this.CSS.updateRule(tdSelector, "display", "none");
7475 this.onHeaderChange();
7479 setColumnWidth: function(col_index, width)
7481 // width = "md-2 xs-2..."
7482 if(!this.colModel.config[col_index]) {
7486 var w = width.split(" ");
7488 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7490 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7493 for(var j = 0; j < w.length; j++) {
7499 var size_cls = w[j].split("-");
7501 if(!Number.isInteger(size_cls[1] * 1)) {
7505 if(!this.colModel.config[col_index][size_cls[0]]) {
7509 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7513 h_row[0].classList.replace(
7514 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7515 "col-"+size_cls[0]+"-"+size_cls[1]
7518 for(var i = 0; i < rows.length; i++) {
7520 var size_cls = w[j].split("-");
7522 if(!Number.isInteger(size_cls[1] * 1)) {
7526 if(!this.colModel.config[col_index][size_cls[0]]) {
7530 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7534 rows[i].classList.replace(
7535 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7536 "col-"+size_cls[0]+"-"+size_cls[1]
7540 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7555 * @class Roo.bootstrap.TableCell
7556 * @extends Roo.bootstrap.Component
7557 * Bootstrap TableCell class
7558 * @cfg {String} html cell contain text
7559 * @cfg {String} cls cell class
7560 * @cfg {String} tag cell tag (td|th) default td
7561 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7562 * @cfg {String} align Aligns the content in a cell
7563 * @cfg {String} axis Categorizes cells
7564 * @cfg {String} bgcolor Specifies the background color of a cell
7565 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7566 * @cfg {Number} colspan Specifies the number of columns a cell should span
7567 * @cfg {String} headers Specifies one or more header cells a cell is related to
7568 * @cfg {Number} height Sets the height of a cell
7569 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7570 * @cfg {Number} rowspan Sets the number of rows a cell should span
7571 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7572 * @cfg {String} valign Vertical aligns the content in a cell
7573 * @cfg {Number} width Specifies the width of a cell
7576 * Create a new TableCell
7577 * @param {Object} config The config object
7580 Roo.bootstrap.TableCell = function(config){
7581 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7584 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7604 getAutoCreate : function(){
7605 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7625 cfg.align=this.align
7631 cfg.bgcolor=this.bgcolor
7634 cfg.charoff=this.charoff
7637 cfg.colspan=this.colspan
7640 cfg.headers=this.headers
7643 cfg.height=this.height
7646 cfg.nowrap=this.nowrap
7649 cfg.rowspan=this.rowspan
7652 cfg.scope=this.scope
7655 cfg.valign=this.valign
7658 cfg.width=this.width
7677 * @class Roo.bootstrap.TableRow
7678 * @extends Roo.bootstrap.Component
7679 * Bootstrap TableRow class
7680 * @cfg {String} cls row class
7681 * @cfg {String} align Aligns the content in a table row
7682 * @cfg {String} bgcolor Specifies a background color for a table row
7683 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7684 * @cfg {String} valign Vertical aligns the content in a table row
7687 * Create a new TableRow
7688 * @param {Object} config The config object
7691 Roo.bootstrap.TableRow = function(config){
7692 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7695 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7703 getAutoCreate : function(){
7704 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7714 cfg.align = this.align;
7717 cfg.bgcolor = this.bgcolor;
7720 cfg.charoff = this.charoff;
7723 cfg.valign = this.valign;
7741 * @class Roo.bootstrap.TableBody
7742 * @extends Roo.bootstrap.Component
7743 * Bootstrap TableBody class
7744 * @cfg {String} cls element class
7745 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7746 * @cfg {String} align Aligns the content inside the element
7747 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7748 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7751 * Create a new TableBody
7752 * @param {Object} config The config object
7755 Roo.bootstrap.TableBody = function(config){
7756 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7759 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7767 getAutoCreate : function(){
7768 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7782 cfg.align = this.align;
7785 cfg.charoff = this.charoff;
7788 cfg.valign = this.valign;
7795 // initEvents : function()
7802 // this.store = Roo.factory(this.store, Roo.data);
7803 // this.store.on('load', this.onLoad, this);
7805 // this.store.load();
7809 // onLoad: function ()
7811 // this.fireEvent('load', this);
7821 * Ext JS Library 1.1.1
7822 * Copyright(c) 2006-2007, Ext JS, LLC.
7824 * Originally Released Under LGPL - original licence link has changed is not relivant.
7827 * <script type="text/javascript">
7830 // as we use this in bootstrap.
7831 Roo.namespace('Roo.form');
7833 * @class Roo.form.Action
7834 * Internal Class used to handle form actions
7836 * @param {Roo.form.BasicForm} el The form element or its id
7837 * @param {Object} config Configuration options
7842 // define the action interface
7843 Roo.form.Action = function(form, options){
7845 this.options = options || {};
7848 * Client Validation Failed
7851 Roo.form.Action.CLIENT_INVALID = 'client';
7853 * Server Validation Failed
7856 Roo.form.Action.SERVER_INVALID = 'server';
7858 * Connect to Server Failed
7861 Roo.form.Action.CONNECT_FAILURE = 'connect';
7863 * Reading Data from Server Failed
7866 Roo.form.Action.LOAD_FAILURE = 'load';
7868 Roo.form.Action.prototype = {
7870 failureType : undefined,
7871 response : undefined,
7875 run : function(options){
7880 success : function(response){
7885 handleResponse : function(response){
7889 // default connection failure
7890 failure : function(response){
7892 this.response = response;
7893 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7894 this.form.afterAction(this, false);
7897 processResponse : function(response){
7898 this.response = response;
7899 if(!response.responseText){
7902 this.result = this.handleResponse(response);
7906 // utility functions used internally
7907 getUrl : function(appendParams){
7908 var url = this.options.url || this.form.url || this.form.el.dom.action;
7910 var p = this.getParams();
7912 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7918 getMethod : function(){
7919 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7922 getParams : function(){
7923 var bp = this.form.baseParams;
7924 var p = this.options.params;
7926 if(typeof p == "object"){
7927 p = Roo.urlEncode(Roo.applyIf(p, bp));
7928 }else if(typeof p == 'string' && bp){
7929 p += '&' + Roo.urlEncode(bp);
7932 p = Roo.urlEncode(bp);
7937 createCallback : function(){
7939 success: this.success,
7940 failure: this.failure,
7942 timeout: (this.form.timeout*1000),
7943 upload: this.form.fileUpload ? this.success : undefined
7948 Roo.form.Action.Submit = function(form, options){
7949 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7952 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7955 haveProgress : false,
7956 uploadComplete : false,
7958 // uploadProgress indicator.
7959 uploadProgress : function()
7961 if (!this.form.progressUrl) {
7965 if (!this.haveProgress) {
7966 Roo.MessageBox.progress("Uploading", "Uploading");
7968 if (this.uploadComplete) {
7969 Roo.MessageBox.hide();
7973 this.haveProgress = true;
7975 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7977 var c = new Roo.data.Connection();
7979 url : this.form.progressUrl,
7984 success : function(req){
7985 //console.log(data);
7989 rdata = Roo.decode(req.responseText)
7991 Roo.log("Invalid data from server..");
7995 if (!rdata || !rdata.success) {
7997 Roo.MessageBox.alert(Roo.encode(rdata));
8000 var data = rdata.data;
8002 if (this.uploadComplete) {
8003 Roo.MessageBox.hide();
8008 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
8009 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
8012 this.uploadProgress.defer(2000,this);
8015 failure: function(data) {
8016 Roo.log('progress url failed ');
8027 // run get Values on the form, so it syncs any secondary forms.
8028 this.form.getValues();
8030 var o = this.options;
8031 var method = this.getMethod();
8032 var isPost = method == 'POST';
8033 if(o.clientValidation === false || this.form.isValid()){
8035 if (this.form.progressUrl) {
8036 this.form.findField('UPLOAD_IDENTIFIER').setValue(
8037 (new Date() * 1) + '' + Math.random());
8042 Roo.Ajax.request(Roo.apply(this.createCallback(), {
8043 form:this.form.el.dom,
8044 url:this.getUrl(!isPost),
8046 params:isPost ? this.getParams() : null,
8047 isUpload: this.form.fileUpload,
8048 formData : this.form.formData
8051 this.uploadProgress();
8053 }else if (o.clientValidation !== false){ // client validation failed
8054 this.failureType = Roo.form.Action.CLIENT_INVALID;
8055 this.form.afterAction(this, false);
8059 success : function(response)
8061 this.uploadComplete= true;
8062 if (this.haveProgress) {
8063 Roo.MessageBox.hide();
8067 var result = this.processResponse(response);
8068 if(result === true || result.success){
8069 this.form.afterAction(this, true);
8073 this.form.markInvalid(result.errors);
8074 this.failureType = Roo.form.Action.SERVER_INVALID;
8076 this.form.afterAction(this, false);
8078 failure : function(response)
8080 this.uploadComplete= true;
8081 if (this.haveProgress) {
8082 Roo.MessageBox.hide();
8085 this.response = response;
8086 this.failureType = Roo.form.Action.CONNECT_FAILURE;
8087 this.form.afterAction(this, false);
8090 handleResponse : function(response){
8091 if(this.form.errorReader){
8092 var rs = this.form.errorReader.read(response);
8095 for(var i = 0, len = rs.records.length; i < len; i++) {
8096 var r = rs.records[i];
8100 if(errors.length < 1){
8104 success : rs.success,
8110 ret = Roo.decode(response.responseText);
8114 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8124 Roo.form.Action.Load = function(form, options){
8125 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8126 this.reader = this.form.reader;
8129 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8134 Roo.Ajax.request(Roo.apply(
8135 this.createCallback(), {
8136 method:this.getMethod(),
8137 url:this.getUrl(false),
8138 params:this.getParams()
8142 success : function(response){
8144 var result = this.processResponse(response);
8145 if(result === true || !result.success || !result.data){
8146 this.failureType = Roo.form.Action.LOAD_FAILURE;
8147 this.form.afterAction(this, false);
8150 this.form.clearInvalid();
8151 this.form.setValues(result.data);
8152 this.form.afterAction(this, true);
8155 handleResponse : function(response){
8156 if(this.form.reader){
8157 var rs = this.form.reader.read(response);
8158 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8160 success : rs.success,
8164 return Roo.decode(response.responseText);
8168 Roo.form.Action.ACTION_TYPES = {
8169 'load' : Roo.form.Action.Load,
8170 'submit' : Roo.form.Action.Submit
8179 * @class Roo.bootstrap.Form
8180 * @extends Roo.bootstrap.Component
8181 * Bootstrap Form class
8182 * @cfg {String} method GET | POST (default POST)
8183 * @cfg {String} labelAlign top | left (default top)
8184 * @cfg {String} align left | right - for navbars
8185 * @cfg {Boolean} loadMask load mask when submit (default true)
8190 * @param {Object} config The config object
8194 Roo.bootstrap.Form = function(config){
8196 Roo.bootstrap.Form.superclass.constructor.call(this, config);
8198 Roo.bootstrap.Form.popover.apply();
8202 * @event clientvalidation
8203 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8204 * @param {Form} this
8205 * @param {Boolean} valid true if the form has passed client-side validation
8207 clientvalidation: true,
8209 * @event beforeaction
8210 * Fires before any action is performed. Return false to cancel the action.
8211 * @param {Form} this
8212 * @param {Action} action The action to be performed
8216 * @event actionfailed
8217 * Fires when an action fails.
8218 * @param {Form} this
8219 * @param {Action} action The action that failed
8221 actionfailed : true,
8223 * @event actioncomplete
8224 * Fires when an action is completed.
8225 * @param {Form} this
8226 * @param {Action} action The action that completed
8228 actioncomplete : true
8232 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
8235 * @cfg {String} method
8236 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8241 * The URL to use for form actions if one isn't supplied in the action options.
8244 * @cfg {Boolean} fileUpload
8245 * Set to true if this form is a file upload.
8249 * @cfg {Object} baseParams
8250 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8254 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8258 * @cfg {Sting} align (left|right) for navbar forms
8263 activeAction : null,
8266 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8267 * element by passing it or its id or mask the form itself by passing in true.
8270 waitMsgTarget : false,
8275 * @cfg {Boolean} errorMask (true|false) default false
8280 * @cfg {Number} maskOffset Default 100
8285 * @cfg {Boolean} maskBody
8289 getAutoCreate : function(){
8293 method : this.method || 'POST',
8294 id : this.id || Roo.id(),
8297 if (this.parent().xtype.match(/^Nav/)) {
8298 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8302 if (this.labelAlign == 'left' ) {
8303 cfg.cls += ' form-horizontal';
8309 initEvents : function()
8311 this.el.on('submit', this.onSubmit, this);
8312 // this was added as random key presses on the form where triggering form submit.
8313 this.el.on('keypress', function(e) {
8314 if (e.getCharCode() != 13) {
8317 // we might need to allow it for textareas.. and some other items.
8318 // check e.getTarget().
8320 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8324 Roo.log("keypress blocked");
8332 onSubmit : function(e){
8337 * Returns true if client-side validation on the form is successful.
8340 isValid : function(){
8341 var items = this.getItems();
8345 items.each(function(f){
8351 Roo.log('invalid field: ' + f.name);
8355 if(!target && f.el.isVisible(true)){
8361 if(this.errorMask && !valid){
8362 Roo.bootstrap.Form.popover.mask(this, target);
8369 * Returns true if any fields in this form have changed since their original load.
8372 isDirty : function(){
8374 var items = this.getItems();
8375 items.each(function(f){
8385 * Performs a predefined action (submit or load) or custom actions you define on this form.
8386 * @param {String} actionName The name of the action type
8387 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8388 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8389 * accept other config options):
8391 Property Type Description
8392 ---------------- --------------- ----------------------------------------------------------------------------------
8393 url String The url for the action (defaults to the form's url)
8394 method String The form method to use (defaults to the form's method, or POST if not defined)
8395 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8396 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8397 validate the form on the client (defaults to false)
8399 * @return {BasicForm} this
8401 doAction : function(action, options){
8402 if(typeof action == 'string'){
8403 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8405 if(this.fireEvent('beforeaction', this, action) !== false){
8406 this.beforeAction(action);
8407 action.run.defer(100, action);
8413 beforeAction : function(action){
8414 var o = action.options;
8419 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8421 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8424 // not really supported yet.. ??
8426 //if(this.waitMsgTarget === true){
8427 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8428 //}else if(this.waitMsgTarget){
8429 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8430 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8432 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8438 afterAction : function(action, success){
8439 this.activeAction = null;
8440 var o = action.options;
8445 Roo.get(document.body).unmask();
8451 //if(this.waitMsgTarget === true){
8452 // this.el.unmask();
8453 //}else if(this.waitMsgTarget){
8454 // this.waitMsgTarget.unmask();
8456 // Roo.MessageBox.updateProgress(1);
8457 // Roo.MessageBox.hide();
8464 Roo.callback(o.success, o.scope, [this, action]);
8465 this.fireEvent('actioncomplete', this, action);
8469 // failure condition..
8470 // we have a scenario where updates need confirming.
8471 // eg. if a locking scenario exists..
8472 // we look for { errors : { needs_confirm : true }} in the response.
8474 (typeof(action.result) != 'undefined') &&
8475 (typeof(action.result.errors) != 'undefined') &&
8476 (typeof(action.result.errors.needs_confirm) != 'undefined')
8479 Roo.log("not supported yet");
8482 Roo.MessageBox.confirm(
8483 "Change requires confirmation",
8484 action.result.errorMsg,
8489 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8499 Roo.callback(o.failure, o.scope, [this, action]);
8500 // show an error message if no failed handler is set..
8501 if (!this.hasListener('actionfailed')) {
8502 Roo.log("need to add dialog support");
8504 Roo.MessageBox.alert("Error",
8505 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8506 action.result.errorMsg :
8507 "Saving Failed, please check your entries or try again"
8512 this.fireEvent('actionfailed', this, action);
8517 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8518 * @param {String} id The value to search for
8521 findField : function(id){
8522 var items = this.getItems();
8523 var field = items.get(id);
8525 items.each(function(f){
8526 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8533 return field || null;
8536 * Mark fields in this form invalid in bulk.
8537 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8538 * @return {BasicForm} this
8540 markInvalid : function(errors){
8541 if(errors instanceof Array){
8542 for(var i = 0, len = errors.length; i < len; i++){
8543 var fieldError = errors[i];
8544 var f = this.findField(fieldError.id);
8546 f.markInvalid(fieldError.msg);
8552 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8553 field.markInvalid(errors[id]);
8557 //Roo.each(this.childForms || [], function (f) {
8558 // f.markInvalid(errors);
8565 * Set values for fields in this form in bulk.
8566 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8567 * @return {BasicForm} this
8569 setValues : function(values){
8570 if(values instanceof Array){ // array of objects
8571 for(var i = 0, len = values.length; i < len; i++){
8573 var f = this.findField(v.id);
8575 f.setValue(v.value);
8576 if(this.trackResetOnLoad){
8577 f.originalValue = f.getValue();
8581 }else{ // object hash
8584 if(typeof values[id] != 'function' && (field = this.findField(id))){
8586 if (field.setFromData &&
8588 field.displayField &&
8589 // combos' with local stores can
8590 // be queried via setValue()
8591 // to set their value..
8592 (field.store && !field.store.isLocal)
8596 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8597 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8598 field.setFromData(sd);
8600 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8602 field.setFromData(values);
8605 field.setValue(values[id]);
8609 if(this.trackResetOnLoad){
8610 field.originalValue = field.getValue();
8616 //Roo.each(this.childForms || [], function (f) {
8617 // f.setValues(values);
8624 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8625 * they are returned as an array.
8626 * @param {Boolean} asString
8629 getValues : function(asString){
8630 //if (this.childForms) {
8631 // copy values from the child forms
8632 // Roo.each(this.childForms, function (f) {
8633 // this.setValues(f.getValues());
8639 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8640 if(asString === true){
8643 return Roo.urlDecode(fs);
8647 * Returns the fields in this form as an object with key/value pairs.
8648 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8651 getFieldValues : function(with_hidden)
8653 var items = this.getItems();
8655 items.each(function(f){
8661 var v = f.getValue();
8663 if (f.inputType =='radio') {
8664 if (typeof(ret[f.getName()]) == 'undefined') {
8665 ret[f.getName()] = ''; // empty..
8668 if (!f.el.dom.checked) {
8676 if(f.xtype == 'MoneyField'){
8677 ret[f.currencyName] = f.getCurrency();
8680 // not sure if this supported any more..
8681 if ((typeof(v) == 'object') && f.getRawValue) {
8682 v = f.getRawValue() ; // dates..
8684 // combo boxes where name != hiddenName...
8685 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8686 ret[f.name] = f.getRawValue();
8688 ret[f.getName()] = v;
8695 * Clears all invalid messages in this form.
8696 * @return {BasicForm} this
8698 clearInvalid : function(){
8699 var items = this.getItems();
8701 items.each(function(f){
8710 * @return {BasicForm} this
8713 var items = this.getItems();
8714 items.each(function(f){
8718 Roo.each(this.childForms || [], function (f) {
8726 getItems : function()
8728 var r=new Roo.util.MixedCollection(false, function(o){
8729 return o.id || (o.id = Roo.id());
8731 var iter = function(el) {
8738 Roo.each(el.items,function(e) {
8747 hideFields : function(items)
8749 Roo.each(items, function(i){
8751 var f = this.findField(i);
8762 showFields : function(items)
8764 Roo.each(items, function(i){
8766 var f = this.findField(i);
8779 Roo.apply(Roo.bootstrap.Form, {
8806 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8807 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8808 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8809 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8812 this.maskEl.top.enableDisplayMode("block");
8813 this.maskEl.left.enableDisplayMode("block");
8814 this.maskEl.bottom.enableDisplayMode("block");
8815 this.maskEl.right.enableDisplayMode("block");
8817 this.toolTip = new Roo.bootstrap.Tooltip({
8818 cls : 'roo-form-error-popover',
8820 'left' : ['r-l', [-2,0], 'right'],
8821 'right' : ['l-r', [2,0], 'left'],
8822 'bottom' : ['tl-bl', [0,2], 'top'],
8823 'top' : [ 'bl-tl', [0,-2], 'bottom']
8827 this.toolTip.render(Roo.get(document.body));
8829 this.toolTip.el.enableDisplayMode("block");
8831 Roo.get(document.body).on('click', function(){
8835 Roo.get(document.body).on('touchstart', function(){
8839 this.isApplied = true
8842 mask : function(form, target)
8846 this.target = target;
8848 if(!this.form.errorMask || !target.el){
8852 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8854 Roo.log(scrollable);
8856 var ot = this.target.el.calcOffsetsTo(scrollable);
8858 var scrollTo = ot[1] - this.form.maskOffset;
8860 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8862 scrollable.scrollTo('top', scrollTo);
8864 var box = this.target.el.getBox();
8866 var zIndex = Roo.bootstrap.Modal.zIndex++;
8869 this.maskEl.top.setStyle('position', 'absolute');
8870 this.maskEl.top.setStyle('z-index', zIndex);
8871 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8872 this.maskEl.top.setLeft(0);
8873 this.maskEl.top.setTop(0);
8874 this.maskEl.top.show();
8876 this.maskEl.left.setStyle('position', 'absolute');
8877 this.maskEl.left.setStyle('z-index', zIndex);
8878 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8879 this.maskEl.left.setLeft(0);
8880 this.maskEl.left.setTop(box.y - this.padding);
8881 this.maskEl.left.show();
8883 this.maskEl.bottom.setStyle('position', 'absolute');
8884 this.maskEl.bottom.setStyle('z-index', zIndex);
8885 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8886 this.maskEl.bottom.setLeft(0);
8887 this.maskEl.bottom.setTop(box.bottom + this.padding);
8888 this.maskEl.bottom.show();
8890 this.maskEl.right.setStyle('position', 'absolute');
8891 this.maskEl.right.setStyle('z-index', zIndex);
8892 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8893 this.maskEl.right.setLeft(box.right + this.padding);
8894 this.maskEl.right.setTop(box.y - this.padding);
8895 this.maskEl.right.show();
8897 this.toolTip.bindEl = this.target.el;
8899 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8901 var tip = this.target.blankText;
8903 if(this.target.getValue() !== '' ) {
8905 if (this.target.invalidText.length) {
8906 tip = this.target.invalidText;
8907 } else if (this.target.regexText.length){
8908 tip = this.target.regexText;
8912 this.toolTip.show(tip);
8914 this.intervalID = window.setInterval(function() {
8915 Roo.bootstrap.Form.popover.unmask();
8918 window.onwheel = function(){ return false;};
8920 (function(){ this.isMasked = true; }).defer(500, this);
8926 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8930 this.maskEl.top.setStyle('position', 'absolute');
8931 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8932 this.maskEl.top.hide();
8934 this.maskEl.left.setStyle('position', 'absolute');
8935 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8936 this.maskEl.left.hide();
8938 this.maskEl.bottom.setStyle('position', 'absolute');
8939 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8940 this.maskEl.bottom.hide();
8942 this.maskEl.right.setStyle('position', 'absolute');
8943 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8944 this.maskEl.right.hide();
8946 this.toolTip.hide();
8948 this.toolTip.el.hide();
8950 window.onwheel = function(){ return true;};
8952 if(this.intervalID){
8953 window.clearInterval(this.intervalID);
8954 this.intervalID = false;
8957 this.isMasked = false;
8967 * Ext JS Library 1.1.1
8968 * Copyright(c) 2006-2007, Ext JS, LLC.
8970 * Originally Released Under LGPL - original licence link has changed is not relivant.
8973 * <script type="text/javascript">
8976 * @class Roo.form.VTypes
8977 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8980 Roo.form.VTypes = function(){
8981 // closure these in so they are only created once.
8982 var alpha = /^[a-zA-Z_]+$/;
8983 var alphanum = /^[a-zA-Z0-9_]+$/;
8984 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8985 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8987 // All these messages and functions are configurable
8990 * The function used to validate email addresses
8991 * @param {String} value The email address
8993 'email' : function(v){
8994 return email.test(v);
8997 * The error text to display when the email validation function returns false
9000 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
9002 * The keystroke filter mask to be applied on email input
9005 'emailMask' : /[a-z0-9_\.\-@]/i,
9008 * The function used to validate URLs
9009 * @param {String} value The URL
9011 'url' : function(v){
9015 * The error text to display when the url validation function returns false
9018 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
9021 * The function used to validate alpha values
9022 * @param {String} value The value
9024 'alpha' : function(v){
9025 return alpha.test(v);
9028 * The error text to display when the alpha validation function returns false
9031 'alphaText' : 'This field should only contain letters and _',
9033 * The keystroke filter mask to be applied on alpha input
9036 'alphaMask' : /[a-z_]/i,
9039 * The function used to validate alphanumeric values
9040 * @param {String} value The value
9042 'alphanum' : function(v){
9043 return alphanum.test(v);
9046 * The error text to display when the alphanumeric validation function returns false
9049 'alphanumText' : 'This field should only contain letters, numbers and _',
9051 * The keystroke filter mask to be applied on alphanumeric input
9054 'alphanumMask' : /[a-z0-9_]/i
9064 * @class Roo.bootstrap.Input
9065 * @extends Roo.bootstrap.Component
9066 * Bootstrap Input class
9067 * @cfg {Boolean} disabled is it disabled
9068 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
9069 * @cfg {String} name name of the input
9070 * @cfg {string} fieldLabel - the label associated
9071 * @cfg {string} placeholder - placeholder to put in text.
9072 * @cfg {string} before - input group add on before
9073 * @cfg {string} after - input group add on after
9074 * @cfg {string} size - (lg|sm) or leave empty..
9075 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
9076 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
9077 * @cfg {Number} md colspan out of 12 for computer-sized screens
9078 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
9079 * @cfg {string} value default value of the input
9080 * @cfg {Number} labelWidth set the width of label
9081 * @cfg {Number} labellg set the width of label (1-12)
9082 * @cfg {Number} labelmd set the width of label (1-12)
9083 * @cfg {Number} labelsm set the width of label (1-12)
9084 * @cfg {Number} labelxs set the width of label (1-12)
9085 * @cfg {String} labelAlign (top|left)
9086 * @cfg {Boolean} readOnly Specifies that the field should be read-only
9087 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9088 * @cfg {String} indicatorpos (left|right) default left
9089 * @cfg {String} capture (user|camera) use for file input only. (default empty)
9090 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9092 * @cfg {String} align (left|center|right) Default left
9093 * @cfg {Boolean} forceFeedback (true|false) Default false
9096 * Create a new Input
9097 * @param {Object} config The config object
9100 Roo.bootstrap.Input = function(config){
9102 Roo.bootstrap.Input.superclass.constructor.call(this, config);
9107 * Fires when this field receives input focus.
9108 * @param {Roo.form.Field} this
9113 * Fires when this field loses input focus.
9114 * @param {Roo.form.Field} this
9119 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
9120 * {@link Roo.EventObject#getKey} to determine which key was pressed.
9121 * @param {Roo.form.Field} this
9122 * @param {Roo.EventObject} e The event object
9127 * Fires just before the field blurs if the field value has changed.
9128 * @param {Roo.form.Field} this
9129 * @param {Mixed} newValue The new value
9130 * @param {Mixed} oldValue The original value
9135 * Fires after the field has been marked as invalid.
9136 * @param {Roo.form.Field} this
9137 * @param {String} msg The validation message
9142 * Fires after the field has been validated with no errors.
9143 * @param {Roo.form.Field} this
9148 * Fires after the key up
9149 * @param {Roo.form.Field} this
9150 * @param {Roo.EventObject} e The event Object
9156 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
9158 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9159 automatic validation (defaults to "keyup").
9161 validationEvent : "keyup",
9163 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9165 validateOnBlur : true,
9167 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9169 validationDelay : 250,
9171 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9173 focusClass : "x-form-focus", // not needed???
9177 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9179 invalidClass : "has-warning",
9182 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9184 validClass : "has-success",
9187 * @cfg {Boolean} hasFeedback (true|false) default true
9192 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9194 invalidFeedbackClass : "glyphicon-warning-sign",
9197 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9199 validFeedbackClass : "glyphicon-ok",
9202 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9204 selectOnFocus : false,
9207 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9211 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9216 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9218 disableKeyFilter : false,
9221 * @cfg {Boolean} disabled True to disable the field (defaults to false).
9225 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9229 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9231 blankText : "Please complete this mandatory field",
9234 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9238 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9240 maxLength : Number.MAX_VALUE,
9242 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9244 minLengthText : "The minimum length for this field is {0}",
9246 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9248 maxLengthText : "The maximum length for this field is {0}",
9252 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9253 * If available, this function will be called only after the basic validators all return true, and will be passed the
9254 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9258 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9259 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9260 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
9264 * @cfg {String} regexText -- Depricated - use Invalid Text
9269 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9275 autocomplete: false,
9294 formatedValue : false,
9295 forceFeedback : false,
9297 indicatorpos : 'left',
9307 parentLabelAlign : function()
9310 while (parent.parent()) {
9311 parent = parent.parent();
9312 if (typeof(parent.labelAlign) !='undefined') {
9313 return parent.labelAlign;
9320 getAutoCreate : function()
9322 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9328 if(this.inputType != 'hidden'){
9329 cfg.cls = 'form-group' //input-group
9335 type : this.inputType,
9337 cls : 'form-control',
9338 placeholder : this.placeholder || '',
9339 autocomplete : this.autocomplete || 'new-password'
9342 if(this.capture.length){
9343 input.capture = this.capture;
9346 if(this.accept.length){
9347 input.accept = this.accept + "/*";
9351 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9354 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9355 input.maxLength = this.maxLength;
9358 if (this.disabled) {
9359 input.disabled=true;
9362 if (this.readOnly) {
9363 input.readonly=true;
9367 input.name = this.name;
9371 input.cls += ' input-' + this.size;
9375 ['xs','sm','md','lg'].map(function(size){
9376 if (settings[size]) {
9377 cfg.cls += ' col-' + size + '-' + settings[size];
9381 var inputblock = input;
9385 cls: 'glyphicon form-control-feedback'
9388 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9391 cls : 'has-feedback',
9399 if (this.before || this.after) {
9402 cls : 'input-group',
9406 if (this.before && typeof(this.before) == 'string') {
9408 inputblock.cn.push({
9410 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9414 if (this.before && typeof(this.before) == 'object') {
9415 this.before = Roo.factory(this.before);
9417 inputblock.cn.push({
9419 cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9420 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9424 inputblock.cn.push(input);
9426 if (this.after && typeof(this.after) == 'string') {
9427 inputblock.cn.push({
9429 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9433 if (this.after && typeof(this.after) == 'object') {
9434 this.after = Roo.factory(this.after);
9436 inputblock.cn.push({
9438 cls : 'roo-input-after input-group-append input-group-text input-group-' +
9439 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9443 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9444 inputblock.cls += ' has-feedback';
9445 inputblock.cn.push(feedback);
9450 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9451 tooltip : 'This field is required'
9453 if (Roo.bootstrap.version == 4) {
9456 style : 'display-none'
9459 if (align ==='left' && this.fieldLabel.length) {
9461 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
9468 cls : 'control-label col-form-label',
9469 html : this.fieldLabel
9480 var labelCfg = cfg.cn[1];
9481 var contentCfg = cfg.cn[2];
9483 if(this.indicatorpos == 'right'){
9488 cls : 'control-label col-form-label',
9492 html : this.fieldLabel
9506 labelCfg = cfg.cn[0];
9507 contentCfg = cfg.cn[1];
9511 if(this.labelWidth > 12){
9512 labelCfg.style = "width: " + this.labelWidth + 'px';
9515 if(this.labelWidth < 13 && this.labelmd == 0){
9516 this.labelmd = this.labelWidth;
9519 if(this.labellg > 0){
9520 labelCfg.cls += ' col-lg-' + this.labellg;
9521 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9524 if(this.labelmd > 0){
9525 labelCfg.cls += ' col-md-' + this.labelmd;
9526 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9529 if(this.labelsm > 0){
9530 labelCfg.cls += ' col-sm-' + this.labelsm;
9531 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9534 if(this.labelxs > 0){
9535 labelCfg.cls += ' col-xs-' + this.labelxs;
9536 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9540 } else if ( this.fieldLabel.length) {
9545 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9546 tooltip : 'This field is required'
9550 //cls : 'input-group-addon',
9551 html : this.fieldLabel
9559 if(this.indicatorpos == 'right'){
9564 //cls : 'input-group-addon',
9565 html : this.fieldLabel
9570 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9571 tooltip : 'This field is required'
9591 if (this.parentType === 'Navbar' && this.parent().bar) {
9592 cfg.cls += ' navbar-form';
9595 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9596 // on BS4 we do this only if not form
9597 cfg.cls += ' navbar-form';
9605 * return the real input element.
9607 inputEl: function ()
9609 return this.el.select('input.form-control',true).first();
9612 tooltipEl : function()
9614 return this.inputEl();
9617 indicatorEl : function()
9619 if (Roo.bootstrap.version == 4) {
9620 return false; // not enabled in v4 yet.
9623 var indicator = this.el.select('i.roo-required-indicator',true).first();
9633 setDisabled : function(v)
9635 var i = this.inputEl().dom;
9637 i.removeAttribute('disabled');
9641 i.setAttribute('disabled','true');
9643 initEvents : function()
9646 this.inputEl().on("keydown" , this.fireKey, this);
9647 this.inputEl().on("focus", this.onFocus, this);
9648 this.inputEl().on("blur", this.onBlur, this);
9650 this.inputEl().relayEvent('keyup', this);
9652 this.indicator = this.indicatorEl();
9655 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9658 // reference to original value for reset
9659 this.originalValue = this.getValue();
9660 //Roo.form.TextField.superclass.initEvents.call(this);
9661 if(this.validationEvent == 'keyup'){
9662 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9663 this.inputEl().on('keyup', this.filterValidation, this);
9665 else if(this.validationEvent !== false){
9666 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9669 if(this.selectOnFocus){
9670 this.on("focus", this.preFocus, this);
9673 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9674 this.inputEl().on("keypress", this.filterKeys, this);
9676 this.inputEl().relayEvent('keypress', this);
9679 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9680 this.el.on("click", this.autoSize, this);
9683 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9684 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9687 if (typeof(this.before) == 'object') {
9688 this.before.render(this.el.select('.roo-input-before',true).first());
9690 if (typeof(this.after) == 'object') {
9691 this.after.render(this.el.select('.roo-input-after',true).first());
9694 this.inputEl().on('change', this.onChange, this);
9697 filterValidation : function(e){
9698 if(!e.isNavKeyPress()){
9699 this.validationTask.delay(this.validationDelay);
9703 * Validates the field value
9704 * @return {Boolean} True if the value is valid, else false
9706 validate : function(){
9707 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9708 if(this.disabled || this.validateValue(this.getRawValue())){
9719 * Validates a value according to the field's validation rules and marks the field as invalid
9720 * if the validation fails
9721 * @param {Mixed} value The value to validate
9722 * @return {Boolean} True if the value is valid, else false
9724 validateValue : function(value)
9726 if(this.getVisibilityEl().hasClass('hidden')){
9730 if(value.length < 1) { // if it's blank
9731 if(this.allowBlank){
9737 if(value.length < this.minLength){
9740 if(value.length > this.maxLength){
9744 var vt = Roo.form.VTypes;
9745 if(!vt[this.vtype](value, this)){
9749 if(typeof this.validator == "function"){
9750 var msg = this.validator(value);
9754 if (typeof(msg) == 'string') {
9755 this.invalidText = msg;
9759 if(this.regex && !this.regex.test(value)){
9767 fireKey : function(e){
9768 //Roo.log('field ' + e.getKey());
9769 if(e.isNavKeyPress()){
9770 this.fireEvent("specialkey", this, e);
9773 focus : function (selectText){
9775 this.inputEl().focus();
9776 if(selectText === true){
9777 this.inputEl().dom.select();
9783 onFocus : function(){
9784 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9785 // this.el.addClass(this.focusClass);
9788 this.hasFocus = true;
9789 this.startValue = this.getValue();
9790 this.fireEvent("focus", this);
9794 beforeBlur : Roo.emptyFn,
9798 onBlur : function(){
9800 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9801 //this.el.removeClass(this.focusClass);
9803 this.hasFocus = false;
9804 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9807 var v = this.getValue();
9808 if(String(v) !== String(this.startValue)){
9809 this.fireEvent('change', this, v, this.startValue);
9811 this.fireEvent("blur", this);
9814 onChange : function(e)
9816 var v = this.getValue();
9817 if(String(v) !== String(this.startValue)){
9818 this.fireEvent('change', this, v, this.startValue);
9824 * Resets the current field value to the originally loaded value and clears any validation messages
9827 this.setValue(this.originalValue);
9831 * Returns the name of the field
9832 * @return {Mixed} name The name field
9834 getName: function(){
9838 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9839 * @return {Mixed} value The field value
9841 getValue : function(){
9843 var v = this.inputEl().getValue();
9848 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9849 * @return {Mixed} value The field value
9851 getRawValue : function(){
9852 var v = this.inputEl().getValue();
9858 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9859 * @param {Mixed} value The value to set
9861 setRawValue : function(v){
9862 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9865 selectText : function(start, end){
9866 var v = this.getRawValue();
9868 start = start === undefined ? 0 : start;
9869 end = end === undefined ? v.length : end;
9870 var d = this.inputEl().dom;
9871 if(d.setSelectionRange){
9872 d.setSelectionRange(start, end);
9873 }else if(d.createTextRange){
9874 var range = d.createTextRange();
9875 range.moveStart("character", start);
9876 range.moveEnd("character", v.length-end);
9883 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9884 * @param {Mixed} value The value to set
9886 setValue : function(v){
9889 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9895 processValue : function(value){
9896 if(this.stripCharsRe){
9897 var newValue = value.replace(this.stripCharsRe, '');
9898 if(newValue !== value){
9899 this.setRawValue(newValue);
9906 preFocus : function(){
9908 if(this.selectOnFocus){
9909 this.inputEl().dom.select();
9912 filterKeys : function(e){
9914 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9917 var c = e.getCharCode(), cc = String.fromCharCode(c);
9918 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9921 if(!this.maskRe.test(cc)){
9926 * Clear any invalid styles/messages for this field
9928 clearInvalid : function(){
9930 if(!this.el || this.preventMark){ // not rendered
9935 this.el.removeClass([this.invalidClass, 'is-invalid']);
9937 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9939 var feedback = this.el.select('.form-control-feedback', true).first();
9942 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9948 this.indicator.removeClass('visible');
9949 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9952 this.fireEvent('valid', this);
9956 * Mark this field as valid
9958 markValid : function()
9960 if(!this.el || this.preventMark){ // not rendered...
9964 this.el.removeClass([this.invalidClass, this.validClass]);
9965 this.inputEl().removeClass(['is-valid', 'is-invalid']);
9967 var feedback = this.el.select('.form-control-feedback', true).first();
9970 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9974 this.indicator.removeClass('visible');
9975 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9982 if(this.allowBlank && !this.getRawValue().length){
9985 if (Roo.bootstrap.version == 3) {
9986 this.el.addClass(this.validClass);
9988 this.inputEl().addClass('is-valid');
9991 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9993 var feedback = this.el.select('.form-control-feedback', true).first();
9996 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9997 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10002 this.fireEvent('valid', this);
10006 * Mark this field as invalid
10007 * @param {String} msg The validation message
10009 markInvalid : function(msg)
10011 if(!this.el || this.preventMark){ // not rendered
10015 this.el.removeClass([this.invalidClass, this.validClass]);
10016 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10018 var feedback = this.el.select('.form-control-feedback', true).first();
10021 this.el.select('.form-control-feedback', true).first().removeClass(
10022 [this.invalidFeedbackClass, this.validFeedbackClass]);
10029 if(this.allowBlank && !this.getRawValue().length){
10033 if(this.indicator){
10034 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
10035 this.indicator.addClass('visible');
10037 if (Roo.bootstrap.version == 3) {
10038 this.el.addClass(this.invalidClass);
10040 this.inputEl().addClass('is-invalid');
10045 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10047 var feedback = this.el.select('.form-control-feedback', true).first();
10050 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10052 if(this.getValue().length || this.forceFeedback){
10053 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10060 this.fireEvent('invalid', this, msg);
10063 SafariOnKeyDown : function(event)
10065 // this is a workaround for a password hang bug on chrome/ webkit.
10066 if (this.inputEl().dom.type != 'password') {
10070 var isSelectAll = false;
10072 if(this.inputEl().dom.selectionEnd > 0){
10073 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
10075 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
10076 event.preventDefault();
10081 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10083 event.preventDefault();
10084 // this is very hacky as keydown always get's upper case.
10086 var cc = String.fromCharCode(event.getCharCode());
10087 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
10091 adjustWidth : function(tag, w){
10092 tag = tag.toLowerCase();
10093 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10094 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10095 if(tag == 'input'){
10098 if(tag == 'textarea'){
10101 }else if(Roo.isOpera){
10102 if(tag == 'input'){
10105 if(tag == 'textarea'){
10113 setFieldLabel : function(v)
10115 if(!this.rendered){
10119 if(this.indicatorEl()){
10120 var ar = this.el.select('label > span',true);
10122 if (ar.elements.length) {
10123 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10124 this.fieldLabel = v;
10128 var br = this.el.select('label',true);
10130 if(br.elements.length) {
10131 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10132 this.fieldLabel = v;
10136 Roo.log('Cannot Found any of label > span || label in input');
10140 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10141 this.fieldLabel = v;
10156 * @class Roo.bootstrap.TextArea
10157 * @extends Roo.bootstrap.Input
10158 * Bootstrap TextArea class
10159 * @cfg {Number} cols Specifies the visible width of a text area
10160 * @cfg {Number} rows Specifies the visible number of lines in a text area
10161 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10162 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10163 * @cfg {string} html text
10166 * Create a new TextArea
10167 * @param {Object} config The config object
10170 Roo.bootstrap.TextArea = function(config){
10171 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10175 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
10185 getAutoCreate : function(){
10187 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10193 if(this.inputType != 'hidden'){
10194 cfg.cls = 'form-group' //input-group
10202 value : this.value || '',
10203 html: this.html || '',
10204 cls : 'form-control',
10205 placeholder : this.placeholder || ''
10209 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10210 input.maxLength = this.maxLength;
10214 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10218 input.cols = this.cols;
10221 if (this.readOnly) {
10222 input.readonly = true;
10226 input.name = this.name;
10230 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10234 ['xs','sm','md','lg'].map(function(size){
10235 if (settings[size]) {
10236 cfg.cls += ' col-' + size + '-' + settings[size];
10240 var inputblock = input;
10242 if(this.hasFeedback && !this.allowBlank){
10246 cls: 'glyphicon form-control-feedback'
10250 cls : 'has-feedback',
10259 if (this.before || this.after) {
10262 cls : 'input-group',
10266 inputblock.cn.push({
10268 cls : 'input-group-addon',
10273 inputblock.cn.push(input);
10275 if(this.hasFeedback && !this.allowBlank){
10276 inputblock.cls += ' has-feedback';
10277 inputblock.cn.push(feedback);
10281 inputblock.cn.push({
10283 cls : 'input-group-addon',
10290 if (align ==='left' && this.fieldLabel.length) {
10295 cls : 'control-label',
10296 html : this.fieldLabel
10307 if(this.labelWidth > 12){
10308 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10311 if(this.labelWidth < 13 && this.labelmd == 0){
10312 this.labelmd = this.labelWidth;
10315 if(this.labellg > 0){
10316 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10317 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10320 if(this.labelmd > 0){
10321 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10322 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10325 if(this.labelsm > 0){
10326 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10327 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10330 if(this.labelxs > 0){
10331 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10332 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10335 } else if ( this.fieldLabel.length) {
10340 //cls : 'input-group-addon',
10341 html : this.fieldLabel
10359 if (this.disabled) {
10360 input.disabled=true;
10367 * return the real textarea element.
10369 inputEl: function ()
10371 return this.el.select('textarea.form-control',true).first();
10375 * Clear any invalid styles/messages for this field
10377 clearInvalid : function()
10380 if(!this.el || this.preventMark){ // not rendered
10384 var label = this.el.select('label', true).first();
10385 var icon = this.el.select('i.fa-star', true).first();
10390 this.el.removeClass( this.validClass);
10391 this.inputEl().removeClass('is-invalid');
10393 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10395 var feedback = this.el.select('.form-control-feedback', true).first();
10398 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10403 this.fireEvent('valid', this);
10407 * Mark this field as valid
10409 markValid : function()
10411 if(!this.el || this.preventMark){ // not rendered
10415 this.el.removeClass([this.invalidClass, this.validClass]);
10416 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10418 var feedback = this.el.select('.form-control-feedback', true).first();
10421 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10424 if(this.disabled || this.allowBlank){
10428 var label = this.el.select('label', true).first();
10429 var icon = this.el.select('i.fa-star', true).first();
10434 if (Roo.bootstrap.version == 3) {
10435 this.el.addClass(this.validClass);
10437 this.inputEl().addClass('is-valid');
10441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10443 var feedback = this.el.select('.form-control-feedback', true).first();
10446 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10447 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10452 this.fireEvent('valid', this);
10456 * Mark this field as invalid
10457 * @param {String} msg The validation message
10459 markInvalid : function(msg)
10461 if(!this.el || this.preventMark){ // not rendered
10465 this.el.removeClass([this.invalidClass, this.validClass]);
10466 this.inputEl().removeClass(['is-valid', 'is-invalid']);
10468 var feedback = this.el.select('.form-control-feedback', true).first();
10471 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10474 if(this.disabled || this.allowBlank){
10478 var label = this.el.select('label', true).first();
10479 var icon = this.el.select('i.fa-star', true).first();
10481 if(!this.getValue().length && label && !icon){
10482 this.el.createChild({
10484 cls : 'text-danger fa fa-lg fa-star',
10485 tooltip : 'This field is required',
10486 style : 'margin-right:5px;'
10490 if (Roo.bootstrap.version == 3) {
10491 this.el.addClass(this.invalidClass);
10493 this.inputEl().addClass('is-invalid');
10496 // fixme ... this may be depricated need to test..
10497 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10499 var feedback = this.el.select('.form-control-feedback', true).first();
10502 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10504 if(this.getValue().length || this.forceFeedback){
10505 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10512 this.fireEvent('invalid', this, msg);
10520 * trigger field - base class for combo..
10525 * @class Roo.bootstrap.TriggerField
10526 * @extends Roo.bootstrap.Input
10527 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10528 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10529 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10530 * for which you can provide a custom implementation. For example:
10532 var trigger = new Roo.bootstrap.TriggerField();
10533 trigger.onTriggerClick = myTriggerFn;
10534 trigger.applyTo('my-field');
10537 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10538 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10539 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10540 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10541 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10544 * Create a new TriggerField.
10545 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10546 * to the base TextField)
10548 Roo.bootstrap.TriggerField = function(config){
10549 this.mimicing = false;
10550 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10553 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10555 * @cfg {String} triggerClass A CSS class to apply to the trigger
10558 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10563 * @cfg {Boolean} removable (true|false) special filter default false
10567 /** @cfg {Boolean} grow @hide */
10568 /** @cfg {Number} growMin @hide */
10569 /** @cfg {Number} growMax @hide */
10575 autoSize: Roo.emptyFn,
10579 deferHeight : true,
10582 actionMode : 'wrap',
10587 getAutoCreate : function(){
10589 var align = this.labelAlign || this.parentLabelAlign();
10594 cls: 'form-group' //input-group
10601 type : this.inputType,
10602 cls : 'form-control',
10603 autocomplete: 'new-password',
10604 placeholder : this.placeholder || ''
10608 input.name = this.name;
10611 input.cls += ' input-' + this.size;
10614 if (this.disabled) {
10615 input.disabled=true;
10618 var inputblock = input;
10620 if(this.hasFeedback && !this.allowBlank){
10624 cls: 'glyphicon form-control-feedback'
10627 if(this.removable && !this.editable && !this.tickable){
10629 cls : 'has-feedback',
10635 cls : 'roo-combo-removable-btn close'
10642 cls : 'has-feedback',
10651 if(this.removable && !this.editable && !this.tickable){
10653 cls : 'roo-removable',
10659 cls : 'roo-combo-removable-btn close'
10666 if (this.before || this.after) {
10669 cls : 'input-group',
10673 inputblock.cn.push({
10675 cls : 'input-group-addon input-group-prepend input-group-text',
10680 inputblock.cn.push(input);
10682 if(this.hasFeedback && !this.allowBlank){
10683 inputblock.cls += ' has-feedback';
10684 inputblock.cn.push(feedback);
10688 inputblock.cn.push({
10690 cls : 'input-group-addon input-group-append input-group-text',
10699 var ibwrap = inputblock;
10704 cls: 'roo-select2-choices',
10708 cls: 'roo-select2-search-field',
10720 cls: 'roo-select2-container input-group',
10725 cls: 'form-hidden-field'
10731 if(!this.multiple && this.showToggleBtn){
10737 if (this.caret != false) {
10740 cls: 'fa fa-' + this.caret
10747 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10749 Roo.bootstrap.version == 3 ? caret : '',
10752 cls: 'combobox-clear',
10766 combobox.cls += ' roo-select2-container-multi';
10770 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10771 tooltip : 'This field is required'
10773 if (Roo.bootstrap.version == 4) {
10776 style : 'display:none'
10781 if (align ==='left' && this.fieldLabel.length) {
10783 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10790 cls : 'control-label',
10791 html : this.fieldLabel
10803 var labelCfg = cfg.cn[1];
10804 var contentCfg = cfg.cn[2];
10806 if(this.indicatorpos == 'right'){
10811 cls : 'control-label',
10815 html : this.fieldLabel
10829 labelCfg = cfg.cn[0];
10830 contentCfg = cfg.cn[1];
10833 if(this.labelWidth > 12){
10834 labelCfg.style = "width: " + this.labelWidth + 'px';
10837 if(this.labelWidth < 13 && this.labelmd == 0){
10838 this.labelmd = this.labelWidth;
10841 if(this.labellg > 0){
10842 labelCfg.cls += ' col-lg-' + this.labellg;
10843 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10846 if(this.labelmd > 0){
10847 labelCfg.cls += ' col-md-' + this.labelmd;
10848 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10851 if(this.labelsm > 0){
10852 labelCfg.cls += ' col-sm-' + this.labelsm;
10853 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10856 if(this.labelxs > 0){
10857 labelCfg.cls += ' col-xs-' + this.labelxs;
10858 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10861 } else if ( this.fieldLabel.length) {
10862 // Roo.log(" label");
10867 //cls : 'input-group-addon',
10868 html : this.fieldLabel
10876 if(this.indicatorpos == 'right'){
10884 html : this.fieldLabel
10898 // Roo.log(" no label && no align");
10905 ['xs','sm','md','lg'].map(function(size){
10906 if (settings[size]) {
10907 cfg.cls += ' col-' + size + '-' + settings[size];
10918 onResize : function(w, h){
10919 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10920 // if(typeof w == 'number'){
10921 // var x = w - this.trigger.getWidth();
10922 // this.inputEl().setWidth(this.adjustWidth('input', x));
10923 // this.trigger.setStyle('left', x+'px');
10928 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10931 getResizeEl : function(){
10932 return this.inputEl();
10936 getPositionEl : function(){
10937 return this.inputEl();
10941 alignErrorIcon : function(){
10942 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10946 initEvents : function(){
10950 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10951 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10952 if(!this.multiple && this.showToggleBtn){
10953 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10954 if(this.hideTrigger){
10955 this.trigger.setDisplayed(false);
10957 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10961 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10964 if(this.removable && !this.editable && !this.tickable){
10965 var close = this.closeTriggerEl();
10968 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10969 close.on('click', this.removeBtnClick, this, close);
10973 //this.trigger.addClassOnOver('x-form-trigger-over');
10974 //this.trigger.addClassOnClick('x-form-trigger-click');
10977 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10981 closeTriggerEl : function()
10983 var close = this.el.select('.roo-combo-removable-btn', true).first();
10984 return close ? close : false;
10987 removeBtnClick : function(e, h, el)
10989 e.preventDefault();
10991 if(this.fireEvent("remove", this) !== false){
10993 this.fireEvent("afterremove", this)
10997 createList : function()
10999 this.list = Roo.get(document.body).createChild({
11000 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
11001 cls: 'typeahead typeahead-long dropdown-menu',
11002 style: 'display:none'
11005 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
11010 initTrigger : function(){
11015 onDestroy : function(){
11017 this.trigger.removeAllListeners();
11018 // this.trigger.remove();
11021 // this.wrap.remove();
11023 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
11027 onFocus : function(){
11028 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
11030 if(!this.mimicing){
11031 this.wrap.addClass('x-trigger-wrap-focus');
11032 this.mimicing = true;
11033 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
11034 if(this.monitorTab){
11035 this.el.on("keydown", this.checkTab, this);
11042 checkTab : function(e){
11043 if(e.getKey() == e.TAB){
11044 this.triggerBlur();
11049 onBlur : function(){
11054 mimicBlur : function(e, t){
11056 if(!this.wrap.contains(t) && this.validateBlur()){
11057 this.triggerBlur();
11063 triggerBlur : function(){
11064 this.mimicing = false;
11065 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
11066 if(this.monitorTab){
11067 this.el.un("keydown", this.checkTab, this);
11069 //this.wrap.removeClass('x-trigger-wrap-focus');
11070 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
11074 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
11075 validateBlur : function(e, t){
11080 onDisable : function(){
11081 this.inputEl().dom.disabled = true;
11082 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11084 // this.wrap.addClass('x-item-disabled');
11089 onEnable : function(){
11090 this.inputEl().dom.disabled = false;
11091 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11093 // this.el.removeClass('x-item-disabled');
11098 onShow : function(){
11099 var ae = this.getActionEl();
11102 ae.dom.style.display = '';
11103 ae.dom.style.visibility = 'visible';
11109 onHide : function(){
11110 var ae = this.getActionEl();
11111 ae.dom.style.display = 'none';
11115 * The function that should handle the trigger's click event. This method does nothing by default until overridden
11116 * by an implementing function.
11118 * @param {EventObject} e
11120 onTriggerClick : Roo.emptyFn
11124 * Ext JS Library 1.1.1
11125 * Copyright(c) 2006-2007, Ext JS, LLC.
11127 * Originally Released Under LGPL - original licence link has changed is not relivant.
11130 * <script type="text/javascript">
11135 * @class Roo.data.SortTypes
11137 * Defines the default sorting (casting?) comparison functions used when sorting data.
11139 Roo.data.SortTypes = {
11141 * Default sort that does nothing
11142 * @param {Mixed} s The value being converted
11143 * @return {Mixed} The comparison value
11145 none : function(s){
11150 * The regular expression used to strip tags
11154 stripTagsRE : /<\/?[^>]+>/gi,
11157 * Strips all HTML tags to sort on text only
11158 * @param {Mixed} s The value being converted
11159 * @return {String} The comparison value
11161 asText : function(s){
11162 return String(s).replace(this.stripTagsRE, "");
11166 * Strips all HTML tags to sort on text only - Case insensitive
11167 * @param {Mixed} s The value being converted
11168 * @return {String} The comparison value
11170 asUCText : function(s){
11171 return String(s).toUpperCase().replace(this.stripTagsRE, "");
11175 * Case insensitive string
11176 * @param {Mixed} s The value being converted
11177 * @return {String} The comparison value
11179 asUCString : function(s) {
11180 return String(s).toUpperCase();
11185 * @param {Mixed} s The value being converted
11186 * @return {Number} The comparison value
11188 asDate : function(s) {
11192 if(s instanceof Date){
11193 return s.getTime();
11195 return Date.parse(String(s));
11200 * @param {Mixed} s The value being converted
11201 * @return {Float} The comparison value
11203 asFloat : function(s) {
11204 var val = parseFloat(String(s).replace(/,/g, ""));
11213 * @param {Mixed} s The value being converted
11214 * @return {Number} The comparison value
11216 asInt : function(s) {
11217 var val = parseInt(String(s).replace(/,/g, ""));
11225 * Ext JS Library 1.1.1
11226 * Copyright(c) 2006-2007, Ext JS, LLC.
11228 * Originally Released Under LGPL - original licence link has changed is not relivant.
11231 * <script type="text/javascript">
11235 * @class Roo.data.Record
11236 * Instances of this class encapsulate both record <em>definition</em> information, and record
11237 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11238 * to access Records cached in an {@link Roo.data.Store} object.<br>
11240 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11241 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11244 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11246 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11247 * {@link #create}. The parameters are the same.
11248 * @param {Array} data An associative Array of data values keyed by the field name.
11249 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11250 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11251 * not specified an integer id is generated.
11253 Roo.data.Record = function(data, id){
11254 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11259 * Generate a constructor for a specific record layout.
11260 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11261 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11262 * Each field definition object may contain the following properties: <ul>
11263 * <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,
11264 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11265 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11266 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11267 * is being used, then this is a string containing the javascript expression to reference the data relative to
11268 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11269 * to the data item relative to the record element. If the mapping expression is the same as the field name,
11270 * this may be omitted.</p></li>
11271 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11272 * <ul><li>auto (Default, implies no conversion)</li>
11277 * <li>date</li></ul></p></li>
11278 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11279 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11280 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11281 * by the Reader into an object that will be stored in the Record. It is passed the
11282 * following parameters:<ul>
11283 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11285 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11287 * <br>usage:<br><pre><code>
11288 var TopicRecord = Roo.data.Record.create(
11289 {name: 'title', mapping: 'topic_title'},
11290 {name: 'author', mapping: 'username'},
11291 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11292 {name: 'lastPost', mapping: 'post_time', type: 'date'},
11293 {name: 'lastPoster', mapping: 'user2'},
11294 {name: 'excerpt', mapping: 'post_text'}
11297 var myNewRecord = new TopicRecord({
11298 title: 'Do my job please',
11301 lastPost: new Date(),
11302 lastPoster: 'Animal',
11303 excerpt: 'No way dude!'
11305 myStore.add(myNewRecord);
11310 Roo.data.Record.create = function(o){
11311 var f = function(){
11312 f.superclass.constructor.apply(this, arguments);
11314 Roo.extend(f, Roo.data.Record);
11315 var p = f.prototype;
11316 p.fields = new Roo.util.MixedCollection(false, function(field){
11319 for(var i = 0, len = o.length; i < len; i++){
11320 p.fields.add(new Roo.data.Field(o[i]));
11322 f.getField = function(name){
11323 return p.fields.get(name);
11328 Roo.data.Record.AUTO_ID = 1000;
11329 Roo.data.Record.EDIT = 'edit';
11330 Roo.data.Record.REJECT = 'reject';
11331 Roo.data.Record.COMMIT = 'commit';
11333 Roo.data.Record.prototype = {
11335 * Readonly flag - true if this record has been modified.
11344 join : function(store){
11345 this.store = store;
11349 * Set the named field to the specified value.
11350 * @param {String} name The name of the field to set.
11351 * @param {Object} value The value to set the field to.
11353 set : function(name, value){
11354 if(this.data[name] == value){
11358 if(!this.modified){
11359 this.modified = {};
11361 if(typeof this.modified[name] == 'undefined'){
11362 this.modified[name] = this.data[name];
11364 this.data[name] = value;
11365 if(!this.editing && this.store){
11366 this.store.afterEdit(this);
11371 * Get the value of the named field.
11372 * @param {String} name The name of the field to get the value of.
11373 * @return {Object} The value of the field.
11375 get : function(name){
11376 return this.data[name];
11380 beginEdit : function(){
11381 this.editing = true;
11382 this.modified = {};
11386 cancelEdit : function(){
11387 this.editing = false;
11388 delete this.modified;
11392 endEdit : function(){
11393 this.editing = false;
11394 if(this.dirty && this.store){
11395 this.store.afterEdit(this);
11400 * Usually called by the {@link Roo.data.Store} which owns the Record.
11401 * Rejects all changes made to the Record since either creation, or the last commit operation.
11402 * Modified fields are reverted to their original values.
11404 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11405 * of reject operations.
11407 reject : function(){
11408 var m = this.modified;
11410 if(typeof m[n] != "function"){
11411 this.data[n] = m[n];
11414 this.dirty = false;
11415 delete this.modified;
11416 this.editing = false;
11418 this.store.afterReject(this);
11423 * Usually called by the {@link Roo.data.Store} which owns the Record.
11424 * Commits all changes made to the Record since either creation, or the last commit operation.
11426 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11427 * of commit operations.
11429 commit : function(){
11430 this.dirty = false;
11431 delete this.modified;
11432 this.editing = false;
11434 this.store.afterCommit(this);
11439 hasError : function(){
11440 return this.error != null;
11444 clearError : function(){
11449 * Creates a copy of this record.
11450 * @param {String} id (optional) A new record id if you don't want to use this record's id
11453 copy : function(newId) {
11454 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11458 * Ext JS Library 1.1.1
11459 * Copyright(c) 2006-2007, Ext JS, LLC.
11461 * Originally Released Under LGPL - original licence link has changed is not relivant.
11464 * <script type="text/javascript">
11470 * @class Roo.data.Store
11471 * @extends Roo.util.Observable
11472 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11473 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11475 * 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
11476 * has no knowledge of the format of the data returned by the Proxy.<br>
11478 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11479 * instances from the data object. These records are cached and made available through accessor functions.
11481 * Creates a new Store.
11482 * @param {Object} config A config object containing the objects needed for the Store to access data,
11483 * and read the data into Records.
11485 Roo.data.Store = function(config){
11486 this.data = new Roo.util.MixedCollection(false);
11487 this.data.getKey = function(o){
11490 this.baseParams = {};
11492 this.paramNames = {
11497 "multisort" : "_multisort"
11500 if(config && config.data){
11501 this.inlineData = config.data;
11502 delete config.data;
11505 Roo.apply(this, config);
11507 if(this.reader){ // reader passed
11508 this.reader = Roo.factory(this.reader, Roo.data);
11509 this.reader.xmodule = this.xmodule || false;
11510 if(!this.recordType){
11511 this.recordType = this.reader.recordType;
11513 if(this.reader.onMetaChange){
11514 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11518 if(this.recordType){
11519 this.fields = this.recordType.prototype.fields;
11521 this.modified = [];
11525 * @event datachanged
11526 * Fires when the data cache has changed, and a widget which is using this Store
11527 * as a Record cache should refresh its view.
11528 * @param {Store} this
11530 datachanged : true,
11532 * @event metachange
11533 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11534 * @param {Store} this
11535 * @param {Object} meta The JSON metadata
11540 * Fires when Records have been added to the Store
11541 * @param {Store} this
11542 * @param {Roo.data.Record[]} records The array of Records added
11543 * @param {Number} index The index at which the record(s) were added
11548 * Fires when a Record has been removed from the Store
11549 * @param {Store} this
11550 * @param {Roo.data.Record} record The Record that was removed
11551 * @param {Number} index The index at which the record was removed
11556 * Fires when a Record has been updated
11557 * @param {Store} this
11558 * @param {Roo.data.Record} record The Record that was updated
11559 * @param {String} operation The update operation being performed. Value may be one of:
11561 Roo.data.Record.EDIT
11562 Roo.data.Record.REJECT
11563 Roo.data.Record.COMMIT
11569 * Fires when the data cache has been cleared.
11570 * @param {Store} this
11574 * @event beforeload
11575 * Fires before a request is made for a new data object. If the beforeload handler returns false
11576 * the load action will be canceled.
11577 * @param {Store} this
11578 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11582 * @event beforeloadadd
11583 * Fires after a new set of Records has been loaded.
11584 * @param {Store} this
11585 * @param {Roo.data.Record[]} records The Records that were loaded
11586 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11588 beforeloadadd : true,
11591 * Fires after a new set of Records has been loaded, before they are added to the store.
11592 * @param {Store} this
11593 * @param {Roo.data.Record[]} records The Records that were loaded
11594 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11595 * @params {Object} return from reader
11599 * @event loadexception
11600 * Fires if an exception occurs in the Proxy during loading.
11601 * Called with the signature of the Proxy's "loadexception" event.
11602 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11605 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11606 * @param {Object} load options
11607 * @param {Object} jsonData from your request (normally this contains the Exception)
11609 loadexception : true
11613 this.proxy = Roo.factory(this.proxy, Roo.data);
11614 this.proxy.xmodule = this.xmodule || false;
11615 this.relayEvents(this.proxy, ["loadexception"]);
11617 this.sortToggle = {};
11618 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11620 Roo.data.Store.superclass.constructor.call(this);
11622 if(this.inlineData){
11623 this.loadData(this.inlineData);
11624 delete this.inlineData;
11628 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11630 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11631 * without a remote query - used by combo/forms at present.
11635 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11638 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11641 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11642 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11645 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11646 * on any HTTP request
11649 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11652 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11656 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11657 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11659 remoteSort : false,
11662 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11663 * loaded or when a record is removed. (defaults to false).
11665 pruneModifiedRecords : false,
11668 lastOptions : null,
11671 * Add Records to the Store and fires the add event.
11672 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11674 add : function(records){
11675 records = [].concat(records);
11676 for(var i = 0, len = records.length; i < len; i++){
11677 records[i].join(this);
11679 var index = this.data.length;
11680 this.data.addAll(records);
11681 this.fireEvent("add", this, records, index);
11685 * Remove a Record from the Store and fires the remove event.
11686 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11688 remove : function(record){
11689 var index = this.data.indexOf(record);
11690 this.data.removeAt(index);
11692 if(this.pruneModifiedRecords){
11693 this.modified.remove(record);
11695 this.fireEvent("remove", this, record, index);
11699 * Remove all Records from the Store and fires the clear event.
11701 removeAll : function(){
11703 if(this.pruneModifiedRecords){
11704 this.modified = [];
11706 this.fireEvent("clear", this);
11710 * Inserts Records to the Store at the given index and fires the add event.
11711 * @param {Number} index The start index at which to insert the passed Records.
11712 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11714 insert : function(index, records){
11715 records = [].concat(records);
11716 for(var i = 0, len = records.length; i < len; i++){
11717 this.data.insert(index, records[i]);
11718 records[i].join(this);
11720 this.fireEvent("add", this, records, index);
11724 * Get the index within the cache of the passed Record.
11725 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11726 * @return {Number} The index of the passed Record. Returns -1 if not found.
11728 indexOf : function(record){
11729 return this.data.indexOf(record);
11733 * Get the index within the cache of the Record with the passed id.
11734 * @param {String} id The id of the Record to find.
11735 * @return {Number} The index of the Record. Returns -1 if not found.
11737 indexOfId : function(id){
11738 return this.data.indexOfKey(id);
11742 * Get the Record with the specified id.
11743 * @param {String} id The id of the Record to find.
11744 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11746 getById : function(id){
11747 return this.data.key(id);
11751 * Get the Record at the specified index.
11752 * @param {Number} index The index of the Record to find.
11753 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11755 getAt : function(index){
11756 return this.data.itemAt(index);
11760 * Returns a range of Records between specified indices.
11761 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11762 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11763 * @return {Roo.data.Record[]} An array of Records
11765 getRange : function(start, end){
11766 return this.data.getRange(start, end);
11770 storeOptions : function(o){
11771 o = Roo.apply({}, o);
11774 this.lastOptions = o;
11778 * Loads the Record cache from the configured Proxy using the configured Reader.
11780 * If using remote paging, then the first load call must specify the <em>start</em>
11781 * and <em>limit</em> properties in the options.params property to establish the initial
11782 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11784 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11785 * and this call will return before the new data has been loaded. Perform any post-processing
11786 * in a callback function, or in a "load" event handler.</strong>
11788 * @param {Object} options An object containing properties which control loading options:<ul>
11789 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11790 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11791 * passed the following arguments:<ul>
11792 * <li>r : Roo.data.Record[]</li>
11793 * <li>options: Options object from the load call</li>
11794 * <li>success: Boolean success indicator</li></ul></li>
11795 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11796 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11799 load : function(options){
11800 options = options || {};
11801 if(this.fireEvent("beforeload", this, options) !== false){
11802 this.storeOptions(options);
11803 var p = Roo.apply(options.params || {}, this.baseParams);
11804 // if meta was not loaded from remote source.. try requesting it.
11805 if (!this.reader.metaFromRemote) {
11806 p._requestMeta = 1;
11808 if(this.sortInfo && this.remoteSort){
11809 var pn = this.paramNames;
11810 p[pn["sort"]] = this.sortInfo.field;
11811 p[pn["dir"]] = this.sortInfo.direction;
11813 if (this.multiSort) {
11814 var pn = this.paramNames;
11815 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11818 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11823 * Reloads the Record cache from the configured Proxy using the configured Reader and
11824 * the options from the last load operation performed.
11825 * @param {Object} options (optional) An object containing properties which may override the options
11826 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11827 * the most recently used options are reused).
11829 reload : function(options){
11830 this.load(Roo.applyIf(options||{}, this.lastOptions));
11834 // Called as a callback by the Reader during a load operation.
11835 loadRecords : function(o, options, success){
11836 if(!o || success === false){
11837 if(success !== false){
11838 this.fireEvent("load", this, [], options, o);
11840 if(options.callback){
11841 options.callback.call(options.scope || this, [], options, false);
11845 // if data returned failure - throw an exception.
11846 if (o.success === false) {
11847 // show a message if no listener is registered.
11848 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11849 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11851 // loadmask wil be hooked into this..
11852 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11855 var r = o.records, t = o.totalRecords || r.length;
11857 this.fireEvent("beforeloadadd", this, r, options, o);
11859 if(!options || options.add !== true){
11860 if(this.pruneModifiedRecords){
11861 this.modified = [];
11863 for(var i = 0, len = r.length; i < len; i++){
11867 this.data = this.snapshot;
11868 delete this.snapshot;
11871 this.data.addAll(r);
11872 this.totalLength = t;
11874 this.fireEvent("datachanged", this);
11876 this.totalLength = Math.max(t, this.data.length+r.length);
11880 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11882 var e = new Roo.data.Record({});
11884 e.set(this.parent.displayField, this.parent.emptyTitle);
11885 e.set(this.parent.valueField, '');
11890 this.fireEvent("load", this, r, options, o);
11891 if(options.callback){
11892 options.callback.call(options.scope || this, r, options, true);
11898 * Loads data from a passed data block. A Reader which understands the format of the data
11899 * must have been configured in the constructor.
11900 * @param {Object} data The data block from which to read the Records. The format of the data expected
11901 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11902 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11904 loadData : function(o, append){
11905 var r = this.reader.readRecords(o);
11906 this.loadRecords(r, {add: append}, true);
11910 * Gets the number of cached records.
11912 * <em>If using paging, this may not be the total size of the dataset. If the data object
11913 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11914 * the data set size</em>
11916 getCount : function(){
11917 return this.data.length || 0;
11921 * Gets the total number of records in the dataset as returned by the server.
11923 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11924 * the dataset size</em>
11926 getTotalCount : function(){
11927 return this.totalLength || 0;
11931 * Returns the sort state of the Store as an object with two properties:
11933 field {String} The name of the field by which the Records are sorted
11934 direction {String} The sort order, "ASC" or "DESC"
11937 getSortState : function(){
11938 return this.sortInfo;
11942 applySort : function(){
11943 if(this.sortInfo && !this.remoteSort){
11944 var s = this.sortInfo, f = s.field;
11945 var st = this.fields.get(f).sortType;
11946 var fn = function(r1, r2){
11947 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11948 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11950 this.data.sort(s.direction, fn);
11951 if(this.snapshot && this.snapshot != this.data){
11952 this.snapshot.sort(s.direction, fn);
11958 * Sets the default sort column and order to be used by the next load operation.
11959 * @param {String} fieldName The name of the field to sort by.
11960 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11962 setDefaultSort : function(field, dir){
11963 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11967 * Sort the Records.
11968 * If remote sorting is used, the sort is performed on the server, and the cache is
11969 * reloaded. If local sorting is used, the cache is sorted internally.
11970 * @param {String} fieldName The name of the field to sort by.
11971 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11973 sort : function(fieldName, dir){
11974 var f = this.fields.get(fieldName);
11976 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11978 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11979 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11984 this.sortToggle[f.name] = dir;
11985 this.sortInfo = {field: f.name, direction: dir};
11986 if(!this.remoteSort){
11988 this.fireEvent("datachanged", this);
11990 this.load(this.lastOptions);
11995 * Calls the specified function for each of the Records in the cache.
11996 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11997 * Returning <em>false</em> aborts and exits the iteration.
11998 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
12000 each : function(fn, scope){
12001 this.data.each(fn, scope);
12005 * Gets all records modified since the last commit. Modified records are persisted across load operations
12006 * (e.g., during paging).
12007 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
12009 getModifiedRecords : function(){
12010 return this.modified;
12014 createFilterFn : function(property, value, anyMatch){
12015 if(!value.exec){ // not a regex
12016 value = String(value);
12017 if(value.length == 0){
12020 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
12022 return function(r){
12023 return value.test(r.data[property]);
12028 * Sums the value of <i>property</i> for each record between start and end and returns the result.
12029 * @param {String} property A field on your records
12030 * @param {Number} start The record index to start at (defaults to 0)
12031 * @param {Number} end The last record index to include (defaults to length - 1)
12032 * @return {Number} The sum
12034 sum : function(property, start, end){
12035 var rs = this.data.items, v = 0;
12036 start = start || 0;
12037 end = (end || end === 0) ? end : rs.length-1;
12039 for(var i = start; i <= end; i++){
12040 v += (rs[i].data[property] || 0);
12046 * Filter the records by a specified property.
12047 * @param {String} field A field on your records
12048 * @param {String/RegExp} value Either a string that the field
12049 * should start with or a RegExp to test against the field
12050 * @param {Boolean} anyMatch True to match any part not just the beginning
12052 filter : function(property, value, anyMatch){
12053 var fn = this.createFilterFn(property, value, anyMatch);
12054 return fn ? this.filterBy(fn) : this.clearFilter();
12058 * Filter by a function. The specified function will be called with each
12059 * record in this data source. If the function returns true the record is included,
12060 * otherwise it is filtered.
12061 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12062 * @param {Object} scope (optional) The scope of the function (defaults to this)
12064 filterBy : function(fn, scope){
12065 this.snapshot = this.snapshot || this.data;
12066 this.data = this.queryBy(fn, scope||this);
12067 this.fireEvent("datachanged", this);
12071 * Query the records by a specified property.
12072 * @param {String} field A field on your records
12073 * @param {String/RegExp} value Either a string that the field
12074 * should start with or a RegExp to test against the field
12075 * @param {Boolean} anyMatch True to match any part not just the beginning
12076 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12078 query : function(property, value, anyMatch){
12079 var fn = this.createFilterFn(property, value, anyMatch);
12080 return fn ? this.queryBy(fn) : this.data.clone();
12084 * Query by a function. The specified function will be called with each
12085 * record in this data source. If the function returns true the record is included
12087 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12088 * @param {Object} scope (optional) The scope of the function (defaults to this)
12089 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12091 queryBy : function(fn, scope){
12092 var data = this.snapshot || this.data;
12093 return data.filterBy(fn, scope||this);
12097 * Collects unique values for a particular dataIndex from this store.
12098 * @param {String} dataIndex The property to collect
12099 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12100 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12101 * @return {Array} An array of the unique values
12103 collect : function(dataIndex, allowNull, bypassFilter){
12104 var d = (bypassFilter === true && this.snapshot) ?
12105 this.snapshot.items : this.data.items;
12106 var v, sv, r = [], l = {};
12107 for(var i = 0, len = d.length; i < len; i++){
12108 v = d[i].data[dataIndex];
12110 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12119 * Revert to a view of the Record cache with no filtering applied.
12120 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12122 clearFilter : function(suppressEvent){
12123 if(this.snapshot && this.snapshot != this.data){
12124 this.data = this.snapshot;
12125 delete this.snapshot;
12126 if(suppressEvent !== true){
12127 this.fireEvent("datachanged", this);
12133 afterEdit : function(record){
12134 if(this.modified.indexOf(record) == -1){
12135 this.modified.push(record);
12137 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12141 afterReject : function(record){
12142 this.modified.remove(record);
12143 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12147 afterCommit : function(record){
12148 this.modified.remove(record);
12149 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12153 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12154 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12156 commitChanges : function(){
12157 var m = this.modified.slice(0);
12158 this.modified = [];
12159 for(var i = 0, len = m.length; i < len; i++){
12165 * Cancel outstanding changes on all changed records.
12167 rejectChanges : function(){
12168 var m = this.modified.slice(0);
12169 this.modified = [];
12170 for(var i = 0, len = m.length; i < len; i++){
12175 onMetaChange : function(meta, rtype, o){
12176 this.recordType = rtype;
12177 this.fields = rtype.prototype.fields;
12178 delete this.snapshot;
12179 this.sortInfo = meta.sortInfo || this.sortInfo;
12180 this.modified = [];
12181 this.fireEvent('metachange', this, this.reader.meta);
12184 moveIndex : function(data, type)
12186 var index = this.indexOf(data);
12188 var newIndex = index + type;
12192 this.insert(newIndex, data);
12197 * Ext JS Library 1.1.1
12198 * Copyright(c) 2006-2007, Ext JS, LLC.
12200 * Originally Released Under LGPL - original licence link has changed is not relivant.
12203 * <script type="text/javascript">
12207 * @class Roo.data.SimpleStore
12208 * @extends Roo.data.Store
12209 * Small helper class to make creating Stores from Array data easier.
12210 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12211 * @cfg {Array} fields An array of field definition objects, or field name strings.
12212 * @cfg {Array} data The multi-dimensional array of data
12214 * @param {Object} config
12216 Roo.data.SimpleStore = function(config){
12217 Roo.data.SimpleStore.superclass.constructor.call(this, {
12219 reader: new Roo.data.ArrayReader({
12222 Roo.data.Record.create(config.fields)
12224 proxy : new Roo.data.MemoryProxy(config.data)
12228 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12230 * Ext JS Library 1.1.1
12231 * Copyright(c) 2006-2007, Ext JS, LLC.
12233 * Originally Released Under LGPL - original licence link has changed is not relivant.
12236 * <script type="text/javascript">
12241 * @extends Roo.data.Store
12242 * @class Roo.data.JsonStore
12243 * Small helper class to make creating Stores for JSON data easier. <br/>
12245 var store = new Roo.data.JsonStore({
12246 url: 'get-images.php',
12248 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12251 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12252 * JsonReader and HttpProxy (unless inline data is provided).</b>
12253 * @cfg {Array} fields An array of field definition objects, or field name strings.
12255 * @param {Object} config
12257 Roo.data.JsonStore = function(c){
12258 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12259 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12260 reader: new Roo.data.JsonReader(c, c.fields)
12263 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12265 * Ext JS Library 1.1.1
12266 * Copyright(c) 2006-2007, Ext JS, LLC.
12268 * Originally Released Under LGPL - original licence link has changed is not relivant.
12271 * <script type="text/javascript">
12275 Roo.data.Field = function(config){
12276 if(typeof config == "string"){
12277 config = {name: config};
12279 Roo.apply(this, config);
12282 this.type = "auto";
12285 var st = Roo.data.SortTypes;
12286 // named sortTypes are supported, here we look them up
12287 if(typeof this.sortType == "string"){
12288 this.sortType = st[this.sortType];
12291 // set default sortType for strings and dates
12292 if(!this.sortType){
12295 this.sortType = st.asUCString;
12298 this.sortType = st.asDate;
12301 this.sortType = st.none;
12306 var stripRe = /[\$,%]/g;
12308 // prebuilt conversion function for this field, instead of
12309 // switching every time we're reading a value
12311 var cv, dateFormat = this.dateFormat;
12316 cv = function(v){ return v; };
12319 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12323 return v !== undefined && v !== null && v !== '' ?
12324 parseInt(String(v).replace(stripRe, ""), 10) : '';
12329 return v !== undefined && v !== null && v !== '' ?
12330 parseFloat(String(v).replace(stripRe, ""), 10) : '';
12335 cv = function(v){ return v === true || v === "true" || v == 1; };
12342 if(v instanceof Date){
12346 if(dateFormat == "timestamp"){
12347 return new Date(v*1000);
12349 return Date.parseDate(v, dateFormat);
12351 var parsed = Date.parse(v);
12352 return parsed ? new Date(parsed) : null;
12361 Roo.data.Field.prototype = {
12369 * Ext JS Library 1.1.1
12370 * Copyright(c) 2006-2007, Ext JS, LLC.
12372 * Originally Released Under LGPL - original licence link has changed is not relivant.
12375 * <script type="text/javascript">
12378 // Base class for reading structured data from a data source. This class is intended to be
12379 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12382 * @class Roo.data.DataReader
12383 * Base class for reading structured data from a data source. This class is intended to be
12384 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12387 Roo.data.DataReader = function(meta, recordType){
12391 this.recordType = recordType instanceof Array ?
12392 Roo.data.Record.create(recordType) : recordType;
12395 Roo.data.DataReader.prototype = {
12397 * Create an empty record
12398 * @param {Object} data (optional) - overlay some values
12399 * @return {Roo.data.Record} record created.
12401 newRow : function(d) {
12403 this.recordType.prototype.fields.each(function(c) {
12405 case 'int' : da[c.name] = 0; break;
12406 case 'date' : da[c.name] = new Date(); break;
12407 case 'float' : da[c.name] = 0.0; break;
12408 case 'boolean' : da[c.name] = false; break;
12409 default : da[c.name] = ""; break;
12413 return new this.recordType(Roo.apply(da, d));
12418 * Ext JS Library 1.1.1
12419 * Copyright(c) 2006-2007, Ext JS, LLC.
12421 * Originally Released Under LGPL - original licence link has changed is not relivant.
12424 * <script type="text/javascript">
12428 * @class Roo.data.DataProxy
12429 * @extends Roo.data.Observable
12430 * This class is an abstract base class for implementations which provide retrieval of
12431 * unformatted data objects.<br>
12433 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12434 * (of the appropriate type which knows how to parse the data object) to provide a block of
12435 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12437 * Custom implementations must implement the load method as described in
12438 * {@link Roo.data.HttpProxy#load}.
12440 Roo.data.DataProxy = function(){
12443 * @event beforeload
12444 * Fires before a network request is made to retrieve a data object.
12445 * @param {Object} This DataProxy object.
12446 * @param {Object} params The params parameter to the load function.
12451 * Fires before the load method's callback is called.
12452 * @param {Object} This DataProxy object.
12453 * @param {Object} o The data object.
12454 * @param {Object} arg The callback argument object passed to the load function.
12458 * @event loadexception
12459 * Fires if an Exception occurs during data retrieval.
12460 * @param {Object} This DataProxy object.
12461 * @param {Object} o The data object.
12462 * @param {Object} arg The callback argument object passed to the load function.
12463 * @param {Object} e The Exception.
12465 loadexception : true
12467 Roo.data.DataProxy.superclass.constructor.call(this);
12470 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12473 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12477 * Ext JS Library 1.1.1
12478 * Copyright(c) 2006-2007, Ext JS, LLC.
12480 * Originally Released Under LGPL - original licence link has changed is not relivant.
12483 * <script type="text/javascript">
12486 * @class Roo.data.MemoryProxy
12487 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12488 * to the Reader when its load method is called.
12490 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12492 Roo.data.MemoryProxy = function(data){
12496 Roo.data.MemoryProxy.superclass.constructor.call(this);
12500 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12503 * Load data from the requested source (in this case an in-memory
12504 * data object passed to the constructor), read the data object into
12505 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12506 * process that block using the passed callback.
12507 * @param {Object} params This parameter is not used by the MemoryProxy class.
12508 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12509 * object into a block of Roo.data.Records.
12510 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12511 * The function must be passed <ul>
12512 * <li>The Record block object</li>
12513 * <li>The "arg" argument from the load function</li>
12514 * <li>A boolean success indicator</li>
12516 * @param {Object} scope The scope in which to call the callback
12517 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12519 load : function(params, reader, callback, scope, arg){
12520 params = params || {};
12523 result = reader.readRecords(params.data ? params.data :this.data);
12525 this.fireEvent("loadexception", this, arg, null, e);
12526 callback.call(scope, null, arg, false);
12529 callback.call(scope, result, arg, true);
12533 update : function(params, records){
12538 * Ext JS Library 1.1.1
12539 * Copyright(c) 2006-2007, Ext JS, LLC.
12541 * Originally Released Under LGPL - original licence link has changed is not relivant.
12544 * <script type="text/javascript">
12547 * @class Roo.data.HttpProxy
12548 * @extends Roo.data.DataProxy
12549 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12550 * configured to reference a certain URL.<br><br>
12552 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12553 * from which the running page was served.<br><br>
12555 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12557 * Be aware that to enable the browser to parse an XML document, the server must set
12558 * the Content-Type header in the HTTP response to "text/xml".
12560 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12561 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12562 * will be used to make the request.
12564 Roo.data.HttpProxy = function(conn){
12565 Roo.data.HttpProxy.superclass.constructor.call(this);
12566 // is conn a conn config or a real conn?
12568 this.useAjax = !conn || !conn.events;
12572 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12573 // thse are take from connection...
12576 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12579 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12580 * extra parameters to each request made by this object. (defaults to undefined)
12583 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12584 * to each request made by this object. (defaults to undefined)
12587 * @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)
12590 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12593 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12599 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12603 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12604 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12605 * a finer-grained basis than the DataProxy events.
12607 getConnection : function(){
12608 return this.useAjax ? Roo.Ajax : this.conn;
12612 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12613 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12614 * process that block using the passed callback.
12615 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12616 * for the request to the remote server.
12617 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12618 * object into a block of Roo.data.Records.
12619 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12620 * The function must be passed <ul>
12621 * <li>The Record block object</li>
12622 * <li>The "arg" argument from the load function</li>
12623 * <li>A boolean success indicator</li>
12625 * @param {Object} scope The scope in which to call the callback
12626 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12628 load : function(params, reader, callback, scope, arg){
12629 if(this.fireEvent("beforeload", this, params) !== false){
12631 params : params || {},
12633 callback : callback,
12638 callback : this.loadResponse,
12642 Roo.applyIf(o, this.conn);
12643 if(this.activeRequest){
12644 Roo.Ajax.abort(this.activeRequest);
12646 this.activeRequest = Roo.Ajax.request(o);
12648 this.conn.request(o);
12651 callback.call(scope||this, null, arg, false);
12656 loadResponse : function(o, success, response){
12657 delete this.activeRequest;
12659 this.fireEvent("loadexception", this, o, response);
12660 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12665 result = o.reader.read(response);
12667 this.fireEvent("loadexception", this, o, response, e);
12668 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12672 this.fireEvent("load", this, o, o.request.arg);
12673 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12677 update : function(dataSet){
12682 updateResponse : function(dataSet){
12687 * Ext JS Library 1.1.1
12688 * Copyright(c) 2006-2007, Ext JS, LLC.
12690 * Originally Released Under LGPL - original licence link has changed is not relivant.
12693 * <script type="text/javascript">
12697 * @class Roo.data.ScriptTagProxy
12698 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12699 * other than the originating domain of the running page.<br><br>
12701 * <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
12702 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12704 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12705 * source code that is used as the source inside a <script> tag.<br><br>
12707 * In order for the browser to process the returned data, the server must wrap the data object
12708 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12709 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12710 * depending on whether the callback name was passed:
12713 boolean scriptTag = false;
12714 String cb = request.getParameter("callback");
12717 response.setContentType("text/javascript");
12719 response.setContentType("application/x-json");
12721 Writer out = response.getWriter();
12723 out.write(cb + "(");
12725 out.print(dataBlock.toJsonString());
12732 * @param {Object} config A configuration object.
12734 Roo.data.ScriptTagProxy = function(config){
12735 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12736 Roo.apply(this, config);
12737 this.head = document.getElementsByTagName("head")[0];
12740 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12742 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12744 * @cfg {String} url The URL from which to request the data object.
12747 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12751 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12752 * the server the name of the callback function set up by the load call to process the returned data object.
12753 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12754 * javascript output which calls this named function passing the data object as its only parameter.
12756 callbackParam : "callback",
12758 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12759 * name to the request.
12764 * Load data from the configured URL, read the data object into
12765 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12766 * process that block using the passed callback.
12767 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12768 * for the request to the remote server.
12769 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12770 * object into a block of Roo.data.Records.
12771 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12772 * The function must be passed <ul>
12773 * <li>The Record block object</li>
12774 * <li>The "arg" argument from the load function</li>
12775 * <li>A boolean success indicator</li>
12777 * @param {Object} scope The scope in which to call the callback
12778 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12780 load : function(params, reader, callback, scope, arg){
12781 if(this.fireEvent("beforeload", this, params) !== false){
12783 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12785 var url = this.url;
12786 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12788 url += "&_dc=" + (new Date().getTime());
12790 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12793 cb : "stcCallback"+transId,
12794 scriptId : "stcScript"+transId,
12798 callback : callback,
12804 window[trans.cb] = function(o){
12805 conn.handleResponse(o, trans);
12808 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12810 if(this.autoAbort !== false){
12814 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12816 var script = document.createElement("script");
12817 script.setAttribute("src", url);
12818 script.setAttribute("type", "text/javascript");
12819 script.setAttribute("id", trans.scriptId);
12820 this.head.appendChild(script);
12822 this.trans = trans;
12824 callback.call(scope||this, null, arg, false);
12829 isLoading : function(){
12830 return this.trans ? true : false;
12834 * Abort the current server request.
12836 abort : function(){
12837 if(this.isLoading()){
12838 this.destroyTrans(this.trans);
12843 destroyTrans : function(trans, isLoaded){
12844 this.head.removeChild(document.getElementById(trans.scriptId));
12845 clearTimeout(trans.timeoutId);
12847 window[trans.cb] = undefined;
12849 delete window[trans.cb];
12852 // if hasn't been loaded, wait for load to remove it to prevent script error
12853 window[trans.cb] = function(){
12854 window[trans.cb] = undefined;
12856 delete window[trans.cb];
12863 handleResponse : function(o, trans){
12864 this.trans = false;
12865 this.destroyTrans(trans, true);
12868 result = trans.reader.readRecords(o);
12870 this.fireEvent("loadexception", this, o, trans.arg, e);
12871 trans.callback.call(trans.scope||window, null, trans.arg, false);
12874 this.fireEvent("load", this, o, trans.arg);
12875 trans.callback.call(trans.scope||window, result, trans.arg, true);
12879 handleFailure : function(trans){
12880 this.trans = false;
12881 this.destroyTrans(trans, false);
12882 this.fireEvent("loadexception", this, null, trans.arg);
12883 trans.callback.call(trans.scope||window, null, trans.arg, false);
12887 * Ext JS Library 1.1.1
12888 * Copyright(c) 2006-2007, Ext JS, LLC.
12890 * Originally Released Under LGPL - original licence link has changed is not relivant.
12893 * <script type="text/javascript">
12897 * @class Roo.data.JsonReader
12898 * @extends Roo.data.DataReader
12899 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12900 * based on mappings in a provided Roo.data.Record constructor.
12902 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12903 * in the reply previously.
12908 var RecordDef = Roo.data.Record.create([
12909 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12910 {name: 'occupation'} // This field will use "occupation" as the mapping.
12912 var myReader = new Roo.data.JsonReader({
12913 totalProperty: "results", // The property which contains the total dataset size (optional)
12914 root: "rows", // The property which contains an Array of row objects
12915 id: "id" // The property within each row object that provides an ID for the record (optional)
12919 * This would consume a JSON file like this:
12921 { 'results': 2, 'rows': [
12922 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12923 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12926 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12927 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12928 * paged from the remote server.
12929 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12930 * @cfg {String} root name of the property which contains the Array of row objects.
12931 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12932 * @cfg {Array} fields Array of field definition objects
12934 * Create a new JsonReader
12935 * @param {Object} meta Metadata configuration options
12936 * @param {Object} recordType Either an Array of field definition objects,
12937 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12939 Roo.data.JsonReader = function(meta, recordType){
12942 // set some defaults:
12943 Roo.applyIf(meta, {
12944 totalProperty: 'total',
12945 successProperty : 'success',
12950 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12952 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12955 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12956 * Used by Store query builder to append _requestMeta to params.
12959 metaFromRemote : false,
12961 * This method is only used by a DataProxy which has retrieved data from a remote server.
12962 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12963 * @return {Object} data A data block which is used by an Roo.data.Store object as
12964 * a cache of Roo.data.Records.
12966 read : function(response){
12967 var json = response.responseText;
12969 var o = /* eval:var:o */ eval("("+json+")");
12971 throw {message: "JsonReader.read: Json object not found"};
12977 this.metaFromRemote = true;
12978 this.meta = o.metaData;
12979 this.recordType = Roo.data.Record.create(o.metaData.fields);
12980 this.onMetaChange(this.meta, this.recordType, o);
12982 return this.readRecords(o);
12985 // private function a store will implement
12986 onMetaChange : function(meta, recordType, o){
12993 simpleAccess: function(obj, subsc) {
13000 getJsonAccessor: function(){
13002 return function(expr) {
13004 return(re.test(expr))
13005 ? new Function("obj", "return obj." + expr)
13010 return Roo.emptyFn;
13015 * Create a data block containing Roo.data.Records from an XML document.
13016 * @param {Object} o An object which contains an Array of row objects in the property specified
13017 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
13018 * which contains the total size of the dataset.
13019 * @return {Object} data A data block which is used by an Roo.data.Store object as
13020 * a cache of Roo.data.Records.
13022 readRecords : function(o){
13024 * After any data loads, the raw JSON data is available for further custom processing.
13028 var s = this.meta, Record = this.recordType,
13029 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
13031 // Generate extraction functions for the totalProperty, the root, the id, and for each field
13033 if(s.totalProperty) {
13034 this.getTotal = this.getJsonAccessor(s.totalProperty);
13036 if(s.successProperty) {
13037 this.getSuccess = this.getJsonAccessor(s.successProperty);
13039 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
13041 var g = this.getJsonAccessor(s.id);
13042 this.getId = function(rec) {
13044 return (r === undefined || r === "") ? null : r;
13047 this.getId = function(){return null;};
13050 for(var jj = 0; jj < fl; jj++){
13052 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
13053 this.ef[jj] = this.getJsonAccessor(map);
13057 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
13058 if(s.totalProperty){
13059 var vt = parseInt(this.getTotal(o), 10);
13064 if(s.successProperty){
13065 var vs = this.getSuccess(o);
13066 if(vs === false || vs === 'false'){
13071 for(var i = 0; i < c; i++){
13074 var id = this.getId(n);
13075 for(var j = 0; j < fl; j++){
13077 var v = this.ef[j](n);
13079 Roo.log('missing convert for ' + f.name);
13083 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13085 var record = new Record(values, id);
13087 records[i] = record;
13093 totalRecords : totalRecords
13098 * Ext JS Library 1.1.1
13099 * Copyright(c) 2006-2007, Ext JS, LLC.
13101 * Originally Released Under LGPL - original licence link has changed is not relivant.
13104 * <script type="text/javascript">
13108 * @class Roo.data.ArrayReader
13109 * @extends Roo.data.DataReader
13110 * Data reader class to create an Array of Roo.data.Record objects from an Array.
13111 * Each element of that Array represents a row of data fields. The
13112 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13113 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13117 var RecordDef = Roo.data.Record.create([
13118 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
13119 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
13121 var myReader = new Roo.data.ArrayReader({
13122 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
13126 * This would consume an Array like this:
13128 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13132 * Create a new JsonReader
13133 * @param {Object} meta Metadata configuration options.
13134 * @param {Object|Array} recordType Either an Array of field definition objects
13136 * @cfg {Array} fields Array of field definition objects
13137 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13138 * as specified to {@link Roo.data.Record#create},
13139 * or an {@link Roo.data.Record} object
13142 * created using {@link Roo.data.Record#create}.
13144 Roo.data.ArrayReader = function(meta, recordType){
13147 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13150 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13152 * Create a data block containing Roo.data.Records from an XML document.
13153 * @param {Object} o An Array of row objects which represents the dataset.
13154 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13155 * a cache of Roo.data.Records.
13157 readRecords : function(o){
13158 var sid = this.meta ? this.meta.id : null;
13159 var recordType = this.recordType, fields = recordType.prototype.fields;
13162 for(var i = 0; i < root.length; i++){
13165 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13166 for(var j = 0, jlen = fields.length; j < jlen; j++){
13167 var f = fields.items[j];
13168 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13169 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13171 values[f.name] = v;
13173 var record = new recordType(values, id);
13175 records[records.length] = record;
13179 totalRecords : records.length
13188 * @class Roo.bootstrap.ComboBox
13189 * @extends Roo.bootstrap.TriggerField
13190 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13191 * @cfg {Boolean} append (true|false) default false
13192 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13193 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13194 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13195 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13196 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13197 * @cfg {Boolean} animate default true
13198 * @cfg {Boolean} emptyResultText only for touch device
13199 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13200 * @cfg {String} emptyTitle default ''
13202 * Create a new ComboBox.
13203 * @param {Object} config Configuration options
13205 Roo.bootstrap.ComboBox = function(config){
13206 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13210 * Fires when the dropdown list is expanded
13211 * @param {Roo.bootstrap.ComboBox} combo This combo box
13216 * Fires when the dropdown list is collapsed
13217 * @param {Roo.bootstrap.ComboBox} combo This combo box
13221 * @event beforeselect
13222 * Fires before a list item is selected. Return false to cancel the selection.
13223 * @param {Roo.bootstrap.ComboBox} combo This combo box
13224 * @param {Roo.data.Record} record The data record returned from the underlying store
13225 * @param {Number} index The index of the selected item in the dropdown list
13227 'beforeselect' : true,
13230 * Fires when a list item is selected
13231 * @param {Roo.bootstrap.ComboBox} combo This combo box
13232 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13233 * @param {Number} index The index of the selected item in the dropdown list
13237 * @event beforequery
13238 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13239 * The event object passed has these properties:
13240 * @param {Roo.bootstrap.ComboBox} combo This combo box
13241 * @param {String} query The query
13242 * @param {Boolean} forceAll true to force "all" query
13243 * @param {Boolean} cancel true to cancel the query
13244 * @param {Object} e The query event object
13246 'beforequery': true,
13249 * Fires when the 'add' icon is pressed (add a listener to enable add button)
13250 * @param {Roo.bootstrap.ComboBox} combo This combo box
13255 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13256 * @param {Roo.bootstrap.ComboBox} combo This combo box
13257 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13262 * Fires when the remove value from the combobox array
13263 * @param {Roo.bootstrap.ComboBox} combo This combo box
13267 * @event afterremove
13268 * Fires when the remove value from the combobox array
13269 * @param {Roo.bootstrap.ComboBox} combo This combo box
13271 'afterremove' : true,
13273 * @event specialfilter
13274 * Fires when specialfilter
13275 * @param {Roo.bootstrap.ComboBox} combo This combo box
13277 'specialfilter' : true,
13280 * Fires when tick the element
13281 * @param {Roo.bootstrap.ComboBox} combo This combo box
13285 * @event touchviewdisplay
13286 * Fires when touch view require special display (default is using displayField)
13287 * @param {Roo.bootstrap.ComboBox} combo This combo box
13288 * @param {Object} cfg set html .
13290 'touchviewdisplay' : true
13295 this.tickItems = [];
13297 this.selectedIndex = -1;
13298 if(this.mode == 'local'){
13299 if(config.queryDelay === undefined){
13300 this.queryDelay = 10;
13302 if(config.minChars === undefined){
13308 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13311 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13312 * rendering into an Roo.Editor, defaults to false)
13315 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13316 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13319 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13322 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13323 * the dropdown list (defaults to undefined, with no header element)
13327 * @cfg {String/Roo.Template} tpl The template to use to render the output
13331 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13333 listWidth: undefined,
13335 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13336 * mode = 'remote' or 'text' if mode = 'local')
13338 displayField: undefined,
13341 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13342 * mode = 'remote' or 'value' if mode = 'local').
13343 * Note: use of a valueField requires the user make a selection
13344 * in order for a value to be mapped.
13346 valueField: undefined,
13348 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13353 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13354 * field's data value (defaults to the underlying DOM element's name)
13356 hiddenName: undefined,
13358 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13362 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13364 selectedClass: 'active',
13367 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13371 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13372 * anchor positions (defaults to 'tl-bl')
13374 listAlign: 'tl-bl?',
13376 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13380 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
13381 * query specified by the allQuery config option (defaults to 'query')
13383 triggerAction: 'query',
13385 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13386 * (defaults to 4, does not apply if editable = false)
13390 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13391 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13395 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13396 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13400 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13401 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13405 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13406 * when editable = true (defaults to false)
13408 selectOnFocus:false,
13410 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13412 queryParam: 'query',
13414 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13415 * when mode = 'remote' (defaults to 'Loading...')
13417 loadingText: 'Loading...',
13419 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13423 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13427 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13428 * traditional select (defaults to true)
13432 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13436 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13440 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13441 * listWidth has a higher value)
13445 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13446 * allow the user to set arbitrary text into the field (defaults to false)
13448 forceSelection:false,
13450 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13451 * if typeAhead = true (defaults to 250)
13453 typeAheadDelay : 250,
13455 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13456 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13458 valueNotFoundText : undefined,
13460 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13462 blockFocus : false,
13465 * @cfg {Boolean} disableClear Disable showing of clear button.
13467 disableClear : false,
13469 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13471 alwaysQuery : false,
13474 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13479 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13481 invalidClass : "has-warning",
13484 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13486 validClass : "has-success",
13489 * @cfg {Boolean} specialFilter (true|false) special filter default false
13491 specialFilter : false,
13494 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13496 mobileTouchView : true,
13499 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13501 useNativeIOS : false,
13504 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13506 mobile_restrict_height : false,
13508 ios_options : false,
13520 btnPosition : 'right',
13521 triggerList : true,
13522 showToggleBtn : true,
13524 emptyResultText: 'Empty',
13525 triggerText : 'Select',
13528 // element that contains real text value.. (when hidden is used..)
13530 getAutoCreate : function()
13535 * Render classic select for iso
13538 if(Roo.isIOS && this.useNativeIOS){
13539 cfg = this.getAutoCreateNativeIOS();
13547 if(Roo.isTouch && this.mobileTouchView){
13548 cfg = this.getAutoCreateTouchView();
13555 if(!this.tickable){
13556 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13561 * ComboBox with tickable selections
13564 var align = this.labelAlign || this.parentLabelAlign();
13567 cls : 'form-group roo-combobox-tickable' //input-group
13570 var btn_text_select = '';
13571 var btn_text_done = '';
13572 var btn_text_cancel = '';
13574 if (this.btn_text_show) {
13575 btn_text_select = 'Select';
13576 btn_text_done = 'Done';
13577 btn_text_cancel = 'Cancel';
13582 cls : 'tickable-buttons',
13587 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13588 //html : this.triggerText
13589 html: btn_text_select
13595 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13597 html: btn_text_done
13603 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13605 html: btn_text_cancel
13611 buttons.cn.unshift({
13613 cls: 'roo-select2-search-field-input'
13619 Roo.each(buttons.cn, function(c){
13621 c.cls += ' btn-' + _this.size;
13624 if (_this.disabled) {
13631 style : 'display: contents',
13636 cls: 'form-hidden-field'
13640 cls: 'roo-select2-choices',
13644 cls: 'roo-select2-search-field',
13655 cls: 'roo-select2-container input-group roo-select2-container-multi',
13661 // cls: 'typeahead typeahead-long dropdown-menu',
13662 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13667 if(this.hasFeedback && !this.allowBlank){
13671 cls: 'glyphicon form-control-feedback'
13674 combobox.cn.push(feedback);
13679 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13680 tooltip : 'This field is required'
13682 if (Roo.bootstrap.version == 4) {
13685 style : 'display:none'
13688 if (align ==='left' && this.fieldLabel.length) {
13690 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13697 cls : 'control-label col-form-label',
13698 html : this.fieldLabel
13710 var labelCfg = cfg.cn[1];
13711 var contentCfg = cfg.cn[2];
13714 if(this.indicatorpos == 'right'){
13720 cls : 'control-label col-form-label',
13724 html : this.fieldLabel
13740 labelCfg = cfg.cn[0];
13741 contentCfg = cfg.cn[1];
13745 if(this.labelWidth > 12){
13746 labelCfg.style = "width: " + this.labelWidth + 'px';
13749 if(this.labelWidth < 13 && this.labelmd == 0){
13750 this.labelmd = this.labelWidth;
13753 if(this.labellg > 0){
13754 labelCfg.cls += ' col-lg-' + this.labellg;
13755 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13758 if(this.labelmd > 0){
13759 labelCfg.cls += ' col-md-' + this.labelmd;
13760 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13763 if(this.labelsm > 0){
13764 labelCfg.cls += ' col-sm-' + this.labelsm;
13765 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13768 if(this.labelxs > 0){
13769 labelCfg.cls += ' col-xs-' + this.labelxs;
13770 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13774 } else if ( this.fieldLabel.length) {
13775 // Roo.log(" label");
13780 //cls : 'input-group-addon',
13781 html : this.fieldLabel
13786 if(this.indicatorpos == 'right'){
13790 //cls : 'input-group-addon',
13791 html : this.fieldLabel
13801 // Roo.log(" no label && no align");
13808 ['xs','sm','md','lg'].map(function(size){
13809 if (settings[size]) {
13810 cfg.cls += ' col-' + size + '-' + settings[size];
13818 _initEventsCalled : false,
13821 initEvents: function()
13823 if (this._initEventsCalled) { // as we call render... prevent looping...
13826 this._initEventsCalled = true;
13829 throw "can not find store for combo";
13832 this.indicator = this.indicatorEl();
13834 this.store = Roo.factory(this.store, Roo.data);
13835 this.store.parent = this;
13837 // if we are building from html. then this element is so complex, that we can not really
13838 // use the rendered HTML.
13839 // so we have to trash and replace the previous code.
13840 if (Roo.XComponent.build_from_html) {
13841 // remove this element....
13842 var e = this.el.dom, k=0;
13843 while (e ) { e = e.previousSibling; ++k;}
13848 this.rendered = false;
13850 this.render(this.parent().getChildContainer(true), k);
13853 if(Roo.isIOS && this.useNativeIOS){
13854 this.initIOSView();
13862 if(Roo.isTouch && this.mobileTouchView){
13863 this.initTouchView();
13868 this.initTickableEvents();
13872 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13874 if(this.hiddenName){
13876 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13878 this.hiddenField.dom.value =
13879 this.hiddenValue !== undefined ? this.hiddenValue :
13880 this.value !== undefined ? this.value : '';
13882 // prevent input submission
13883 this.el.dom.removeAttribute('name');
13884 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13889 // this.el.dom.setAttribute('autocomplete', 'off');
13892 var cls = 'x-combo-list';
13894 //this.list = new Roo.Layer({
13895 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13901 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13902 _this.list.setWidth(lw);
13905 this.list.on('mouseover', this.onViewOver, this);
13906 this.list.on('mousemove', this.onViewMove, this);
13907 this.list.on('scroll', this.onViewScroll, this);
13910 this.list.swallowEvent('mousewheel');
13911 this.assetHeight = 0;
13914 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13915 this.assetHeight += this.header.getHeight();
13918 this.innerList = this.list.createChild({cls:cls+'-inner'});
13919 this.innerList.on('mouseover', this.onViewOver, this);
13920 this.innerList.on('mousemove', this.onViewMove, this);
13921 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13923 if(this.allowBlank && !this.pageSize && !this.disableClear){
13924 this.footer = this.list.createChild({cls:cls+'-ft'});
13925 this.pageTb = new Roo.Toolbar(this.footer);
13929 this.footer = this.list.createChild({cls:cls+'-ft'});
13930 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13931 {pageSize: this.pageSize});
13935 if (this.pageTb && this.allowBlank && !this.disableClear) {
13937 this.pageTb.add(new Roo.Toolbar.Fill(), {
13938 cls: 'x-btn-icon x-btn-clear',
13940 handler: function()
13943 _this.clearValue();
13944 _this.onSelect(false, -1);
13949 this.assetHeight += this.footer.getHeight();
13954 this.tpl = Roo.bootstrap.version == 4 ?
13955 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
13956 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13959 this.view = new Roo.View(this.list, this.tpl, {
13960 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13962 //this.view.wrapEl.setDisplayed(false);
13963 this.view.on('click', this.onViewClick, this);
13966 this.store.on('beforeload', this.onBeforeLoad, this);
13967 this.store.on('load', this.onLoad, this);
13968 this.store.on('loadexception', this.onLoadException, this);
13970 if(this.resizable){
13971 this.resizer = new Roo.Resizable(this.list, {
13972 pinned:true, handles:'se'
13974 this.resizer.on('resize', function(r, w, h){
13975 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13976 this.listWidth = w;
13977 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13978 this.restrictHeight();
13980 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13983 if(!this.editable){
13984 this.editable = true;
13985 this.setEditable(false);
13990 if (typeof(this.events.add.listeners) != 'undefined') {
13992 this.addicon = this.wrap.createChild(
13993 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13995 this.addicon.on('click', function(e) {
13996 this.fireEvent('add', this);
13999 if (typeof(this.events.edit.listeners) != 'undefined') {
14001 this.editicon = this.wrap.createChild(
14002 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
14003 if (this.addicon) {
14004 this.editicon.setStyle('margin-left', '40px');
14006 this.editicon.on('click', function(e) {
14008 // we fire even if inothing is selected..
14009 this.fireEvent('edit', this, this.lastData );
14015 this.keyNav = new Roo.KeyNav(this.inputEl(), {
14016 "up" : function(e){
14017 this.inKeyMode = true;
14021 "down" : function(e){
14022 if(!this.isExpanded()){
14023 this.onTriggerClick();
14025 this.inKeyMode = true;
14030 "enter" : function(e){
14031 // this.onViewClick();
14035 if(this.fireEvent("specialkey", this, e)){
14036 this.onViewClick(false);
14042 "esc" : function(e){
14046 "tab" : function(e){
14049 if(this.fireEvent("specialkey", this, e)){
14050 this.onViewClick(false);
14058 doRelay : function(foo, bar, hname){
14059 if(hname == 'down' || this.scope.isExpanded()){
14060 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14069 this.queryDelay = Math.max(this.queryDelay || 10,
14070 this.mode == 'local' ? 10 : 250);
14073 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14075 if(this.typeAhead){
14076 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14078 if(this.editable !== false){
14079 this.inputEl().on("keyup", this.onKeyUp, this);
14081 if(this.forceSelection){
14082 this.inputEl().on('blur', this.doForce, this);
14086 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14087 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14091 initTickableEvents: function()
14095 if(this.hiddenName){
14097 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14099 this.hiddenField.dom.value =
14100 this.hiddenValue !== undefined ? this.hiddenValue :
14101 this.value !== undefined ? this.value : '';
14103 // prevent input submission
14104 this.el.dom.removeAttribute('name');
14105 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14110 // this.list = this.el.select('ul.dropdown-menu',true).first();
14112 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14113 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14114 if(this.triggerList){
14115 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14118 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14119 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14121 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14122 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14124 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14125 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14127 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14128 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14129 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14132 this.cancelBtn.hide();
14137 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14138 _this.list.setWidth(lw);
14141 this.list.on('mouseover', this.onViewOver, this);
14142 this.list.on('mousemove', this.onViewMove, this);
14144 this.list.on('scroll', this.onViewScroll, this);
14147 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
14148 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14151 this.view = new Roo.View(this.list, this.tpl, {
14156 selectedClass: this.selectedClass
14159 //this.view.wrapEl.setDisplayed(false);
14160 this.view.on('click', this.onViewClick, this);
14164 this.store.on('beforeload', this.onBeforeLoad, this);
14165 this.store.on('load', this.onLoad, this);
14166 this.store.on('loadexception', this.onLoadException, this);
14169 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14170 "up" : function(e){
14171 this.inKeyMode = true;
14175 "down" : function(e){
14176 this.inKeyMode = true;
14180 "enter" : function(e){
14181 if(this.fireEvent("specialkey", this, e)){
14182 this.onViewClick(false);
14188 "esc" : function(e){
14189 this.onTickableFooterButtonClick(e, false, false);
14192 "tab" : function(e){
14193 this.fireEvent("specialkey", this, e);
14195 this.onTickableFooterButtonClick(e, false, false);
14202 doRelay : function(e, fn, key){
14203 if(this.scope.isExpanded()){
14204 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14213 this.queryDelay = Math.max(this.queryDelay || 10,
14214 this.mode == 'local' ? 10 : 250);
14217 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14219 if(this.typeAhead){
14220 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14223 if(this.editable !== false){
14224 this.tickableInputEl().on("keyup", this.onKeyUp, this);
14227 this.indicator = this.indicatorEl();
14229 if(this.indicator){
14230 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14231 this.indicator.hide();
14236 onDestroy : function(){
14238 this.view.setStore(null);
14239 this.view.el.removeAllListeners();
14240 this.view.el.remove();
14241 this.view.purgeListeners();
14244 this.list.dom.innerHTML = '';
14248 this.store.un('beforeload', this.onBeforeLoad, this);
14249 this.store.un('load', this.onLoad, this);
14250 this.store.un('loadexception', this.onLoadException, this);
14252 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14256 fireKey : function(e){
14257 if(e.isNavKeyPress() && !this.list.isVisible()){
14258 this.fireEvent("specialkey", this, e);
14263 onResize: function(w, h){
14264 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14266 // if(typeof w != 'number'){
14267 // // we do not handle it!?!?
14270 // var tw = this.trigger.getWidth();
14271 // // tw += this.addicon ? this.addicon.getWidth() : 0;
14272 // // tw += this.editicon ? this.editicon.getWidth() : 0;
14274 // this.inputEl().setWidth( this.adjustWidth('input', x));
14276 // //this.trigger.setStyle('left', x+'px');
14278 // if(this.list && this.listWidth === undefined){
14279 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14280 // this.list.setWidth(lw);
14281 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14289 * Allow or prevent the user from directly editing the field text. If false is passed,
14290 * the user will only be able to select from the items defined in the dropdown list. This method
14291 * is the runtime equivalent of setting the 'editable' config option at config time.
14292 * @param {Boolean} value True to allow the user to directly edit the field text
14294 setEditable : function(value){
14295 if(value == this.editable){
14298 this.editable = value;
14300 this.inputEl().dom.setAttribute('readOnly', true);
14301 this.inputEl().on('mousedown', this.onTriggerClick, this);
14302 this.inputEl().addClass('x-combo-noedit');
14304 this.inputEl().dom.setAttribute('readOnly', false);
14305 this.inputEl().un('mousedown', this.onTriggerClick, this);
14306 this.inputEl().removeClass('x-combo-noedit');
14312 onBeforeLoad : function(combo,opts){
14313 if(!this.hasFocus){
14317 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14319 this.restrictHeight();
14320 this.selectedIndex = -1;
14324 onLoad : function(){
14326 this.hasQuery = false;
14328 if(!this.hasFocus){
14332 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14333 this.loading.hide();
14336 if(this.store.getCount() > 0){
14339 this.restrictHeight();
14340 if(this.lastQuery == this.allQuery){
14341 if(this.editable && !this.tickable){
14342 this.inputEl().dom.select();
14346 !this.selectByValue(this.value, true) &&
14349 !this.store.lastOptions ||
14350 typeof(this.store.lastOptions.add) == 'undefined' ||
14351 this.store.lastOptions.add != true
14354 this.select(0, true);
14357 if(this.autoFocus){
14360 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14361 this.taTask.delay(this.typeAheadDelay);
14365 this.onEmptyResults();
14371 onLoadException : function()
14373 this.hasQuery = false;
14375 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14376 this.loading.hide();
14379 if(this.tickable && this.editable){
14384 // only causes errors at present
14385 //Roo.log(this.store.reader.jsonData);
14386 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14388 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14394 onTypeAhead : function(){
14395 if(this.store.getCount() > 0){
14396 var r = this.store.getAt(0);
14397 var newValue = r.data[this.displayField];
14398 var len = newValue.length;
14399 var selStart = this.getRawValue().length;
14401 if(selStart != len){
14402 this.setRawValue(newValue);
14403 this.selectText(selStart, newValue.length);
14409 onSelect : function(record, index){
14411 if(this.fireEvent('beforeselect', this, record, index) !== false){
14413 this.setFromData(index > -1 ? record.data : false);
14416 this.fireEvent('select', this, record, index);
14421 * Returns the currently selected field value or empty string if no value is set.
14422 * @return {String} value The selected value
14424 getValue : function()
14426 if(Roo.isIOS && this.useNativeIOS){
14427 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14431 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14434 if(this.valueField){
14435 return typeof this.value != 'undefined' ? this.value : '';
14437 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14441 getRawValue : function()
14443 if(Roo.isIOS && this.useNativeIOS){
14444 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14447 var v = this.inputEl().getValue();
14453 * Clears any text/value currently set in the field
14455 clearValue : function(){
14457 if(this.hiddenField){
14458 this.hiddenField.dom.value = '';
14461 this.setRawValue('');
14462 this.lastSelectionText = '';
14463 this.lastData = false;
14465 var close = this.closeTriggerEl();
14476 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14477 * will be displayed in the field. If the value does not match the data value of an existing item,
14478 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14479 * Otherwise the field will be blank (although the value will still be set).
14480 * @param {String} value The value to match
14482 setValue : function(v)
14484 if(Roo.isIOS && this.useNativeIOS){
14485 this.setIOSValue(v);
14495 if(this.valueField){
14496 var r = this.findRecord(this.valueField, v);
14498 text = r.data[this.displayField];
14499 }else if(this.valueNotFoundText !== undefined){
14500 text = this.valueNotFoundText;
14503 this.lastSelectionText = text;
14504 if(this.hiddenField){
14505 this.hiddenField.dom.value = v;
14507 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14510 var close = this.closeTriggerEl();
14513 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14519 * @property {Object} the last set data for the element
14524 * Sets the value of the field based on a object which is related to the record format for the store.
14525 * @param {Object} value the value to set as. or false on reset?
14527 setFromData : function(o){
14534 var dv = ''; // display value
14535 var vv = ''; // value value..
14537 if (this.displayField) {
14538 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14540 // this is an error condition!!!
14541 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14544 if(this.valueField){
14545 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14548 var close = this.closeTriggerEl();
14551 if(dv.length || vv * 1 > 0){
14553 this.blockFocus=true;
14559 if(this.hiddenField){
14560 this.hiddenField.dom.value = vv;
14562 this.lastSelectionText = dv;
14563 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14567 // no hidden field.. - we store the value in 'value', but still display
14568 // display field!!!!
14569 this.lastSelectionText = dv;
14570 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14577 reset : function(){
14578 // overridden so that last data is reset..
14585 this.setValue(this.originalValue);
14586 //this.clearInvalid();
14587 this.lastData = false;
14589 this.view.clearSelections();
14595 findRecord : function(prop, value){
14597 if(this.store.getCount() > 0){
14598 this.store.each(function(r){
14599 if(r.data[prop] == value){
14609 getName: function()
14611 // returns hidden if it's set..
14612 if (!this.rendered) {return ''};
14613 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14617 onViewMove : function(e, t){
14618 this.inKeyMode = false;
14622 onViewOver : function(e, t){
14623 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14626 var item = this.view.findItemFromChild(t);
14629 var index = this.view.indexOf(item);
14630 this.select(index, false);
14635 onViewClick : function(view, doFocus, el, e)
14637 var index = this.view.getSelectedIndexes()[0];
14639 var r = this.store.getAt(index);
14643 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14650 Roo.each(this.tickItems, function(v,k){
14652 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14654 _this.tickItems.splice(k, 1);
14656 if(typeof(e) == 'undefined' && view == false){
14657 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14669 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14670 this.tickItems.push(r.data);
14673 if(typeof(e) == 'undefined' && view == false){
14674 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14681 this.onSelect(r, index);
14683 if(doFocus !== false && !this.blockFocus){
14684 this.inputEl().focus();
14689 restrictHeight : function(){
14690 //this.innerList.dom.style.height = '';
14691 //var inner = this.innerList.dom;
14692 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14693 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14694 //this.list.beginUpdate();
14695 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14696 this.list.alignTo(this.inputEl(), this.listAlign);
14697 this.list.alignTo(this.inputEl(), this.listAlign);
14698 //this.list.endUpdate();
14702 onEmptyResults : function(){
14704 if(this.tickable && this.editable){
14705 this.hasFocus = false;
14706 this.restrictHeight();
14714 * Returns true if the dropdown list is expanded, else false.
14716 isExpanded : function(){
14717 return this.list.isVisible();
14721 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14722 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14723 * @param {String} value The data value of the item to select
14724 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14725 * selected item if it is not currently in view (defaults to true)
14726 * @return {Boolean} True if the value matched an item in the list, else false
14728 selectByValue : function(v, scrollIntoView){
14729 if(v !== undefined && v !== null){
14730 var r = this.findRecord(this.valueField || this.displayField, v);
14732 this.select(this.store.indexOf(r), scrollIntoView);
14740 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14741 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14742 * @param {Number} index The zero-based index of the list item to select
14743 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14744 * selected item if it is not currently in view (defaults to true)
14746 select : function(index, scrollIntoView){
14747 this.selectedIndex = index;
14748 this.view.select(index);
14749 if(scrollIntoView !== false){
14750 var el = this.view.getNode(index);
14752 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14755 this.list.scrollChildIntoView(el, false);
14761 selectNext : function(){
14762 var ct = this.store.getCount();
14764 if(this.selectedIndex == -1){
14766 }else if(this.selectedIndex < ct-1){
14767 this.select(this.selectedIndex+1);
14773 selectPrev : function(){
14774 var ct = this.store.getCount();
14776 if(this.selectedIndex == -1){
14778 }else if(this.selectedIndex != 0){
14779 this.select(this.selectedIndex-1);
14785 onKeyUp : function(e){
14786 if(this.editable !== false && !e.isSpecialKey()){
14787 this.lastKey = e.getKey();
14788 this.dqTask.delay(this.queryDelay);
14793 validateBlur : function(){
14794 return !this.list || !this.list.isVisible();
14798 initQuery : function(){
14800 var v = this.getRawValue();
14802 if(this.tickable && this.editable){
14803 v = this.tickableInputEl().getValue();
14810 doForce : function(){
14811 if(this.inputEl().dom.value.length > 0){
14812 this.inputEl().dom.value =
14813 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14819 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14820 * query allowing the query action to be canceled if needed.
14821 * @param {String} query The SQL query to execute
14822 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14823 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14824 * saved in the current store (defaults to false)
14826 doQuery : function(q, forceAll){
14828 if(q === undefined || q === null){
14833 forceAll: forceAll,
14837 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14842 forceAll = qe.forceAll;
14843 if(forceAll === true || (q.length >= this.minChars)){
14845 this.hasQuery = true;
14847 if(this.lastQuery != q || this.alwaysQuery){
14848 this.lastQuery = q;
14849 if(this.mode == 'local'){
14850 this.selectedIndex = -1;
14852 this.store.clearFilter();
14855 if(this.specialFilter){
14856 this.fireEvent('specialfilter', this);
14861 this.store.filter(this.displayField, q);
14864 this.store.fireEvent("datachanged", this.store);
14871 this.store.baseParams[this.queryParam] = q;
14873 var options = {params : this.getParams(q)};
14876 options.add = true;
14877 options.params.start = this.page * this.pageSize;
14880 this.store.load(options);
14883 * this code will make the page width larger, at the beginning, the list not align correctly,
14884 * we should expand the list on onLoad
14885 * so command out it
14890 this.selectedIndex = -1;
14895 this.loadNext = false;
14899 getParams : function(q){
14901 //p[this.queryParam] = q;
14905 p.limit = this.pageSize;
14911 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14913 collapse : function(){
14914 if(!this.isExpanded()){
14920 this.hasFocus = false;
14924 this.cancelBtn.hide();
14925 this.trigger.show();
14928 this.tickableInputEl().dom.value = '';
14929 this.tickableInputEl().blur();
14934 Roo.get(document).un('mousedown', this.collapseIf, this);
14935 Roo.get(document).un('mousewheel', this.collapseIf, this);
14936 if (!this.editable) {
14937 Roo.get(document).un('keydown', this.listKeyPress, this);
14939 this.fireEvent('collapse', this);
14945 collapseIf : function(e){
14946 var in_combo = e.within(this.el);
14947 var in_list = e.within(this.list);
14948 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14950 if (in_combo || in_list || is_list) {
14951 //e.stopPropagation();
14956 this.onTickableFooterButtonClick(e, false, false);
14964 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14966 expand : function(){
14968 if(this.isExpanded() || !this.hasFocus){
14972 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14973 this.list.setWidth(lw);
14979 this.restrictHeight();
14983 this.tickItems = Roo.apply([], this.item);
14986 this.cancelBtn.show();
14987 this.trigger.hide();
14990 this.tickableInputEl().focus();
14995 Roo.get(document).on('mousedown', this.collapseIf, this);
14996 Roo.get(document).on('mousewheel', this.collapseIf, this);
14997 if (!this.editable) {
14998 Roo.get(document).on('keydown', this.listKeyPress, this);
15001 this.fireEvent('expand', this);
15005 // Implements the default empty TriggerField.onTriggerClick function
15006 onTriggerClick : function(e)
15008 Roo.log('trigger click');
15010 if(this.disabled || !this.triggerList){
15015 this.loadNext = false;
15017 if(this.isExpanded()){
15019 if (!this.blockFocus) {
15020 this.inputEl().focus();
15024 this.hasFocus = true;
15025 if(this.triggerAction == 'all') {
15026 this.doQuery(this.allQuery, true);
15028 this.doQuery(this.getRawValue());
15030 if (!this.blockFocus) {
15031 this.inputEl().focus();
15036 onTickableTriggerClick : function(e)
15043 this.loadNext = false;
15044 this.hasFocus = true;
15046 if(this.triggerAction == 'all') {
15047 this.doQuery(this.allQuery, true);
15049 this.doQuery(this.getRawValue());
15053 onSearchFieldClick : function(e)
15055 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
15056 this.onTickableFooterButtonClick(e, false, false);
15060 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
15065 this.loadNext = false;
15066 this.hasFocus = true;
15068 if(this.triggerAction == 'all') {
15069 this.doQuery(this.allQuery, true);
15071 this.doQuery(this.getRawValue());
15075 listKeyPress : function(e)
15077 //Roo.log('listkeypress');
15078 // scroll to first matching element based on key pres..
15079 if (e.isSpecialKey()) {
15082 var k = String.fromCharCode(e.getKey()).toUpperCase();
15085 var csel = this.view.getSelectedNodes();
15086 var cselitem = false;
15088 var ix = this.view.indexOf(csel[0]);
15089 cselitem = this.store.getAt(ix);
15090 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15096 this.store.each(function(v) {
15098 // start at existing selection.
15099 if (cselitem.id == v.id) {
15105 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15106 match = this.store.indexOf(v);
15112 if (match === false) {
15113 return true; // no more action?
15116 this.view.select(match);
15117 var sn = Roo.get(this.view.getSelectedNodes()[0]);
15118 sn.scrollIntoView(sn.dom.parentNode, false);
15121 onViewScroll : function(e, t){
15123 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){
15127 this.hasQuery = true;
15129 this.loading = this.list.select('.loading', true).first();
15131 if(this.loading === null){
15132 this.list.createChild({
15134 cls: 'loading roo-select2-more-results roo-select2-active',
15135 html: 'Loading more results...'
15138 this.loading = this.list.select('.loading', true).first();
15140 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15142 this.loading.hide();
15145 this.loading.show();
15150 this.loadNext = true;
15152 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15157 addItem : function(o)
15159 var dv = ''; // display value
15161 if (this.displayField) {
15162 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15164 // this is an error condition!!!
15165 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
15172 var choice = this.choices.createChild({
15174 cls: 'roo-select2-search-choice',
15183 cls: 'roo-select2-search-choice-close fa fa-times',
15188 }, this.searchField);
15190 var close = choice.select('a.roo-select2-search-choice-close', true).first();
15192 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15200 this.inputEl().dom.value = '';
15205 onRemoveItem : function(e, _self, o)
15207 e.preventDefault();
15209 this.lastItem = Roo.apply([], this.item);
15211 var index = this.item.indexOf(o.data) * 1;
15214 Roo.log('not this item?!');
15218 this.item.splice(index, 1);
15223 this.fireEvent('remove', this, e);
15229 syncValue : function()
15231 if(!this.item.length){
15238 Roo.each(this.item, function(i){
15239 if(_this.valueField){
15240 value.push(i[_this.valueField]);
15247 this.value = value.join(',');
15249 if(this.hiddenField){
15250 this.hiddenField.dom.value = this.value;
15253 this.store.fireEvent("datachanged", this.store);
15258 clearItem : function()
15260 if(!this.multiple){
15266 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15274 if(this.tickable && !Roo.isTouch){
15275 this.view.refresh();
15279 inputEl: function ()
15281 if(Roo.isIOS && this.useNativeIOS){
15282 return this.el.select('select.roo-ios-select', true).first();
15285 if(Roo.isTouch && this.mobileTouchView){
15286 return this.el.select('input.form-control',true).first();
15290 return this.searchField;
15293 return this.el.select('input.form-control',true).first();
15296 onTickableFooterButtonClick : function(e, btn, el)
15298 e.preventDefault();
15300 this.lastItem = Roo.apply([], this.item);
15302 if(btn && btn.name == 'cancel'){
15303 this.tickItems = Roo.apply([], this.item);
15312 Roo.each(this.tickItems, function(o){
15320 validate : function()
15322 if(this.getVisibilityEl().hasClass('hidden')){
15326 var v = this.getRawValue();
15329 v = this.getValue();
15332 if(this.disabled || this.allowBlank || v.length){
15337 this.markInvalid();
15341 tickableInputEl : function()
15343 if(!this.tickable || !this.editable){
15344 return this.inputEl();
15347 return this.inputEl().select('.roo-select2-search-field-input', true).first();
15351 getAutoCreateTouchView : function()
15356 cls: 'form-group' //input-group
15362 type : this.inputType,
15363 cls : 'form-control x-combo-noedit',
15364 autocomplete: 'new-password',
15365 placeholder : this.placeholder || '',
15370 input.name = this.name;
15374 input.cls += ' input-' + this.size;
15377 if (this.disabled) {
15378 input.disabled = true;
15389 inputblock.cls += ' input-group';
15391 inputblock.cn.unshift({
15393 cls : 'input-group-addon input-group-prepend input-group-text',
15398 if(this.removable && !this.multiple){
15399 inputblock.cls += ' roo-removable';
15401 inputblock.cn.push({
15404 cls : 'roo-combo-removable-btn close'
15408 if(this.hasFeedback && !this.allowBlank){
15410 inputblock.cls += ' has-feedback';
15412 inputblock.cn.push({
15414 cls: 'glyphicon form-control-feedback'
15421 inputblock.cls += (this.before) ? '' : ' input-group';
15423 inputblock.cn.push({
15425 cls : 'input-group-addon input-group-append input-group-text',
15431 var ibwrap = inputblock;
15436 cls: 'roo-select2-choices',
15440 cls: 'roo-select2-search-field',
15453 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15458 cls: 'form-hidden-field'
15464 if(!this.multiple && this.showToggleBtn){
15470 if (this.caret != false) {
15473 cls: 'fa fa-' + this.caret
15480 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15482 Roo.bootstrap.version == 3 ? caret : '',
15485 cls: 'combobox-clear',
15499 combobox.cls += ' roo-select2-container-multi';
15502 var align = this.labelAlign || this.parentLabelAlign();
15504 if (align ==='left' && this.fieldLabel.length) {
15509 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15510 tooltip : 'This field is required'
15514 cls : 'control-label col-form-label',
15515 html : this.fieldLabel
15526 var labelCfg = cfg.cn[1];
15527 var contentCfg = cfg.cn[2];
15530 if(this.indicatorpos == 'right'){
15535 cls : 'control-label col-form-label',
15539 html : this.fieldLabel
15543 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15544 tooltip : 'This field is required'
15557 labelCfg = cfg.cn[0];
15558 contentCfg = cfg.cn[1];
15563 if(this.labelWidth > 12){
15564 labelCfg.style = "width: " + this.labelWidth + 'px';
15567 if(this.labelWidth < 13 && this.labelmd == 0){
15568 this.labelmd = this.labelWidth;
15571 if(this.labellg > 0){
15572 labelCfg.cls += ' col-lg-' + this.labellg;
15573 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15576 if(this.labelmd > 0){
15577 labelCfg.cls += ' col-md-' + this.labelmd;
15578 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15581 if(this.labelsm > 0){
15582 labelCfg.cls += ' col-sm-' + this.labelsm;
15583 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15586 if(this.labelxs > 0){
15587 labelCfg.cls += ' col-xs-' + this.labelxs;
15588 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15592 } else if ( this.fieldLabel.length) {
15596 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15597 tooltip : 'This field is required'
15601 cls : 'control-label',
15602 html : this.fieldLabel
15613 if(this.indicatorpos == 'right'){
15617 cls : 'control-label',
15618 html : this.fieldLabel,
15622 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15623 tooltip : 'This field is required'
15640 var settings = this;
15642 ['xs','sm','md','lg'].map(function(size){
15643 if (settings[size]) {
15644 cfg.cls += ' col-' + size + '-' + settings[size];
15651 initTouchView : function()
15653 this.renderTouchView();
15655 this.touchViewEl.on('scroll', function(){
15656 this.el.dom.scrollTop = 0;
15659 this.originalValue = this.getValue();
15661 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15663 this.inputEl().on("click", this.showTouchView, this);
15664 if (this.triggerEl) {
15665 this.triggerEl.on("click", this.showTouchView, this);
15669 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15670 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15672 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15674 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15675 this.store.on('load', this.onTouchViewLoad, this);
15676 this.store.on('loadexception', this.onTouchViewLoadException, this);
15678 if(this.hiddenName){
15680 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15682 this.hiddenField.dom.value =
15683 this.hiddenValue !== undefined ? this.hiddenValue :
15684 this.value !== undefined ? this.value : '';
15686 this.el.dom.removeAttribute('name');
15687 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15691 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15692 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15695 if(this.removable && !this.multiple){
15696 var close = this.closeTriggerEl();
15698 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15699 close.on('click', this.removeBtnClick, this, close);
15703 * fix the bug in Safari iOS8
15705 this.inputEl().on("focus", function(e){
15706 document.activeElement.blur();
15709 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15716 renderTouchView : function()
15718 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15719 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15721 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15722 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15724 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15725 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15726 this.touchViewBodyEl.setStyle('overflow', 'auto');
15728 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15729 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15731 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15732 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15736 showTouchView : function()
15742 this.touchViewHeaderEl.hide();
15744 if(this.modalTitle.length){
15745 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15746 this.touchViewHeaderEl.show();
15749 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15750 this.touchViewEl.show();
15752 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15754 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15755 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15757 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15759 if(this.modalTitle.length){
15760 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15763 this.touchViewBodyEl.setHeight(bodyHeight);
15767 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15769 this.touchViewEl.addClass('in');
15772 if(this._touchViewMask){
15773 Roo.get(document.body).addClass("x-body-masked");
15774 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15775 this._touchViewMask.setStyle('z-index', 10000);
15776 this._touchViewMask.addClass('show');
15779 this.doTouchViewQuery();
15783 hideTouchView : function()
15785 this.touchViewEl.removeClass('in');
15789 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15791 this.touchViewEl.setStyle('display', 'none');
15794 if(this._touchViewMask){
15795 this._touchViewMask.removeClass('show');
15796 Roo.get(document.body).removeClass("x-body-masked");
15800 setTouchViewValue : function()
15807 Roo.each(this.tickItems, function(o){
15812 this.hideTouchView();
15815 doTouchViewQuery : function()
15824 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15828 if(!this.alwaysQuery || this.mode == 'local'){
15829 this.onTouchViewLoad();
15836 onTouchViewBeforeLoad : function(combo,opts)
15842 onTouchViewLoad : function()
15844 if(this.store.getCount() < 1){
15845 this.onTouchViewEmptyResults();
15849 this.clearTouchView();
15851 var rawValue = this.getRawValue();
15853 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15855 this.tickItems = [];
15857 this.store.data.each(function(d, rowIndex){
15858 var row = this.touchViewListGroup.createChild(template);
15860 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15861 row.addClass(d.data.cls);
15864 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15867 html : d.data[this.displayField]
15870 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15871 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15874 row.removeClass('selected');
15875 if(!this.multiple && this.valueField &&
15876 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15879 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15880 row.addClass('selected');
15883 if(this.multiple && this.valueField &&
15884 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15888 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15889 this.tickItems.push(d.data);
15892 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15896 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15898 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15900 if(this.modalTitle.length){
15901 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15904 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15906 if(this.mobile_restrict_height && listHeight < bodyHeight){
15907 this.touchViewBodyEl.setHeight(listHeight);
15912 if(firstChecked && listHeight > bodyHeight){
15913 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15918 onTouchViewLoadException : function()
15920 this.hideTouchView();
15923 onTouchViewEmptyResults : function()
15925 this.clearTouchView();
15927 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15929 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15933 clearTouchView : function()
15935 this.touchViewListGroup.dom.innerHTML = '';
15938 onTouchViewClick : function(e, el, o)
15940 e.preventDefault();
15943 var rowIndex = o.rowIndex;
15945 var r = this.store.getAt(rowIndex);
15947 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15949 if(!this.multiple){
15950 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15951 c.dom.removeAttribute('checked');
15954 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15956 this.setFromData(r.data);
15958 var close = this.closeTriggerEl();
15964 this.hideTouchView();
15966 this.fireEvent('select', this, r, rowIndex);
15971 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15972 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15973 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15977 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15978 this.addItem(r.data);
15979 this.tickItems.push(r.data);
15983 getAutoCreateNativeIOS : function()
15986 cls: 'form-group' //input-group,
15991 cls : 'roo-ios-select'
15995 combobox.name = this.name;
15998 if (this.disabled) {
15999 combobox.disabled = true;
16002 var settings = this;
16004 ['xs','sm','md','lg'].map(function(size){
16005 if (settings[size]) {
16006 cfg.cls += ' col-' + size + '-' + settings[size];
16016 initIOSView : function()
16018 this.store.on('load', this.onIOSViewLoad, this);
16023 onIOSViewLoad : function()
16025 if(this.store.getCount() < 1){
16029 this.clearIOSView();
16031 if(this.allowBlank) {
16033 var default_text = '-- SELECT --';
16035 if(this.placeholder.length){
16036 default_text = this.placeholder;
16039 if(this.emptyTitle.length){
16040 default_text += ' - ' + this.emptyTitle + ' -';
16043 var opt = this.inputEl().createChild({
16046 html : default_text
16050 o[this.valueField] = 0;
16051 o[this.displayField] = default_text;
16053 this.ios_options.push({
16060 this.store.data.each(function(d, rowIndex){
16064 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
16065 html = d.data[this.displayField];
16070 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16071 value = d.data[this.valueField];
16080 if(this.value == d.data[this.valueField]){
16081 option['selected'] = true;
16084 var opt = this.inputEl().createChild(option);
16086 this.ios_options.push({
16093 this.inputEl().on('change', function(){
16094 this.fireEvent('select', this);
16099 clearIOSView: function()
16101 this.inputEl().dom.innerHTML = '';
16103 this.ios_options = [];
16106 setIOSValue: function(v)
16110 if(!this.ios_options){
16114 Roo.each(this.ios_options, function(opts){
16116 opts.el.dom.removeAttribute('selected');
16118 if(opts.data[this.valueField] != v){
16122 opts.el.dom.setAttribute('selected', true);
16128 * @cfg {Boolean} grow
16132 * @cfg {Number} growMin
16136 * @cfg {Number} growMax
16145 Roo.apply(Roo.bootstrap.ComboBox, {
16149 cls: 'modal-header',
16171 cls: 'list-group-item',
16175 cls: 'roo-combobox-list-group-item-value'
16179 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16193 listItemCheckbox : {
16195 cls: 'list-group-item',
16199 cls: 'roo-combobox-list-group-item-value'
16203 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16219 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16224 cls: 'modal-footer',
16232 cls: 'col-xs-6 text-left',
16235 cls: 'btn btn-danger roo-touch-view-cancel',
16241 cls: 'col-xs-6 text-right',
16244 cls: 'btn btn-success roo-touch-view-ok',
16255 Roo.apply(Roo.bootstrap.ComboBox, {
16257 touchViewTemplate : {
16259 cls: 'modal fade roo-combobox-touch-view',
16263 cls: 'modal-dialog',
16264 style : 'position:fixed', // we have to fix position....
16268 cls: 'modal-content',
16270 Roo.bootstrap.ComboBox.header,
16271 Roo.bootstrap.ComboBox.body,
16272 Roo.bootstrap.ComboBox.footer
16281 * Ext JS Library 1.1.1
16282 * Copyright(c) 2006-2007, Ext JS, LLC.
16284 * Originally Released Under LGPL - original licence link has changed is not relivant.
16287 * <script type="text/javascript">
16292 * @extends Roo.util.Observable
16293 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
16294 * This class also supports single and multi selection modes. <br>
16295 * Create a data model bound view:
16297 var store = new Roo.data.Store(...);
16299 var view = new Roo.View({
16301 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
16303 singleSelect: true,
16304 selectedClass: "ydataview-selected",
16308 // listen for node click?
16309 view.on("click", function(vw, index, node, e){
16310 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16314 dataModel.load("foobar.xml");
16316 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16318 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16319 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16321 * Note: old style constructor is still suported (container, template, config)
16324 * Create a new View
16325 * @param {Object} config The config object
16328 Roo.View = function(config, depreciated_tpl, depreciated_config){
16330 this.parent = false;
16332 if (typeof(depreciated_tpl) == 'undefined') {
16333 // new way.. - universal constructor.
16334 Roo.apply(this, config);
16335 this.el = Roo.get(this.el);
16338 this.el = Roo.get(config);
16339 this.tpl = depreciated_tpl;
16340 Roo.apply(this, depreciated_config);
16342 this.wrapEl = this.el.wrap().wrap();
16343 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16346 if(typeof(this.tpl) == "string"){
16347 this.tpl = new Roo.Template(this.tpl);
16349 // support xtype ctors..
16350 this.tpl = new Roo.factory(this.tpl, Roo);
16354 this.tpl.compile();
16359 * @event beforeclick
16360 * Fires before a click is processed. Returns false to cancel the default action.
16361 * @param {Roo.View} this
16362 * @param {Number} index The index of the target node
16363 * @param {HTMLElement} node The target node
16364 * @param {Roo.EventObject} e The raw event object
16366 "beforeclick" : true,
16369 * Fires when a template node is clicked.
16370 * @param {Roo.View} this
16371 * @param {Number} index The index of the target node
16372 * @param {HTMLElement} node The target node
16373 * @param {Roo.EventObject} e The raw event object
16378 * Fires when a template node is double clicked.
16379 * @param {Roo.View} this
16380 * @param {Number} index The index of the target node
16381 * @param {HTMLElement} node The target node
16382 * @param {Roo.EventObject} e The raw event object
16386 * @event contextmenu
16387 * Fires when a template node is right clicked.
16388 * @param {Roo.View} this
16389 * @param {Number} index The index of the target node
16390 * @param {HTMLElement} node The target node
16391 * @param {Roo.EventObject} e The raw event object
16393 "contextmenu" : true,
16395 * @event selectionchange
16396 * Fires when the selected nodes change.
16397 * @param {Roo.View} this
16398 * @param {Array} selections Array of the selected nodes
16400 "selectionchange" : true,
16403 * @event beforeselect
16404 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16405 * @param {Roo.View} this
16406 * @param {HTMLElement} node The node to be selected
16407 * @param {Array} selections Array of currently selected nodes
16409 "beforeselect" : true,
16411 * @event preparedata
16412 * Fires on every row to render, to allow you to change the data.
16413 * @param {Roo.View} this
16414 * @param {Object} data to be rendered (change this)
16416 "preparedata" : true
16424 "click": this.onClick,
16425 "dblclick": this.onDblClick,
16426 "contextmenu": this.onContextMenu,
16430 this.selections = [];
16432 this.cmp = new Roo.CompositeElementLite([]);
16434 this.store = Roo.factory(this.store, Roo.data);
16435 this.setStore(this.store, true);
16438 if ( this.footer && this.footer.xtype) {
16440 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16442 this.footer.dataSource = this.store;
16443 this.footer.container = fctr;
16444 this.footer = Roo.factory(this.footer, Roo);
16445 fctr.insertFirst(this.el);
16447 // this is a bit insane - as the paging toolbar seems to detach the el..
16448 // dom.parentNode.parentNode.parentNode
16449 // they get detached?
16453 Roo.View.superclass.constructor.call(this);
16458 Roo.extend(Roo.View, Roo.util.Observable, {
16461 * @cfg {Roo.data.Store} store Data store to load data from.
16466 * @cfg {String|Roo.Element} el The container element.
16471 * @cfg {String|Roo.Template} tpl The template used by this View
16475 * @cfg {String} dataName the named area of the template to use as the data area
16476 * Works with domtemplates roo-name="name"
16480 * @cfg {String} selectedClass The css class to add to selected nodes
16482 selectedClass : "x-view-selected",
16484 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16489 * @cfg {String} text to display on mask (default Loading)
16493 * @cfg {Boolean} multiSelect Allow multiple selection
16495 multiSelect : false,
16497 * @cfg {Boolean} singleSelect Allow single selection
16499 singleSelect: false,
16502 * @cfg {Boolean} toggleSelect - selecting
16504 toggleSelect : false,
16507 * @cfg {Boolean} tickable - selecting
16512 * Returns the element this view is bound to.
16513 * @return {Roo.Element}
16515 getEl : function(){
16516 return this.wrapEl;
16522 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16524 refresh : function(){
16525 //Roo.log('refresh');
16528 // if we are using something like 'domtemplate', then
16529 // the what gets used is:
16530 // t.applySubtemplate(NAME, data, wrapping data..)
16531 // the outer template then get' applied with
16532 // the store 'extra data'
16533 // and the body get's added to the
16534 // roo-name="data" node?
16535 // <span class='roo-tpl-{name}'></span> ?????
16539 this.clearSelections();
16540 this.el.update("");
16542 var records = this.store.getRange();
16543 if(records.length < 1) {
16545 // is this valid?? = should it render a template??
16547 this.el.update(this.emptyText);
16551 if (this.dataName) {
16552 this.el.update(t.apply(this.store.meta)); //????
16553 el = this.el.child('.roo-tpl-' + this.dataName);
16556 for(var i = 0, len = records.length; i < len; i++){
16557 var data = this.prepareData(records[i].data, i, records[i]);
16558 this.fireEvent("preparedata", this, data, i, records[i]);
16560 var d = Roo.apply({}, data);
16563 Roo.apply(d, {'roo-id' : Roo.id()});
16567 Roo.each(this.parent.item, function(item){
16568 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16571 Roo.apply(d, {'roo-data-checked' : 'checked'});
16575 html[html.length] = Roo.util.Format.trim(
16577 t.applySubtemplate(this.dataName, d, this.store.meta) :
16584 el.update(html.join(""));
16585 this.nodes = el.dom.childNodes;
16586 this.updateIndexes(0);
16591 * Function to override to reformat the data that is sent to
16592 * the template for each node.
16593 * DEPRICATED - use the preparedata event handler.
16594 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16595 * a JSON object for an UpdateManager bound view).
16597 prepareData : function(data, index, record)
16599 this.fireEvent("preparedata", this, data, index, record);
16603 onUpdate : function(ds, record){
16604 // Roo.log('on update');
16605 this.clearSelections();
16606 var index = this.store.indexOf(record);
16607 var n = this.nodes[index];
16608 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16609 n.parentNode.removeChild(n);
16610 this.updateIndexes(index, index);
16616 onAdd : function(ds, records, index)
16618 //Roo.log(['on Add', ds, records, index] );
16619 this.clearSelections();
16620 if(this.nodes.length == 0){
16624 var n = this.nodes[index];
16625 for(var i = 0, len = records.length; i < len; i++){
16626 var d = this.prepareData(records[i].data, i, records[i]);
16628 this.tpl.insertBefore(n, d);
16631 this.tpl.append(this.el, d);
16634 this.updateIndexes(index);
16637 onRemove : function(ds, record, index){
16638 // Roo.log('onRemove');
16639 this.clearSelections();
16640 var el = this.dataName ?
16641 this.el.child('.roo-tpl-' + this.dataName) :
16644 el.dom.removeChild(this.nodes[index]);
16645 this.updateIndexes(index);
16649 * Refresh an individual node.
16650 * @param {Number} index
16652 refreshNode : function(index){
16653 this.onUpdate(this.store, this.store.getAt(index));
16656 updateIndexes : function(startIndex, endIndex){
16657 var ns = this.nodes;
16658 startIndex = startIndex || 0;
16659 endIndex = endIndex || ns.length - 1;
16660 for(var i = startIndex; i <= endIndex; i++){
16661 ns[i].nodeIndex = i;
16666 * Changes the data store this view uses and refresh the view.
16667 * @param {Store} store
16669 setStore : function(store, initial){
16670 if(!initial && this.store){
16671 this.store.un("datachanged", this.refresh);
16672 this.store.un("add", this.onAdd);
16673 this.store.un("remove", this.onRemove);
16674 this.store.un("update", this.onUpdate);
16675 this.store.un("clear", this.refresh);
16676 this.store.un("beforeload", this.onBeforeLoad);
16677 this.store.un("load", this.onLoad);
16678 this.store.un("loadexception", this.onLoad);
16682 store.on("datachanged", this.refresh, this);
16683 store.on("add", this.onAdd, this);
16684 store.on("remove", this.onRemove, this);
16685 store.on("update", this.onUpdate, this);
16686 store.on("clear", this.refresh, this);
16687 store.on("beforeload", this.onBeforeLoad, this);
16688 store.on("load", this.onLoad, this);
16689 store.on("loadexception", this.onLoad, this);
16697 * onbeforeLoad - masks the loading area.
16700 onBeforeLoad : function(store,opts)
16702 //Roo.log('onBeforeLoad');
16704 this.el.update("");
16706 this.el.mask(this.mask ? this.mask : "Loading" );
16708 onLoad : function ()
16715 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16716 * @param {HTMLElement} node
16717 * @return {HTMLElement} The template node
16719 findItemFromChild : function(node){
16720 var el = this.dataName ?
16721 this.el.child('.roo-tpl-' + this.dataName,true) :
16724 if(!node || node.parentNode == el){
16727 var p = node.parentNode;
16728 while(p && p != el){
16729 if(p.parentNode == el){
16738 onClick : function(e){
16739 var item = this.findItemFromChild(e.getTarget());
16741 var index = this.indexOf(item);
16742 if(this.onItemClick(item, index, e) !== false){
16743 this.fireEvent("click", this, index, item, e);
16746 this.clearSelections();
16751 onContextMenu : function(e){
16752 var item = this.findItemFromChild(e.getTarget());
16754 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16759 onDblClick : function(e){
16760 var item = this.findItemFromChild(e.getTarget());
16762 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16766 onItemClick : function(item, index, e)
16768 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16771 if (this.toggleSelect) {
16772 var m = this.isSelected(item) ? 'unselect' : 'select';
16775 _t[m](item, true, false);
16778 if(this.multiSelect || this.singleSelect){
16779 if(this.multiSelect && e.shiftKey && this.lastSelection){
16780 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16782 this.select(item, this.multiSelect && e.ctrlKey);
16783 this.lastSelection = item;
16786 if(!this.tickable){
16787 e.preventDefault();
16795 * Get the number of selected nodes.
16798 getSelectionCount : function(){
16799 return this.selections.length;
16803 * Get the currently selected nodes.
16804 * @return {Array} An array of HTMLElements
16806 getSelectedNodes : function(){
16807 return this.selections;
16811 * Get the indexes of the selected nodes.
16814 getSelectedIndexes : function(){
16815 var indexes = [], s = this.selections;
16816 for(var i = 0, len = s.length; i < len; i++){
16817 indexes.push(s[i].nodeIndex);
16823 * Clear all selections
16824 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16826 clearSelections : function(suppressEvent){
16827 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16828 this.cmp.elements = this.selections;
16829 this.cmp.removeClass(this.selectedClass);
16830 this.selections = [];
16831 if(!suppressEvent){
16832 this.fireEvent("selectionchange", this, this.selections);
16838 * Returns true if the passed node is selected
16839 * @param {HTMLElement/Number} node The node or node index
16840 * @return {Boolean}
16842 isSelected : function(node){
16843 var s = this.selections;
16847 node = this.getNode(node);
16848 return s.indexOf(node) !== -1;
16853 * @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
16854 * @param {Boolean} keepExisting (optional) true to keep existing selections
16855 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16857 select : function(nodeInfo, keepExisting, suppressEvent){
16858 if(nodeInfo instanceof Array){
16860 this.clearSelections(true);
16862 for(var i = 0, len = nodeInfo.length; i < len; i++){
16863 this.select(nodeInfo[i], true, true);
16867 var node = this.getNode(nodeInfo);
16868 if(!node || this.isSelected(node)){
16869 return; // already selected.
16872 this.clearSelections(true);
16875 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16876 Roo.fly(node).addClass(this.selectedClass);
16877 this.selections.push(node);
16878 if(!suppressEvent){
16879 this.fireEvent("selectionchange", this, this.selections);
16887 * @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
16888 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16889 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16891 unselect : function(nodeInfo, keepExisting, suppressEvent)
16893 if(nodeInfo instanceof Array){
16894 Roo.each(this.selections, function(s) {
16895 this.unselect(s, nodeInfo);
16899 var node = this.getNode(nodeInfo);
16900 if(!node || !this.isSelected(node)){
16901 //Roo.log("not selected");
16902 return; // not selected.
16906 Roo.each(this.selections, function(s) {
16908 Roo.fly(node).removeClass(this.selectedClass);
16915 this.selections= ns;
16916 this.fireEvent("selectionchange", this, this.selections);
16920 * Gets a template node.
16921 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16922 * @return {HTMLElement} The node or null if it wasn't found
16924 getNode : function(nodeInfo){
16925 if(typeof nodeInfo == "string"){
16926 return document.getElementById(nodeInfo);
16927 }else if(typeof nodeInfo == "number"){
16928 return this.nodes[nodeInfo];
16934 * Gets a range template nodes.
16935 * @param {Number} startIndex
16936 * @param {Number} endIndex
16937 * @return {Array} An array of nodes
16939 getNodes : function(start, end){
16940 var ns = this.nodes;
16941 start = start || 0;
16942 end = typeof end == "undefined" ? ns.length - 1 : end;
16945 for(var i = start; i <= end; i++){
16949 for(var i = start; i >= end; i--){
16957 * Finds the index of the passed node
16958 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16959 * @return {Number} The index of the node or -1
16961 indexOf : function(node){
16962 node = this.getNode(node);
16963 if(typeof node.nodeIndex == "number"){
16964 return node.nodeIndex;
16966 var ns = this.nodes;
16967 for(var i = 0, len = ns.length; i < len; i++){
16978 * based on jquery fullcalendar
16982 Roo.bootstrap = Roo.bootstrap || {};
16984 * @class Roo.bootstrap.Calendar
16985 * @extends Roo.bootstrap.Component
16986 * Bootstrap Calendar class
16987 * @cfg {Boolean} loadMask (true|false) default false
16988 * @cfg {Object} header generate the user specific header of the calendar, default false
16991 * Create a new Container
16992 * @param {Object} config The config object
16997 Roo.bootstrap.Calendar = function(config){
16998 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
17002 * Fires when a date is selected
17003 * @param {DatePicker} this
17004 * @param {Date} date The selected date
17008 * @event monthchange
17009 * Fires when the displayed month changes
17010 * @param {DatePicker} this
17011 * @param {Date} date The selected month
17013 'monthchange': true,
17015 * @event evententer
17016 * Fires when mouse over an event
17017 * @param {Calendar} this
17018 * @param {event} Event
17020 'evententer': true,
17022 * @event eventleave
17023 * Fires when the mouse leaves an
17024 * @param {Calendar} this
17027 'eventleave': true,
17029 * @event eventclick
17030 * Fires when the mouse click an
17031 * @param {Calendar} this
17040 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
17043 * @cfg {Number} startDay
17044 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
17052 getAutoCreate : function(){
17055 var fc_button = function(name, corner, style, content ) {
17056 return Roo.apply({},{
17058 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
17060 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
17063 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
17074 style : 'width:100%',
17081 cls : 'fc-header-left',
17083 fc_button('prev', 'left', 'arrow', '‹' ),
17084 fc_button('next', 'right', 'arrow', '›' ),
17085 { tag: 'span', cls: 'fc-header-space' },
17086 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
17094 cls : 'fc-header-center',
17098 cls: 'fc-header-title',
17101 html : 'month / year'
17109 cls : 'fc-header-right',
17111 /* fc_button('month', 'left', '', 'month' ),
17112 fc_button('week', '', '', 'week' ),
17113 fc_button('day', 'right', '', 'day' )
17125 header = this.header;
17128 var cal_heads = function() {
17130 // fixme - handle this.
17132 for (var i =0; i < Date.dayNames.length; i++) {
17133 var d = Date.dayNames[i];
17136 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17137 html : d.substring(0,3)
17141 ret[0].cls += ' fc-first';
17142 ret[6].cls += ' fc-last';
17145 var cal_cell = function(n) {
17148 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17153 cls: 'fc-day-number',
17157 cls: 'fc-day-content',
17161 style: 'position: relative;' // height: 17px;
17173 var cal_rows = function() {
17176 for (var r = 0; r < 6; r++) {
17183 for (var i =0; i < Date.dayNames.length; i++) {
17184 var d = Date.dayNames[i];
17185 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17188 row.cn[0].cls+=' fc-first';
17189 row.cn[0].cn[0].style = 'min-height:90px';
17190 row.cn[6].cls+=' fc-last';
17194 ret[0].cls += ' fc-first';
17195 ret[4].cls += ' fc-prev-last';
17196 ret[5].cls += ' fc-last';
17203 cls: 'fc-border-separate',
17204 style : 'width:100%',
17212 cls : 'fc-first fc-last',
17230 cls : 'fc-content',
17231 style : "position: relative;",
17234 cls : 'fc-view fc-view-month fc-grid',
17235 style : 'position: relative',
17236 unselectable : 'on',
17239 cls : 'fc-event-container',
17240 style : 'position:absolute;z-index:8;top:0;left:0;'
17258 initEvents : function()
17261 throw "can not find store for calendar";
17267 style: "text-align:center",
17271 style: "background-color:white;width:50%;margin:250 auto",
17275 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
17286 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17288 var size = this.el.select('.fc-content', true).first().getSize();
17289 this.maskEl.setSize(size.width, size.height);
17290 this.maskEl.enableDisplayMode("block");
17291 if(!this.loadMask){
17292 this.maskEl.hide();
17295 this.store = Roo.factory(this.store, Roo.data);
17296 this.store.on('load', this.onLoad, this);
17297 this.store.on('beforeload', this.onBeforeLoad, this);
17301 this.cells = this.el.select('.fc-day',true);
17302 //Roo.log(this.cells);
17303 this.textNodes = this.el.query('.fc-day-number');
17304 this.cells.addClassOnOver('fc-state-hover');
17306 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17307 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17308 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17309 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17311 this.on('monthchange', this.onMonthChange, this);
17313 this.update(new Date().clearTime());
17316 resize : function() {
17317 var sz = this.el.getSize();
17319 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17320 this.el.select('.fc-day-content div',true).setHeight(34);
17325 showPrevMonth : function(e){
17326 this.update(this.activeDate.add("mo", -1));
17328 showToday : function(e){
17329 this.update(new Date().clearTime());
17332 showNextMonth : function(e){
17333 this.update(this.activeDate.add("mo", 1));
17337 showPrevYear : function(){
17338 this.update(this.activeDate.add("y", -1));
17342 showNextYear : function(){
17343 this.update(this.activeDate.add("y", 1));
17348 update : function(date)
17350 var vd = this.activeDate;
17351 this.activeDate = date;
17352 // if(vd && this.el){
17353 // var t = date.getTime();
17354 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17355 // Roo.log('using add remove');
17357 // this.fireEvent('monthchange', this, date);
17359 // this.cells.removeClass("fc-state-highlight");
17360 // this.cells.each(function(c){
17361 // if(c.dateValue == t){
17362 // c.addClass("fc-state-highlight");
17363 // setTimeout(function(){
17364 // try{c.dom.firstChild.focus();}catch(e){}
17374 var days = date.getDaysInMonth();
17376 var firstOfMonth = date.getFirstDateOfMonth();
17377 var startingPos = firstOfMonth.getDay()-this.startDay;
17379 if(startingPos < this.startDay){
17383 var pm = date.add(Date.MONTH, -1);
17384 var prevStart = pm.getDaysInMonth()-startingPos;
17386 this.cells = this.el.select('.fc-day',true);
17387 this.textNodes = this.el.query('.fc-day-number');
17388 this.cells.addClassOnOver('fc-state-hover');
17390 var cells = this.cells.elements;
17391 var textEls = this.textNodes;
17393 Roo.each(cells, function(cell){
17394 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17397 days += startingPos;
17399 // convert everything to numbers so it's fast
17400 var day = 86400000;
17401 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17404 //Roo.log(prevStart);
17406 var today = new Date().clearTime().getTime();
17407 var sel = date.clearTime().getTime();
17408 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17409 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17410 var ddMatch = this.disabledDatesRE;
17411 var ddText = this.disabledDatesText;
17412 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17413 var ddaysText = this.disabledDaysText;
17414 var format = this.format;
17416 var setCellClass = function(cal, cell){
17420 //Roo.log('set Cell Class');
17422 var t = d.getTime();
17426 cell.dateValue = t;
17428 cell.className += " fc-today";
17429 cell.className += " fc-state-highlight";
17430 cell.title = cal.todayText;
17433 // disable highlight in other month..
17434 //cell.className += " fc-state-highlight";
17439 cell.className = " fc-state-disabled";
17440 cell.title = cal.minText;
17444 cell.className = " fc-state-disabled";
17445 cell.title = cal.maxText;
17449 if(ddays.indexOf(d.getDay()) != -1){
17450 cell.title = ddaysText;
17451 cell.className = " fc-state-disabled";
17454 if(ddMatch && format){
17455 var fvalue = d.dateFormat(format);
17456 if(ddMatch.test(fvalue)){
17457 cell.title = ddText.replace("%0", fvalue);
17458 cell.className = " fc-state-disabled";
17462 if (!cell.initialClassName) {
17463 cell.initialClassName = cell.dom.className;
17466 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17471 for(; i < startingPos; i++) {
17472 textEls[i].innerHTML = (++prevStart);
17473 d.setDate(d.getDate()+1);
17475 cells[i].className = "fc-past fc-other-month";
17476 setCellClass(this, cells[i]);
17481 for(; i < days; i++){
17482 intDay = i - startingPos + 1;
17483 textEls[i].innerHTML = (intDay);
17484 d.setDate(d.getDate()+1);
17486 cells[i].className = ''; // "x-date-active";
17487 setCellClass(this, cells[i]);
17491 for(; i < 42; i++) {
17492 textEls[i].innerHTML = (++extraDays);
17493 d.setDate(d.getDate()+1);
17495 cells[i].className = "fc-future fc-other-month";
17496 setCellClass(this, cells[i]);
17499 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17501 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17503 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17504 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17506 if(totalRows != 6){
17507 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17508 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17511 this.fireEvent('monthchange', this, date);
17515 if(!this.internalRender){
17516 var main = this.el.dom.firstChild;
17517 var w = main.offsetWidth;
17518 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17519 Roo.fly(main).setWidth(w);
17520 this.internalRender = true;
17521 // opera does not respect the auto grow header center column
17522 // then, after it gets a width opera refuses to recalculate
17523 // without a second pass
17524 if(Roo.isOpera && !this.secondPass){
17525 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17526 this.secondPass = true;
17527 this.update.defer(10, this, [date]);
17534 findCell : function(dt) {
17535 dt = dt.clearTime().getTime();
17537 this.cells.each(function(c){
17538 //Roo.log("check " +c.dateValue + '?=' + dt);
17539 if(c.dateValue == dt){
17549 findCells : function(ev) {
17550 var s = ev.start.clone().clearTime().getTime();
17552 var e= ev.end.clone().clearTime().getTime();
17555 this.cells.each(function(c){
17556 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17558 if(c.dateValue > e){
17561 if(c.dateValue < s){
17570 // findBestRow: function(cells)
17574 // for (var i =0 ; i < cells.length;i++) {
17575 // ret = Math.max(cells[i].rows || 0,ret);
17582 addItem : function(ev)
17584 // look for vertical location slot in
17585 var cells = this.findCells(ev);
17587 // ev.row = this.findBestRow(cells);
17589 // work out the location.
17593 for(var i =0; i < cells.length; i++) {
17595 cells[i].row = cells[0].row;
17598 cells[i].row = cells[i].row + 1;
17608 if (crow.start.getY() == cells[i].getY()) {
17610 crow.end = cells[i];
17627 cells[0].events.push(ev);
17629 this.calevents.push(ev);
17632 clearEvents: function() {
17634 if(!this.calevents){
17638 Roo.each(this.cells.elements, function(c){
17644 Roo.each(this.calevents, function(e) {
17645 Roo.each(e.els, function(el) {
17646 el.un('mouseenter' ,this.onEventEnter, this);
17647 el.un('mouseleave' ,this.onEventLeave, this);
17652 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17658 renderEvents: function()
17662 this.cells.each(function(c) {
17671 if(c.row != c.events.length){
17672 r = 4 - (4 - (c.row - c.events.length));
17675 c.events = ev.slice(0, r);
17676 c.more = ev.slice(r);
17678 if(c.more.length && c.more.length == 1){
17679 c.events.push(c.more.pop());
17682 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17686 this.cells.each(function(c) {
17688 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17691 for (var e = 0; e < c.events.length; e++){
17692 var ev = c.events[e];
17693 var rows = ev.rows;
17695 for(var i = 0; i < rows.length; i++) {
17697 // how many rows should it span..
17700 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17701 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17703 unselectable : "on",
17706 cls: 'fc-event-inner',
17710 // cls: 'fc-event-time',
17711 // html : cells.length > 1 ? '' : ev.time
17715 cls: 'fc-event-title',
17716 html : String.format('{0}', ev.title)
17723 cls: 'ui-resizable-handle ui-resizable-e',
17724 html : '  '
17731 cfg.cls += ' fc-event-start';
17733 if ((i+1) == rows.length) {
17734 cfg.cls += ' fc-event-end';
17737 var ctr = _this.el.select('.fc-event-container',true).first();
17738 var cg = ctr.createChild(cfg);
17740 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17741 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17743 var r = (c.more.length) ? 1 : 0;
17744 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17745 cg.setWidth(ebox.right - sbox.x -2);
17747 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17748 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17749 cg.on('click', _this.onEventClick, _this, ev);
17760 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17761 style : 'position: absolute',
17762 unselectable : "on",
17765 cls: 'fc-event-inner',
17769 cls: 'fc-event-title',
17777 cls: 'ui-resizable-handle ui-resizable-e',
17778 html : '  '
17784 var ctr = _this.el.select('.fc-event-container',true).first();
17785 var cg = ctr.createChild(cfg);
17787 var sbox = c.select('.fc-day-content',true).first().getBox();
17788 var ebox = c.select('.fc-day-content',true).first().getBox();
17790 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17791 cg.setWidth(ebox.right - sbox.x -2);
17793 cg.on('click', _this.onMoreEventClick, _this, c.more);
17803 onEventEnter: function (e, el,event,d) {
17804 this.fireEvent('evententer', this, el, event);
17807 onEventLeave: function (e, el,event,d) {
17808 this.fireEvent('eventleave', this, el, event);
17811 onEventClick: function (e, el,event,d) {
17812 this.fireEvent('eventclick', this, el, event);
17815 onMonthChange: function () {
17819 onMoreEventClick: function(e, el, more)
17823 this.calpopover.placement = 'right';
17824 this.calpopover.setTitle('More');
17826 this.calpopover.setContent('');
17828 var ctr = this.calpopover.el.select('.popover-content', true).first();
17830 Roo.each(more, function(m){
17832 cls : 'fc-event-hori fc-event-draggable',
17835 var cg = ctr.createChild(cfg);
17837 cg.on('click', _this.onEventClick, _this, m);
17840 this.calpopover.show(el);
17845 onLoad: function ()
17847 this.calevents = [];
17850 if(this.store.getCount() > 0){
17851 this.store.data.each(function(d){
17854 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17855 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17856 time : d.data.start_time,
17857 title : d.data.title,
17858 description : d.data.description,
17859 venue : d.data.venue
17864 this.renderEvents();
17866 if(this.calevents.length && this.loadMask){
17867 this.maskEl.hide();
17871 onBeforeLoad: function()
17873 this.clearEvents();
17875 this.maskEl.show();
17889 * @class Roo.bootstrap.Popover
17890 * @extends Roo.bootstrap.Component
17891 * Bootstrap Popover class
17892 * @cfg {String} html contents of the popover (or false to use children..)
17893 * @cfg {String} title of popover (or false to hide)
17894 * @cfg {String} placement how it is placed
17895 * @cfg {String} trigger click || hover (or false to trigger manually)
17896 * @cfg {String} over what (parent or false to trigger manually.)
17897 * @cfg {Number} delay - delay before showing
17900 * Create a new Popover
17901 * @param {Object} config The config object
17904 Roo.bootstrap.Popover = function(config){
17905 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17911 * After the popover show
17913 * @param {Roo.bootstrap.Popover} this
17918 * After the popover hide
17920 * @param {Roo.bootstrap.Popover} this
17926 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17928 title: 'Fill in a title',
17931 placement : 'right',
17932 trigger : 'hover', // hover
17938 can_build_overlaid : false,
17940 getChildContainer : function()
17942 return this.el.select('.popover-content',true).first();
17945 getAutoCreate : function(){
17948 cls : 'popover roo-dynamic',
17949 style: 'display:block',
17955 cls : 'popover-inner',
17959 cls: 'popover-title popover-header',
17963 cls : 'popover-content popover-body',
17974 setTitle: function(str)
17977 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17979 setContent: function(str)
17982 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17984 // as it get's added to the bottom of the page.
17985 onRender : function(ct, position)
17987 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17989 var cfg = Roo.apply({}, this.getAutoCreate());
17993 cfg.cls += ' ' + this.cls;
17996 cfg.style = this.style;
17998 //Roo.log("adding to ");
17999 this.el = Roo.get(document.body).createChild(cfg, position);
18000 // Roo.log(this.el);
18005 initEvents : function()
18007 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
18008 this.el.enableDisplayMode('block');
18010 if (this.over === false) {
18013 if (this.triggers === false) {
18016 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18017 var triggers = this.trigger ? this.trigger.split(' ') : [];
18018 Roo.each(triggers, function(trigger) {
18020 if (trigger == 'click') {
18021 on_el.on('click', this.toggle, this);
18022 } else if (trigger != 'manual') {
18023 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
18024 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
18026 on_el.on(eventIn ,this.enter, this);
18027 on_el.on(eventOut, this.leave, this);
18038 toggle : function () {
18039 this.hoverState == 'in' ? this.leave() : this.enter();
18042 enter : function () {
18044 clearTimeout(this.timeout);
18046 this.hoverState = 'in';
18048 if (!this.delay || !this.delay.show) {
18053 this.timeout = setTimeout(function () {
18054 if (_t.hoverState == 'in') {
18057 }, this.delay.show)
18060 leave : function() {
18061 clearTimeout(this.timeout);
18063 this.hoverState = 'out';
18065 if (!this.delay || !this.delay.hide) {
18070 this.timeout = setTimeout(function () {
18071 if (_t.hoverState == 'out') {
18074 }, this.delay.hide)
18077 show : function (on_el)
18080 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18084 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18085 if (this.html !== false) {
18086 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18088 this.el.removeClass([
18089 'fade','top','bottom', 'left', 'right','in',
18090 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18092 if (!this.title.length) {
18093 this.el.select('.popover-title',true).hide();
18096 var placement = typeof this.placement == 'function' ?
18097 this.placement.call(this, this.el, on_el) :
18100 var autoToken = /\s?auto?\s?/i;
18101 var autoPlace = autoToken.test(placement);
18103 placement = placement.replace(autoToken, '') || 'top';
18107 //this.el.setXY([0,0]);
18109 this.el.dom.style.display='block';
18110 this.el.addClass(placement);
18112 //this.el.appendTo(on_el);
18114 var p = this.getPosition();
18115 var box = this.el.getBox();
18120 var align = Roo.bootstrap.Popover.alignment[placement];
18123 this.el.alignTo(on_el, align[0],align[1]);
18124 //var arrow = this.el.select('.arrow',true).first();
18125 //arrow.set(align[2],
18127 this.el.addClass('in');
18130 if (this.el.hasClass('fade')) {
18134 this.hoverState = 'in';
18136 this.fireEvent('show', this);
18141 this.el.setXY([0,0]);
18142 this.el.removeClass('in');
18144 this.hoverState = null;
18146 this.fireEvent('hide', this);
18151 Roo.bootstrap.Popover.alignment = {
18152 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18153 'right' : ['l-r', [10,0], 'left bs-popover-left'],
18154 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18155 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18166 * @class Roo.bootstrap.Progress
18167 * @extends Roo.bootstrap.Component
18168 * Bootstrap Progress class
18169 * @cfg {Boolean} striped striped of the progress bar
18170 * @cfg {Boolean} active animated of the progress bar
18174 * Create a new Progress
18175 * @param {Object} config The config object
18178 Roo.bootstrap.Progress = function(config){
18179 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18182 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
18187 getAutoCreate : function(){
18195 cfg.cls += ' progress-striped';
18199 cfg.cls += ' active';
18218 * @class Roo.bootstrap.ProgressBar
18219 * @extends Roo.bootstrap.Component
18220 * Bootstrap ProgressBar class
18221 * @cfg {Number} aria_valuenow aria-value now
18222 * @cfg {Number} aria_valuemin aria-value min
18223 * @cfg {Number} aria_valuemax aria-value max
18224 * @cfg {String} label label for the progress bar
18225 * @cfg {String} panel (success | info | warning | danger )
18226 * @cfg {String} role role of the progress bar
18227 * @cfg {String} sr_only text
18231 * Create a new ProgressBar
18232 * @param {Object} config The config object
18235 Roo.bootstrap.ProgressBar = function(config){
18236 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18239 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
18243 aria_valuemax : 100,
18249 getAutoCreate : function()
18254 cls: 'progress-bar',
18255 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18267 cfg.role = this.role;
18270 if(this.aria_valuenow){
18271 cfg['aria-valuenow'] = this.aria_valuenow;
18274 if(this.aria_valuemin){
18275 cfg['aria-valuemin'] = this.aria_valuemin;
18278 if(this.aria_valuemax){
18279 cfg['aria-valuemax'] = this.aria_valuemax;
18282 if(this.label && !this.sr_only){
18283 cfg.html = this.label;
18287 cfg.cls += ' progress-bar-' + this.panel;
18293 update : function(aria_valuenow)
18295 this.aria_valuenow = aria_valuenow;
18297 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18312 * @class Roo.bootstrap.TabGroup
18313 * @extends Roo.bootstrap.Column
18314 * Bootstrap Column class
18315 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18316 * @cfg {Boolean} carousel true to make the group behave like a carousel
18317 * @cfg {Boolean} bullets show bullets for the panels
18318 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18319 * @cfg {Number} timer auto slide timer .. default 0 millisecond
18320 * @cfg {Boolean} showarrow (true|false) show arrow default true
18323 * Create a new TabGroup
18324 * @param {Object} config The config object
18327 Roo.bootstrap.TabGroup = function(config){
18328 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18330 this.navId = Roo.id();
18333 Roo.bootstrap.TabGroup.register(this);
18337 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
18340 transition : false,
18345 slideOnTouch : false,
18348 getAutoCreate : function()
18350 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18352 cfg.cls += ' tab-content';
18354 if (this.carousel) {
18355 cfg.cls += ' carousel slide';
18358 cls : 'carousel-inner',
18362 if(this.bullets && !Roo.isTouch){
18365 cls : 'carousel-bullets',
18369 if(this.bullets_cls){
18370 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18377 cfg.cn[0].cn.push(bullets);
18380 if(this.showarrow){
18381 cfg.cn[0].cn.push({
18383 class : 'carousel-arrow',
18387 class : 'carousel-prev',
18391 class : 'fa fa-chevron-left'
18397 class : 'carousel-next',
18401 class : 'fa fa-chevron-right'
18414 initEvents: function()
18416 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18417 // this.el.on("touchstart", this.onTouchStart, this);
18420 if(this.autoslide){
18423 this.slideFn = window.setInterval(function() {
18424 _this.showPanelNext();
18428 if(this.showarrow){
18429 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18430 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18436 // onTouchStart : function(e, el, o)
18438 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18442 // this.showPanelNext();
18446 getChildContainer : function()
18448 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18452 * register a Navigation item
18453 * @param {Roo.bootstrap.NavItem} the navitem to add
18455 register : function(item)
18457 this.tabs.push( item);
18458 item.navId = this.navId; // not really needed..
18463 getActivePanel : function()
18466 Roo.each(this.tabs, function(t) {
18476 getPanelByName : function(n)
18479 Roo.each(this.tabs, function(t) {
18480 if (t.tabId == n) {
18488 indexOfPanel : function(p)
18491 Roo.each(this.tabs, function(t,i) {
18492 if (t.tabId == p.tabId) {
18501 * show a specific panel
18502 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18503 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18505 showPanel : function (pan)
18507 if(this.transition || typeof(pan) == 'undefined'){
18508 Roo.log("waiting for the transitionend");
18512 if (typeof(pan) == 'number') {
18513 pan = this.tabs[pan];
18516 if (typeof(pan) == 'string') {
18517 pan = this.getPanelByName(pan);
18520 var cur = this.getActivePanel();
18523 Roo.log('pan or acitve pan is undefined');
18527 if (pan.tabId == this.getActivePanel().tabId) {
18531 if (false === cur.fireEvent('beforedeactivate')) {
18535 if(this.bullets > 0 && !Roo.isTouch){
18536 this.setActiveBullet(this.indexOfPanel(pan));
18539 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18541 //class="carousel-item carousel-item-next carousel-item-left"
18543 this.transition = true;
18544 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18545 var lr = dir == 'next' ? 'left' : 'right';
18546 pan.el.addClass(dir); // or prev
18547 pan.el.addClass('carousel-item-' + dir); // or prev
18548 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18549 cur.el.addClass(lr); // or right
18550 pan.el.addClass(lr);
18551 cur.el.addClass('carousel-item-' +lr); // or right
18552 pan.el.addClass('carousel-item-' +lr);
18556 cur.el.on('transitionend', function() {
18557 Roo.log("trans end?");
18559 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18560 pan.setActive(true);
18562 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18563 cur.setActive(false);
18565 _this.transition = false;
18567 }, this, { single: true } );
18572 cur.setActive(false);
18573 pan.setActive(true);
18578 showPanelNext : function()
18580 var i = this.indexOfPanel(this.getActivePanel());
18582 if (i >= this.tabs.length - 1 && !this.autoslide) {
18586 if (i >= this.tabs.length - 1 && this.autoslide) {
18590 this.showPanel(this.tabs[i+1]);
18593 showPanelPrev : function()
18595 var i = this.indexOfPanel(this.getActivePanel());
18597 if (i < 1 && !this.autoslide) {
18601 if (i < 1 && this.autoslide) {
18602 i = this.tabs.length;
18605 this.showPanel(this.tabs[i-1]);
18609 addBullet: function()
18611 if(!this.bullets || Roo.isTouch){
18614 var ctr = this.el.select('.carousel-bullets',true).first();
18615 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18616 var bullet = ctr.createChild({
18617 cls : 'bullet bullet-' + i
18618 },ctr.dom.lastChild);
18623 bullet.on('click', (function(e, el, o, ii, t){
18625 e.preventDefault();
18627 this.showPanel(ii);
18629 if(this.autoslide && this.slideFn){
18630 clearInterval(this.slideFn);
18631 this.slideFn = window.setInterval(function() {
18632 _this.showPanelNext();
18636 }).createDelegate(this, [i, bullet], true));
18641 setActiveBullet : function(i)
18647 Roo.each(this.el.select('.bullet', true).elements, function(el){
18648 el.removeClass('selected');
18651 var bullet = this.el.select('.bullet-' + i, true).first();
18657 bullet.addClass('selected');
18668 Roo.apply(Roo.bootstrap.TabGroup, {
18672 * register a Navigation Group
18673 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18675 register : function(navgrp)
18677 this.groups[navgrp.navId] = navgrp;
18681 * fetch a Navigation Group based on the navigation ID
18682 * if one does not exist , it will get created.
18683 * @param {string} the navgroup to add
18684 * @returns {Roo.bootstrap.NavGroup} the navgroup
18686 get: function(navId) {
18687 if (typeof(this.groups[navId]) == 'undefined') {
18688 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18690 return this.groups[navId] ;
18705 * @class Roo.bootstrap.TabPanel
18706 * @extends Roo.bootstrap.Component
18707 * Bootstrap TabPanel class
18708 * @cfg {Boolean} active panel active
18709 * @cfg {String} html panel content
18710 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18711 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18712 * @cfg {String} href click to link..
18716 * Create a new TabPanel
18717 * @param {Object} config The config object
18720 Roo.bootstrap.TabPanel = function(config){
18721 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18725 * Fires when the active status changes
18726 * @param {Roo.bootstrap.TabPanel} this
18727 * @param {Boolean} state the new state
18732 * @event beforedeactivate
18733 * Fires before a tab is de-activated - can be used to do validation on a form.
18734 * @param {Roo.bootstrap.TabPanel} this
18735 * @return {Boolean} false if there is an error
18738 'beforedeactivate': true
18741 this.tabId = this.tabId || Roo.id();
18745 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18753 getAutoCreate : function(){
18758 // item is needed for carousel - not sure if it has any effect otherwise
18759 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18760 html: this.html || ''
18764 cfg.cls += ' active';
18768 cfg.tabId = this.tabId;
18776 initEvents: function()
18778 var p = this.parent();
18780 this.navId = this.navId || p.navId;
18782 if (typeof(this.navId) != 'undefined') {
18783 // not really needed.. but just in case.. parent should be a NavGroup.
18784 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18788 var i = tg.tabs.length - 1;
18790 if(this.active && tg.bullets > 0 && i < tg.bullets){
18791 tg.setActiveBullet(i);
18795 this.el.on('click', this.onClick, this);
18798 this.el.on("touchstart", this.onTouchStart, this);
18799 this.el.on("touchmove", this.onTouchMove, this);
18800 this.el.on("touchend", this.onTouchEnd, this);
18805 onRender : function(ct, position)
18807 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18810 setActive : function(state)
18812 Roo.log("panel - set active " + this.tabId + "=" + state);
18814 this.active = state;
18816 this.el.removeClass('active');
18818 } else if (!this.el.hasClass('active')) {
18819 this.el.addClass('active');
18822 this.fireEvent('changed', this, state);
18825 onClick : function(e)
18827 e.preventDefault();
18829 if(!this.href.length){
18833 window.location.href = this.href;
18842 onTouchStart : function(e)
18844 this.swiping = false;
18846 this.startX = e.browserEvent.touches[0].clientX;
18847 this.startY = e.browserEvent.touches[0].clientY;
18850 onTouchMove : function(e)
18852 this.swiping = true;
18854 this.endX = e.browserEvent.touches[0].clientX;
18855 this.endY = e.browserEvent.touches[0].clientY;
18858 onTouchEnd : function(e)
18865 var tabGroup = this.parent();
18867 if(this.endX > this.startX){ // swiping right
18868 tabGroup.showPanelPrev();
18872 if(this.startX > this.endX){ // swiping left
18873 tabGroup.showPanelNext();
18892 * @class Roo.bootstrap.DateField
18893 * @extends Roo.bootstrap.Input
18894 * Bootstrap DateField class
18895 * @cfg {Number} weekStart default 0
18896 * @cfg {String} viewMode default empty, (months|years)
18897 * @cfg {String} minViewMode default empty, (months|years)
18898 * @cfg {Number} startDate default -Infinity
18899 * @cfg {Number} endDate default Infinity
18900 * @cfg {Boolean} todayHighlight default false
18901 * @cfg {Boolean} todayBtn default false
18902 * @cfg {Boolean} calendarWeeks default false
18903 * @cfg {Object} daysOfWeekDisabled default empty
18904 * @cfg {Boolean} singleMode default false (true | false)
18906 * @cfg {Boolean} keyboardNavigation default true
18907 * @cfg {String} language default en
18910 * Create a new DateField
18911 * @param {Object} config The config object
18914 Roo.bootstrap.DateField = function(config){
18915 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18919 * Fires when this field show.
18920 * @param {Roo.bootstrap.DateField} this
18921 * @param {Mixed} date The date value
18926 * Fires when this field hide.
18927 * @param {Roo.bootstrap.DateField} this
18928 * @param {Mixed} date The date value
18933 * Fires when select a date.
18934 * @param {Roo.bootstrap.DateField} this
18935 * @param {Mixed} date The date value
18939 * @event beforeselect
18940 * Fires when before select a date.
18941 * @param {Roo.bootstrap.DateField} this
18942 * @param {Mixed} date The date value
18944 beforeselect : true
18948 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18951 * @cfg {String} format
18952 * The default date format string which can be overriden for localization support. The format must be
18953 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18957 * @cfg {String} altFormats
18958 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18959 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18961 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18969 todayHighlight : false,
18975 keyboardNavigation: true,
18977 calendarWeeks: false,
18979 startDate: -Infinity,
18983 daysOfWeekDisabled: [],
18987 singleMode : false,
18989 UTCDate: function()
18991 return new Date(Date.UTC.apply(Date, arguments));
18994 UTCToday: function()
18996 var today = new Date();
18997 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
19000 getDate: function() {
19001 var d = this.getUTCDate();
19002 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
19005 getUTCDate: function() {
19009 setDate: function(d) {
19010 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
19013 setUTCDate: function(d) {
19015 this.setValue(this.formatDate(this.date));
19018 onRender: function(ct, position)
19021 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
19023 this.language = this.language || 'en';
19024 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
19025 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
19027 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
19028 this.format = this.format || 'm/d/y';
19029 this.isInline = false;
19030 this.isInput = true;
19031 this.component = this.el.select('.add-on', true).first() || false;
19032 this.component = (this.component && this.component.length === 0) ? false : this.component;
19033 this.hasInput = this.component && this.inputEl().length;
19035 if (typeof(this.minViewMode === 'string')) {
19036 switch (this.minViewMode) {
19038 this.minViewMode = 1;
19041 this.minViewMode = 2;
19044 this.minViewMode = 0;
19049 if (typeof(this.viewMode === 'string')) {
19050 switch (this.viewMode) {
19063 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
19065 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
19067 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19069 this.picker().on('mousedown', this.onMousedown, this);
19070 this.picker().on('click', this.onClick, this);
19072 this.picker().addClass('datepicker-dropdown');
19074 this.startViewMode = this.viewMode;
19076 if(this.singleMode){
19077 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19078 v.setVisibilityMode(Roo.Element.DISPLAY);
19082 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19083 v.setStyle('width', '189px');
19087 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19088 if(!this.calendarWeeks){
19093 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19094 v.attr('colspan', function(i, val){
19095 return parseInt(val) + 1;
19100 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19102 this.setStartDate(this.startDate);
19103 this.setEndDate(this.endDate);
19105 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19112 if(this.isInline) {
19117 picker : function()
19119 return this.pickerEl;
19120 // return this.el.select('.datepicker', true).first();
19123 fillDow: function()
19125 var dowCnt = this.weekStart;
19134 if(this.calendarWeeks){
19142 while (dowCnt < this.weekStart + 7) {
19146 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19150 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19153 fillMonths: function()
19156 var months = this.picker().select('>.datepicker-months td', true).first();
19158 months.dom.innerHTML = '';
19164 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19167 months.createChild(month);
19174 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;
19176 if (this.date < this.startDate) {
19177 this.viewDate = new Date(this.startDate);
19178 } else if (this.date > this.endDate) {
19179 this.viewDate = new Date(this.endDate);
19181 this.viewDate = new Date(this.date);
19189 var d = new Date(this.viewDate),
19190 year = d.getUTCFullYear(),
19191 month = d.getUTCMonth(),
19192 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19193 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19194 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19195 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19196 currentDate = this.date && this.date.valueOf(),
19197 today = this.UTCToday();
19199 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19201 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19203 // this.picker.select('>tfoot th.today').
19204 // .text(dates[this.language].today)
19205 // .toggle(this.todayBtn !== false);
19207 this.updateNavArrows();
19210 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19212 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19214 prevMonth.setUTCDate(day);
19216 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19218 var nextMonth = new Date(prevMonth);
19220 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19222 nextMonth = nextMonth.valueOf();
19224 var fillMonths = false;
19226 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19228 while(prevMonth.valueOf() <= nextMonth) {
19231 if (prevMonth.getUTCDay() === this.weekStart) {
19233 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19241 if(this.calendarWeeks){
19242 // ISO 8601: First week contains first thursday.
19243 // ISO also states week starts on Monday, but we can be more abstract here.
19245 // Start of current week: based on weekstart/current date
19246 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19247 // Thursday of this week
19248 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19249 // First Thursday of year, year from thursday
19250 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19251 // Calendar week: ms between thursdays, div ms per day, div 7 days
19252 calWeek = (th - yth) / 864e5 / 7 + 1;
19254 fillMonths.cn.push({
19262 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19264 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19267 if (this.todayHighlight &&
19268 prevMonth.getUTCFullYear() == today.getFullYear() &&
19269 prevMonth.getUTCMonth() == today.getMonth() &&
19270 prevMonth.getUTCDate() == today.getDate()) {
19271 clsName += ' today';
19274 if (currentDate && prevMonth.valueOf() === currentDate) {
19275 clsName += ' active';
19278 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19279 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19280 clsName += ' disabled';
19283 fillMonths.cn.push({
19285 cls: 'day ' + clsName,
19286 html: prevMonth.getDate()
19289 prevMonth.setDate(prevMonth.getDate()+1);
19292 var currentYear = this.date && this.date.getUTCFullYear();
19293 var currentMonth = this.date && this.date.getUTCMonth();
19295 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19297 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19298 v.removeClass('active');
19300 if(currentYear === year && k === currentMonth){
19301 v.addClass('active');
19304 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19305 v.addClass('disabled');
19311 year = parseInt(year/10, 10) * 10;
19313 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19315 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19318 for (var i = -1; i < 11; i++) {
19319 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19321 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19329 showMode: function(dir)
19332 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19335 Roo.each(this.picker().select('>div',true).elements, function(v){
19336 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19339 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19344 if(this.isInline) {
19348 this.picker().removeClass(['bottom', 'top']);
19350 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19352 * place to the top of element!
19356 this.picker().addClass('top');
19357 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19362 this.picker().addClass('bottom');
19364 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19367 parseDate : function(value)
19369 if(!value || value instanceof Date){
19372 var v = Date.parseDate(value, this.format);
19373 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19374 v = Date.parseDate(value, 'Y-m-d');
19376 if(!v && this.altFormats){
19377 if(!this.altFormatsArray){
19378 this.altFormatsArray = this.altFormats.split("|");
19380 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19381 v = Date.parseDate(value, this.altFormatsArray[i]);
19387 formatDate : function(date, fmt)
19389 return (!date || !(date instanceof Date)) ?
19390 date : date.dateFormat(fmt || this.format);
19393 onFocus : function()
19395 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19399 onBlur : function()
19401 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19403 var d = this.inputEl().getValue();
19410 showPopup : function()
19412 this.picker().show();
19416 this.fireEvent('showpopup', this, this.date);
19419 hidePopup : function()
19421 if(this.isInline) {
19424 this.picker().hide();
19425 this.viewMode = this.startViewMode;
19428 this.fireEvent('hidepopup', this, this.date);
19432 onMousedown: function(e)
19434 e.stopPropagation();
19435 e.preventDefault();
19440 Roo.bootstrap.DateField.superclass.keyup.call(this);
19444 setValue: function(v)
19446 if(this.fireEvent('beforeselect', this, v) !== false){
19447 var d = new Date(this.parseDate(v) ).clearTime();
19449 if(isNaN(d.getTime())){
19450 this.date = this.viewDate = '';
19451 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19455 v = this.formatDate(d);
19457 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19459 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19463 this.fireEvent('select', this, this.date);
19467 getValue: function()
19469 return this.formatDate(this.date);
19472 fireKey: function(e)
19474 if (!this.picker().isVisible()){
19475 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19481 var dateChanged = false,
19483 newDate, newViewDate;
19488 e.preventDefault();
19492 if (!this.keyboardNavigation) {
19495 dir = e.keyCode == 37 ? -1 : 1;
19498 newDate = this.moveYear(this.date, dir);
19499 newViewDate = this.moveYear(this.viewDate, dir);
19500 } else if (e.shiftKey){
19501 newDate = this.moveMonth(this.date, dir);
19502 newViewDate = this.moveMonth(this.viewDate, dir);
19504 newDate = new Date(this.date);
19505 newDate.setUTCDate(this.date.getUTCDate() + dir);
19506 newViewDate = new Date(this.viewDate);
19507 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19509 if (this.dateWithinRange(newDate)){
19510 this.date = newDate;
19511 this.viewDate = newViewDate;
19512 this.setValue(this.formatDate(this.date));
19514 e.preventDefault();
19515 dateChanged = true;
19520 if (!this.keyboardNavigation) {
19523 dir = e.keyCode == 38 ? -1 : 1;
19525 newDate = this.moveYear(this.date, dir);
19526 newViewDate = this.moveYear(this.viewDate, dir);
19527 } else if (e.shiftKey){
19528 newDate = this.moveMonth(this.date, dir);
19529 newViewDate = this.moveMonth(this.viewDate, dir);
19531 newDate = new Date(this.date);
19532 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19533 newViewDate = new Date(this.viewDate);
19534 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19536 if (this.dateWithinRange(newDate)){
19537 this.date = newDate;
19538 this.viewDate = newViewDate;
19539 this.setValue(this.formatDate(this.date));
19541 e.preventDefault();
19542 dateChanged = true;
19546 this.setValue(this.formatDate(this.date));
19548 e.preventDefault();
19551 this.setValue(this.formatDate(this.date));
19565 onClick: function(e)
19567 e.stopPropagation();
19568 e.preventDefault();
19570 var target = e.getTarget();
19572 if(target.nodeName.toLowerCase() === 'i'){
19573 target = Roo.get(target).dom.parentNode;
19576 var nodeName = target.nodeName;
19577 var className = target.className;
19578 var html = target.innerHTML;
19579 //Roo.log(nodeName);
19581 switch(nodeName.toLowerCase()) {
19583 switch(className) {
19589 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19590 switch(this.viewMode){
19592 this.viewDate = this.moveMonth(this.viewDate, dir);
19596 this.viewDate = this.moveYear(this.viewDate, dir);
19602 var date = new Date();
19603 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19605 this.setValue(this.formatDate(this.date));
19612 if (className.indexOf('disabled') < 0) {
19613 this.viewDate.setUTCDate(1);
19614 if (className.indexOf('month') > -1) {
19615 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19617 var year = parseInt(html, 10) || 0;
19618 this.viewDate.setUTCFullYear(year);
19622 if(this.singleMode){
19623 this.setValue(this.formatDate(this.viewDate));
19634 //Roo.log(className);
19635 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19636 var day = parseInt(html, 10) || 1;
19637 var year = this.viewDate.getUTCFullYear(),
19638 month = this.viewDate.getUTCMonth();
19640 if (className.indexOf('old') > -1) {
19647 } else if (className.indexOf('new') > -1) {
19655 //Roo.log([year,month,day]);
19656 this.date = this.UTCDate(year, month, day,0,0,0,0);
19657 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19659 //Roo.log(this.formatDate(this.date));
19660 this.setValue(this.formatDate(this.date));
19667 setStartDate: function(startDate)
19669 this.startDate = startDate || -Infinity;
19670 if (this.startDate !== -Infinity) {
19671 this.startDate = this.parseDate(this.startDate);
19674 this.updateNavArrows();
19677 setEndDate: function(endDate)
19679 this.endDate = endDate || Infinity;
19680 if (this.endDate !== Infinity) {
19681 this.endDate = this.parseDate(this.endDate);
19684 this.updateNavArrows();
19687 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19689 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19690 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19691 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19693 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19694 return parseInt(d, 10);
19697 this.updateNavArrows();
19700 updateNavArrows: function()
19702 if(this.singleMode){
19706 var d = new Date(this.viewDate),
19707 year = d.getUTCFullYear(),
19708 month = d.getUTCMonth();
19710 Roo.each(this.picker().select('.prev', true).elements, function(v){
19712 switch (this.viewMode) {
19715 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19721 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19728 Roo.each(this.picker().select('.next', true).elements, function(v){
19730 switch (this.viewMode) {
19733 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19739 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19747 moveMonth: function(date, dir)
19752 var new_date = new Date(date.valueOf()),
19753 day = new_date.getUTCDate(),
19754 month = new_date.getUTCMonth(),
19755 mag = Math.abs(dir),
19757 dir = dir > 0 ? 1 : -1;
19760 // If going back one month, make sure month is not current month
19761 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19763 return new_date.getUTCMonth() == month;
19765 // If going forward one month, make sure month is as expected
19766 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19768 return new_date.getUTCMonth() != new_month;
19770 new_month = month + dir;
19771 new_date.setUTCMonth(new_month);
19772 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19773 if (new_month < 0 || new_month > 11) {
19774 new_month = (new_month + 12) % 12;
19777 // For magnitudes >1, move one month at a time...
19778 for (var i=0; i<mag; i++) {
19779 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19780 new_date = this.moveMonth(new_date, dir);
19782 // ...then reset the day, keeping it in the new month
19783 new_month = new_date.getUTCMonth();
19784 new_date.setUTCDate(day);
19786 return new_month != new_date.getUTCMonth();
19789 // Common date-resetting loop -- if date is beyond end of month, make it
19792 new_date.setUTCDate(--day);
19793 new_date.setUTCMonth(new_month);
19798 moveYear: function(date, dir)
19800 return this.moveMonth(date, dir*12);
19803 dateWithinRange: function(date)
19805 return date >= this.startDate && date <= this.endDate;
19811 this.picker().remove();
19814 validateValue : function(value)
19816 if(this.getVisibilityEl().hasClass('hidden')){
19820 if(value.length < 1) {
19821 if(this.allowBlank){
19827 if(value.length < this.minLength){
19830 if(value.length > this.maxLength){
19834 var vt = Roo.form.VTypes;
19835 if(!vt[this.vtype](value, this)){
19839 if(typeof this.validator == "function"){
19840 var msg = this.validator(value);
19846 if(this.regex && !this.regex.test(value)){
19850 if(typeof(this.parseDate(value)) == 'undefined'){
19854 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19858 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19868 this.date = this.viewDate = '';
19870 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19875 Roo.apply(Roo.bootstrap.DateField, {
19886 html: '<i class="fa fa-arrow-left"/>'
19896 html: '<i class="fa fa-arrow-right"/>'
19938 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19939 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19940 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19941 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19942 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19955 navFnc: 'FullYear',
19960 navFnc: 'FullYear',
19965 Roo.apply(Roo.bootstrap.DateField, {
19969 cls: 'datepicker dropdown-menu roo-dynamic',
19973 cls: 'datepicker-days',
19977 cls: 'table-condensed',
19979 Roo.bootstrap.DateField.head,
19983 Roo.bootstrap.DateField.footer
19990 cls: 'datepicker-months',
19994 cls: 'table-condensed',
19996 Roo.bootstrap.DateField.head,
19997 Roo.bootstrap.DateField.content,
19998 Roo.bootstrap.DateField.footer
20005 cls: 'datepicker-years',
20009 cls: 'table-condensed',
20011 Roo.bootstrap.DateField.head,
20012 Roo.bootstrap.DateField.content,
20013 Roo.bootstrap.DateField.footer
20032 * @class Roo.bootstrap.TimeField
20033 * @extends Roo.bootstrap.Input
20034 * Bootstrap DateField class
20038 * Create a new TimeField
20039 * @param {Object} config The config object
20042 Roo.bootstrap.TimeField = function(config){
20043 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
20047 * Fires when this field show.
20048 * @param {Roo.bootstrap.DateField} thisthis
20049 * @param {Mixed} date The date value
20054 * Fires when this field hide.
20055 * @param {Roo.bootstrap.DateField} this
20056 * @param {Mixed} date The date value
20061 * Fires when select a date.
20062 * @param {Roo.bootstrap.DateField} this
20063 * @param {Mixed} date The date value
20069 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
20072 * @cfg {String} format
20073 * The default time format string which can be overriden for localization support. The format must be
20074 * valid according to {@link Date#parseDate} (defaults to 'H:i').
20078 onRender: function(ct, position)
20081 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20083 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20085 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20087 this.pop = this.picker().select('>.datepicker-time',true).first();
20088 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20090 this.picker().on('mousedown', this.onMousedown, this);
20091 this.picker().on('click', this.onClick, this);
20093 this.picker().addClass('datepicker-dropdown');
20098 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20099 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20100 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20101 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20102 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20103 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20107 fireKey: function(e){
20108 if (!this.picker().isVisible()){
20109 if (e.keyCode == 27) { // allow escape to hide and re-show picker
20115 e.preventDefault();
20123 this.onTogglePeriod();
20126 this.onIncrementMinutes();
20129 this.onDecrementMinutes();
20138 onClick: function(e) {
20139 e.stopPropagation();
20140 e.preventDefault();
20143 picker : function()
20145 return this.el.select('.datepicker', true).first();
20148 fillTime: function()
20150 var time = this.pop.select('tbody', true).first();
20152 time.dom.innerHTML = '';
20167 cls: 'hours-up glyphicon glyphicon-chevron-up'
20187 cls: 'minutes-up glyphicon glyphicon-chevron-up'
20208 cls: 'timepicker-hour',
20223 cls: 'timepicker-minute',
20238 cls: 'btn btn-primary period',
20260 cls: 'hours-down glyphicon glyphicon-chevron-down'
20280 cls: 'minutes-down glyphicon glyphicon-chevron-down'
20298 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20305 var hours = this.time.getHours();
20306 var minutes = this.time.getMinutes();
20319 hours = hours - 12;
20323 hours = '0' + hours;
20327 minutes = '0' + minutes;
20330 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20331 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20332 this.pop.select('button', true).first().dom.innerHTML = period;
20338 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20340 var cls = ['bottom'];
20342 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20349 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20354 this.picker().addClass(cls.join('-'));
20358 Roo.each(cls, function(c){
20360 _this.picker().setTop(_this.inputEl().getHeight());
20364 _this.picker().setTop(0 - _this.picker().getHeight());
20369 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20373 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20380 onFocus : function()
20382 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20386 onBlur : function()
20388 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20394 this.picker().show();
20399 this.fireEvent('show', this, this.date);
20404 this.picker().hide();
20407 this.fireEvent('hide', this, this.date);
20410 setTime : function()
20413 this.setValue(this.time.format(this.format));
20415 this.fireEvent('select', this, this.date);
20420 onMousedown: function(e){
20421 e.stopPropagation();
20422 e.preventDefault();
20425 onIncrementHours: function()
20427 Roo.log('onIncrementHours');
20428 this.time = this.time.add(Date.HOUR, 1);
20433 onDecrementHours: function()
20435 Roo.log('onDecrementHours');
20436 this.time = this.time.add(Date.HOUR, -1);
20440 onIncrementMinutes: function()
20442 Roo.log('onIncrementMinutes');
20443 this.time = this.time.add(Date.MINUTE, 1);
20447 onDecrementMinutes: function()
20449 Roo.log('onDecrementMinutes');
20450 this.time = this.time.add(Date.MINUTE, -1);
20454 onTogglePeriod: function()
20456 Roo.log('onTogglePeriod');
20457 this.time = this.time.add(Date.HOUR, 12);
20464 Roo.apply(Roo.bootstrap.TimeField, {
20494 cls: 'btn btn-info ok',
20506 Roo.apply(Roo.bootstrap.TimeField, {
20510 cls: 'datepicker dropdown-menu',
20514 cls: 'datepicker-time',
20518 cls: 'table-condensed',
20520 Roo.bootstrap.TimeField.content,
20521 Roo.bootstrap.TimeField.footer
20540 * @class Roo.bootstrap.MonthField
20541 * @extends Roo.bootstrap.Input
20542 * Bootstrap MonthField class
20544 * @cfg {String} language default en
20547 * Create a new MonthField
20548 * @param {Object} config The config object
20551 Roo.bootstrap.MonthField = function(config){
20552 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20557 * Fires when this field show.
20558 * @param {Roo.bootstrap.MonthField} this
20559 * @param {Mixed} date The date value
20564 * Fires when this field hide.
20565 * @param {Roo.bootstrap.MonthField} this
20566 * @param {Mixed} date The date value
20571 * Fires when select a date.
20572 * @param {Roo.bootstrap.MonthField} this
20573 * @param {String} oldvalue The old value
20574 * @param {String} newvalue The new value
20580 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20582 onRender: function(ct, position)
20585 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20587 this.language = this.language || 'en';
20588 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20589 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20591 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20592 this.isInline = false;
20593 this.isInput = true;
20594 this.component = this.el.select('.add-on', true).first() || false;
20595 this.component = (this.component && this.component.length === 0) ? false : this.component;
20596 this.hasInput = this.component && this.inputEL().length;
20598 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20600 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20602 this.picker().on('mousedown', this.onMousedown, this);
20603 this.picker().on('click', this.onClick, this);
20605 this.picker().addClass('datepicker-dropdown');
20607 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20608 v.setStyle('width', '189px');
20615 if(this.isInline) {
20621 setValue: function(v, suppressEvent)
20623 var o = this.getValue();
20625 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20629 if(suppressEvent !== true){
20630 this.fireEvent('select', this, o, v);
20635 getValue: function()
20640 onClick: function(e)
20642 e.stopPropagation();
20643 e.preventDefault();
20645 var target = e.getTarget();
20647 if(target.nodeName.toLowerCase() === 'i'){
20648 target = Roo.get(target).dom.parentNode;
20651 var nodeName = target.nodeName;
20652 var className = target.className;
20653 var html = target.innerHTML;
20655 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20659 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20661 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20667 picker : function()
20669 return this.pickerEl;
20672 fillMonths: function()
20675 var months = this.picker().select('>.datepicker-months td', true).first();
20677 months.dom.innerHTML = '';
20683 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20686 months.createChild(month);
20695 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20696 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20699 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20700 e.removeClass('active');
20702 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20703 e.addClass('active');
20710 if(this.isInline) {
20714 this.picker().removeClass(['bottom', 'top']);
20716 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20718 * place to the top of element!
20722 this.picker().addClass('top');
20723 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20728 this.picker().addClass('bottom');
20730 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20733 onFocus : function()
20735 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20739 onBlur : function()
20741 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20743 var d = this.inputEl().getValue();
20752 this.picker().show();
20753 this.picker().select('>.datepicker-months', true).first().show();
20757 this.fireEvent('show', this, this.date);
20762 if(this.isInline) {
20765 this.picker().hide();
20766 this.fireEvent('hide', this, this.date);
20770 onMousedown: function(e)
20772 e.stopPropagation();
20773 e.preventDefault();
20778 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20782 fireKey: function(e)
20784 if (!this.picker().isVisible()){
20785 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20796 e.preventDefault();
20800 dir = e.keyCode == 37 ? -1 : 1;
20802 this.vIndex = this.vIndex + dir;
20804 if(this.vIndex < 0){
20808 if(this.vIndex > 11){
20812 if(isNaN(this.vIndex)){
20816 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20822 dir = e.keyCode == 38 ? -1 : 1;
20824 this.vIndex = this.vIndex + dir * 4;
20826 if(this.vIndex < 0){
20830 if(this.vIndex > 11){
20834 if(isNaN(this.vIndex)){
20838 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20843 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20844 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20848 e.preventDefault();
20851 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20852 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20868 this.picker().remove();
20873 Roo.apply(Roo.bootstrap.MonthField, {
20892 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20893 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20898 Roo.apply(Roo.bootstrap.MonthField, {
20902 cls: 'datepicker dropdown-menu roo-dynamic',
20906 cls: 'datepicker-months',
20910 cls: 'table-condensed',
20912 Roo.bootstrap.DateField.content
20932 * @class Roo.bootstrap.CheckBox
20933 * @extends Roo.bootstrap.Input
20934 * Bootstrap CheckBox class
20936 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20937 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20938 * @cfg {String} boxLabel The text that appears beside the checkbox
20939 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20940 * @cfg {Boolean} checked initnal the element
20941 * @cfg {Boolean} inline inline the element (default false)
20942 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20943 * @cfg {String} tooltip label tooltip
20946 * Create a new CheckBox
20947 * @param {Object} config The config object
20950 Roo.bootstrap.CheckBox = function(config){
20951 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20956 * Fires when the element is checked or unchecked.
20957 * @param {Roo.bootstrap.CheckBox} this This input
20958 * @param {Boolean} checked The new checked value
20963 * Fires when the element is click.
20964 * @param {Roo.bootstrap.CheckBox} this This input
20971 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20973 inputType: 'checkbox',
20982 getAutoCreate : function()
20984 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20990 cfg.cls = 'form-group ' + this.inputType; //input-group
20993 cfg.cls += ' ' + this.inputType + '-inline';
20999 type : this.inputType,
21000 value : this.inputValue,
21001 cls : 'roo-' + this.inputType, //'form-box',
21002 placeholder : this.placeholder || ''
21006 if(this.inputType != 'radio'){
21010 cls : 'roo-hidden-value',
21011 value : this.checked ? this.inputValue : this.valueOff
21016 if (this.weight) { // Validity check?
21017 cfg.cls += " " + this.inputType + "-" + this.weight;
21020 if (this.disabled) {
21021 input.disabled=true;
21025 input.checked = this.checked;
21030 input.name = this.name;
21032 if(this.inputType != 'radio'){
21033 hidden.name = this.name;
21034 input.name = '_hidden_' + this.name;
21039 input.cls += ' input-' + this.size;
21044 ['xs','sm','md','lg'].map(function(size){
21045 if (settings[size]) {
21046 cfg.cls += ' col-' + size + '-' + settings[size];
21050 var inputblock = input;
21052 if (this.before || this.after) {
21055 cls : 'input-group',
21060 inputblock.cn.push({
21062 cls : 'input-group-addon',
21067 inputblock.cn.push(input);
21069 if(this.inputType != 'radio'){
21070 inputblock.cn.push(hidden);
21074 inputblock.cn.push({
21076 cls : 'input-group-addon',
21083 if (align ==='left' && this.fieldLabel.length) {
21084 // Roo.log("left and has label");
21089 cls : 'control-label',
21090 html : this.fieldLabel
21100 if(this.labelWidth > 12){
21101 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21104 if(this.labelWidth < 13 && this.labelmd == 0){
21105 this.labelmd = this.labelWidth;
21108 if(this.labellg > 0){
21109 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21110 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21113 if(this.labelmd > 0){
21114 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21115 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21118 if(this.labelsm > 0){
21119 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21120 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21123 if(this.labelxs > 0){
21124 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21125 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21128 } else if ( this.fieldLabel.length) {
21129 // Roo.log(" label");
21133 tag: this.boxLabel ? 'span' : 'label',
21135 cls: 'control-label box-input-label',
21136 //cls : 'input-group-addon',
21137 html : this.fieldLabel
21146 // Roo.log(" no label && no align");
21147 cfg.cn = [ inputblock ] ;
21153 var boxLabelCfg = {
21155 //'for': id, // box label is handled by onclick - so no for...
21157 html: this.boxLabel
21161 boxLabelCfg.tooltip = this.tooltip;
21164 cfg.cn.push(boxLabelCfg);
21167 if(this.inputType != 'radio'){
21168 cfg.cn.push(hidden);
21176 * return the real input element.
21178 inputEl: function ()
21180 return this.el.select('input.roo-' + this.inputType,true).first();
21182 hiddenEl: function ()
21184 return this.el.select('input.roo-hidden-value',true).first();
21187 labelEl: function()
21189 return this.el.select('label.control-label',true).first();
21191 /* depricated... */
21195 return this.labelEl();
21198 boxLabelEl: function()
21200 return this.el.select('label.box-label',true).first();
21203 initEvents : function()
21205 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21207 this.inputEl().on('click', this.onClick, this);
21209 if (this.boxLabel) {
21210 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
21213 this.startValue = this.getValue();
21216 Roo.bootstrap.CheckBox.register(this);
21220 onClick : function(e)
21222 if(this.fireEvent('click', this, e) !== false){
21223 this.setChecked(!this.checked);
21228 setChecked : function(state,suppressEvent)
21230 this.startValue = this.getValue();
21232 if(this.inputType == 'radio'){
21234 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21235 e.dom.checked = false;
21238 this.inputEl().dom.checked = true;
21240 this.inputEl().dom.value = this.inputValue;
21242 if(suppressEvent !== true){
21243 this.fireEvent('check', this, true);
21251 this.checked = state;
21253 this.inputEl().dom.checked = state;
21256 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21258 if(suppressEvent !== true){
21259 this.fireEvent('check', this, state);
21265 getValue : function()
21267 if(this.inputType == 'radio'){
21268 return this.getGroupValue();
21271 return this.hiddenEl().dom.value;
21275 getGroupValue : function()
21277 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21281 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21284 setValue : function(v,suppressEvent)
21286 if(this.inputType == 'radio'){
21287 this.setGroupValue(v, suppressEvent);
21291 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21296 setGroupValue : function(v, suppressEvent)
21298 this.startValue = this.getValue();
21300 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21301 e.dom.checked = false;
21303 if(e.dom.value == v){
21304 e.dom.checked = true;
21308 if(suppressEvent !== true){
21309 this.fireEvent('check', this, true);
21317 validate : function()
21319 if(this.getVisibilityEl().hasClass('hidden')){
21325 (this.inputType == 'radio' && this.validateRadio()) ||
21326 (this.inputType == 'checkbox' && this.validateCheckbox())
21332 this.markInvalid();
21336 validateRadio : function()
21338 if(this.getVisibilityEl().hasClass('hidden')){
21342 if(this.allowBlank){
21348 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21349 if(!e.dom.checked){
21361 validateCheckbox : function()
21364 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21365 //return (this.getValue() == this.inputValue) ? true : false;
21368 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21376 for(var i in group){
21377 if(group[i].el.isVisible(true)){
21385 for(var i in group){
21390 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21397 * Mark this field as valid
21399 markValid : function()
21403 this.fireEvent('valid', this);
21405 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21408 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21415 if(this.inputType == 'radio'){
21416 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21417 var fg = e.findParent('.form-group', false, true);
21418 if (Roo.bootstrap.version == 3) {
21419 fg.removeClass([_this.invalidClass, _this.validClass]);
21420 fg.addClass(_this.validClass);
21422 fg.removeClass(['is-valid', 'is-invalid']);
21423 fg.addClass('is-valid');
21431 var fg = this.el.findParent('.form-group', false, true);
21432 if (Roo.bootstrap.version == 3) {
21433 fg.removeClass([this.invalidClass, this.validClass]);
21434 fg.addClass(this.validClass);
21436 fg.removeClass(['is-valid', 'is-invalid']);
21437 fg.addClass('is-valid');
21442 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21448 for(var i in group){
21449 var fg = group[i].el.findParent('.form-group', false, true);
21450 if (Roo.bootstrap.version == 3) {
21451 fg.removeClass([this.invalidClass, this.validClass]);
21452 fg.addClass(this.validClass);
21454 fg.removeClass(['is-valid', 'is-invalid']);
21455 fg.addClass('is-valid');
21461 * Mark this field as invalid
21462 * @param {String} msg The validation message
21464 markInvalid : function(msg)
21466 if(this.allowBlank){
21472 this.fireEvent('invalid', this, msg);
21474 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21477 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21481 label.markInvalid();
21484 if(this.inputType == 'radio'){
21486 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21487 var fg = e.findParent('.form-group', false, true);
21488 if (Roo.bootstrap.version == 3) {
21489 fg.removeClass([_this.invalidClass, _this.validClass]);
21490 fg.addClass(_this.invalidClass);
21492 fg.removeClass(['is-invalid', 'is-valid']);
21493 fg.addClass('is-invalid');
21501 var fg = this.el.findParent('.form-group', false, true);
21502 if (Roo.bootstrap.version == 3) {
21503 fg.removeClass([_this.invalidClass, _this.validClass]);
21504 fg.addClass(_this.invalidClass);
21506 fg.removeClass(['is-invalid', 'is-valid']);
21507 fg.addClass('is-invalid');
21512 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21518 for(var i in group){
21519 var fg = group[i].el.findParent('.form-group', false, true);
21520 if (Roo.bootstrap.version == 3) {
21521 fg.removeClass([_this.invalidClass, _this.validClass]);
21522 fg.addClass(_this.invalidClass);
21524 fg.removeClass(['is-invalid', 'is-valid']);
21525 fg.addClass('is-invalid');
21531 clearInvalid : function()
21533 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21535 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21537 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21539 if (label && label.iconEl) {
21540 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21541 label.iconEl.removeClass(['is-invalid', 'is-valid']);
21545 disable : function()
21547 if(this.inputType != 'radio'){
21548 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21555 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21556 _this.getActionEl().addClass(this.disabledClass);
21557 e.dom.disabled = true;
21561 this.disabled = true;
21562 this.fireEvent("disable", this);
21566 enable : function()
21568 if(this.inputType != 'radio'){
21569 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21576 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21577 _this.getActionEl().removeClass(this.disabledClass);
21578 e.dom.disabled = false;
21582 this.disabled = false;
21583 this.fireEvent("enable", this);
21587 setBoxLabel : function(v)
21592 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21598 Roo.apply(Roo.bootstrap.CheckBox, {
21603 * register a CheckBox Group
21604 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21606 register : function(checkbox)
21608 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21609 this.groups[checkbox.groupId] = {};
21612 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21616 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21620 * fetch a CheckBox Group based on the group ID
21621 * @param {string} the group ID
21622 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21624 get: function(groupId) {
21625 if (typeof(this.groups[groupId]) == 'undefined') {
21629 return this.groups[groupId] ;
21642 * @class Roo.bootstrap.Radio
21643 * @extends Roo.bootstrap.Component
21644 * Bootstrap Radio class
21645 * @cfg {String} boxLabel - the label associated
21646 * @cfg {String} value - the value of radio
21649 * Create a new Radio
21650 * @param {Object} config The config object
21652 Roo.bootstrap.Radio = function(config){
21653 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21657 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21663 getAutoCreate : function()
21667 cls : 'form-group radio',
21672 html : this.boxLabel
21680 initEvents : function()
21682 this.parent().register(this);
21684 this.el.on('click', this.onClick, this);
21688 onClick : function(e)
21690 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21691 this.setChecked(true);
21695 setChecked : function(state, suppressEvent)
21697 this.parent().setValue(this.value, suppressEvent);
21701 setBoxLabel : function(v)
21706 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21721 * @class Roo.bootstrap.SecurePass
21722 * @extends Roo.bootstrap.Input
21723 * Bootstrap SecurePass class
21727 * Create a new SecurePass
21728 * @param {Object} config The config object
21731 Roo.bootstrap.SecurePass = function (config) {
21732 // these go here, so the translation tool can replace them..
21734 PwdEmpty: "Please type a password, and then retype it to confirm.",
21735 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21736 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21737 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21738 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21739 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21740 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21741 TooWeak: "Your password is Too Weak."
21743 this.meterLabel = "Password strength:";
21744 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21745 this.meterClass = [
21746 "roo-password-meter-tooweak",
21747 "roo-password-meter-weak",
21748 "roo-password-meter-medium",
21749 "roo-password-meter-strong",
21750 "roo-password-meter-grey"
21755 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21758 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21760 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21762 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21763 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21764 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21765 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21766 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21767 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21768 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21778 * @cfg {String/Object} Label for the strength meter (defaults to
21779 * 'Password strength:')
21784 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21785 * ['Weak', 'Medium', 'Strong'])
21788 pwdStrengths: false,
21801 initEvents: function ()
21803 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21805 if (this.el.is('input[type=password]') && Roo.isSafari) {
21806 this.el.on('keydown', this.SafariOnKeyDown, this);
21809 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21812 onRender: function (ct, position)
21814 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21815 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21816 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21818 this.trigger.createChild({
21823 cls: 'roo-password-meter-grey col-xs-12',
21826 //width: this.meterWidth + 'px'
21830 cls: 'roo-password-meter-text'
21836 if (this.hideTrigger) {
21837 this.trigger.setDisplayed(false);
21839 this.setSize(this.width || '', this.height || '');
21842 onDestroy: function ()
21844 if (this.trigger) {
21845 this.trigger.removeAllListeners();
21846 this.trigger.remove();
21849 this.wrap.remove();
21851 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21854 checkStrength: function ()
21856 var pwd = this.inputEl().getValue();
21857 if (pwd == this._lastPwd) {
21862 if (this.ClientSideStrongPassword(pwd)) {
21864 } else if (this.ClientSideMediumPassword(pwd)) {
21866 } else if (this.ClientSideWeakPassword(pwd)) {
21872 Roo.log('strength1: ' + strength);
21874 //var pm = this.trigger.child('div/div/div').dom;
21875 var pm = this.trigger.child('div/div');
21876 pm.removeClass(this.meterClass);
21877 pm.addClass(this.meterClass[strength]);
21880 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21882 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21884 this._lastPwd = pwd;
21888 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21890 this._lastPwd = '';
21892 var pm = this.trigger.child('div/div');
21893 pm.removeClass(this.meterClass);
21894 pm.addClass('roo-password-meter-grey');
21897 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21900 this.inputEl().dom.type='password';
21903 validateValue: function (value)
21906 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21909 if (value.length == 0) {
21910 if (this.allowBlank) {
21911 this.clearInvalid();
21915 this.markInvalid(this.errors.PwdEmpty);
21916 this.errorMsg = this.errors.PwdEmpty;
21924 if ('[\x21-\x7e]*'.match(value)) {
21925 this.markInvalid(this.errors.PwdBadChar);
21926 this.errorMsg = this.errors.PwdBadChar;
21929 if (value.length < 6) {
21930 this.markInvalid(this.errors.PwdShort);
21931 this.errorMsg = this.errors.PwdShort;
21934 if (value.length > 16) {
21935 this.markInvalid(this.errors.PwdLong);
21936 this.errorMsg = this.errors.PwdLong;
21940 if (this.ClientSideStrongPassword(value)) {
21942 } else if (this.ClientSideMediumPassword(value)) {
21944 } else if (this.ClientSideWeakPassword(value)) {
21951 if (strength < 2) {
21952 //this.markInvalid(this.errors.TooWeak);
21953 this.errorMsg = this.errors.TooWeak;
21958 console.log('strength2: ' + strength);
21960 //var pm = this.trigger.child('div/div/div').dom;
21962 var pm = this.trigger.child('div/div');
21963 pm.removeClass(this.meterClass);
21964 pm.addClass(this.meterClass[strength]);
21966 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21968 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21970 this.errorMsg = '';
21974 CharacterSetChecks: function (type)
21977 this.fResult = false;
21980 isctype: function (character, type)
21983 case this.kCapitalLetter:
21984 if (character >= 'A' && character <= 'Z') {
21989 case this.kSmallLetter:
21990 if (character >= 'a' && character <= 'z') {
21996 if (character >= '0' && character <= '9') {
22001 case this.kPunctuation:
22002 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
22013 IsLongEnough: function (pwd, size)
22015 return !(pwd == null || isNaN(size) || pwd.length < size);
22018 SpansEnoughCharacterSets: function (word, nb)
22020 if (!this.IsLongEnough(word, nb))
22025 var characterSetChecks = new Array(
22026 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
22027 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
22030 for (var index = 0; index < word.length; ++index) {
22031 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22032 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
22033 characterSetChecks[nCharSet].fResult = true;
22040 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
22041 if (characterSetChecks[nCharSet].fResult) {
22046 if (nCharSets < nb) {
22052 ClientSideStrongPassword: function (pwd)
22054 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22057 ClientSideMediumPassword: function (pwd)
22059 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22062 ClientSideWeakPassword: function (pwd)
22064 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22067 })//<script type="text/javascript">
22070 * Based Ext JS Library 1.1.1
22071 * Copyright(c) 2006-2007, Ext JS, LLC.
22077 * @class Roo.HtmlEditorCore
22078 * @extends Roo.Component
22079 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22081 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22084 Roo.HtmlEditorCore = function(config){
22087 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22092 * @event initialize
22093 * Fires when the editor is fully initialized (including the iframe)
22094 * @param {Roo.HtmlEditorCore} this
22099 * Fires when the editor is first receives the focus. Any insertion must wait
22100 * until after this event.
22101 * @param {Roo.HtmlEditorCore} this
22105 * @event beforesync
22106 * Fires before the textarea is updated with content from the editor iframe. Return false
22107 * to cancel the sync.
22108 * @param {Roo.HtmlEditorCore} this
22109 * @param {String} html
22113 * @event beforepush
22114 * Fires before the iframe editor is updated with content from the textarea. Return false
22115 * to cancel the push.
22116 * @param {Roo.HtmlEditorCore} this
22117 * @param {String} html
22122 * Fires when the textarea is updated with content from the editor iframe.
22123 * @param {Roo.HtmlEditorCore} this
22124 * @param {String} html
22129 * Fires when the iframe editor is updated with content from the textarea.
22130 * @param {Roo.HtmlEditorCore} this
22131 * @param {String} html
22136 * @event editorevent
22137 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22138 * @param {Roo.HtmlEditorCore} this
22144 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22146 // defaults : white / black...
22147 this.applyBlacklists();
22154 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
22158 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
22164 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22169 * @cfg {Number} height (in pixels)
22173 * @cfg {Number} width (in pixels)
22178 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22181 stylesheets: false,
22186 // private properties
22187 validationEvent : false,
22189 initialized : false,
22191 sourceEditMode : false,
22192 onFocus : Roo.emptyFn,
22194 hideMode:'offsets',
22198 // blacklist + whitelisted elements..
22205 * Protected method that will not generally be called directly. It
22206 * is called when the editor initializes the iframe with HTML contents. Override this method if you
22207 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22209 getDocMarkup : function(){
22213 // inherit styels from page...??
22214 if (this.stylesheets === false) {
22216 Roo.get(document.head).select('style').each(function(node) {
22217 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22220 Roo.get(document.head).select('link').each(function(node) {
22221 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22224 } else if (!this.stylesheets.length) {
22226 st = '<style type="text/css">' +
22227 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22230 st = '<style type="text/css">' +
22235 st += '<style type="text/css">' +
22236 'IMG { cursor: pointer } ' +
22239 var cls = 'roo-htmleditor-body';
22241 if(this.bodyCls.length){
22242 cls += ' ' + this.bodyCls;
22245 return '<html><head>' + st +
22246 //<style type="text/css">' +
22247 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22249 ' </head><body class="' + cls + '"></body></html>';
22253 onRender : function(ct, position)
22256 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22257 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22260 this.el.dom.style.border = '0 none';
22261 this.el.dom.setAttribute('tabIndex', -1);
22262 this.el.addClass('x-hidden hide');
22266 if(Roo.isIE){ // fix IE 1px bogus margin
22267 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22271 this.frameId = Roo.id();
22275 var iframe = this.owner.wrap.createChild({
22277 cls: 'form-control', // bootstrap..
22279 name: this.frameId,
22280 frameBorder : 'no',
22281 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
22286 this.iframe = iframe.dom;
22288 this.assignDocWin();
22290 this.doc.designMode = 'on';
22293 this.doc.write(this.getDocMarkup());
22297 var task = { // must defer to wait for browser to be ready
22299 //console.log("run task?" + this.doc.readyState);
22300 this.assignDocWin();
22301 if(this.doc.body || this.doc.readyState == 'complete'){
22303 this.doc.designMode="on";
22307 Roo.TaskMgr.stop(task);
22308 this.initEditor.defer(10, this);
22315 Roo.TaskMgr.start(task);
22320 onResize : function(w, h)
22322 Roo.log('resize: ' +w + ',' + h );
22323 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22327 if(typeof w == 'number'){
22329 this.iframe.style.width = w + 'px';
22331 if(typeof h == 'number'){
22333 this.iframe.style.height = h + 'px';
22335 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22342 * Toggles the editor between standard and source edit mode.
22343 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22345 toggleSourceEdit : function(sourceEditMode){
22347 this.sourceEditMode = sourceEditMode === true;
22349 if(this.sourceEditMode){
22351 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
22354 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22355 //this.iframe.className = '';
22358 //this.setSize(this.owner.wrap.getSize());
22359 //this.fireEvent('editmodechange', this, this.sourceEditMode);
22366 * Protected method that will not generally be called directly. If you need/want
22367 * custom HTML cleanup, this is the method you should override.
22368 * @param {String} html The HTML to be cleaned
22369 * return {String} The cleaned HTML
22371 cleanHtml : function(html){
22372 html = String(html);
22373 if(html.length > 5){
22374 if(Roo.isSafari){ // strip safari nonsense
22375 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22378 if(html == ' '){
22385 * HTML Editor -> Textarea
22386 * Protected method that will not generally be called directly. Syncs the contents
22387 * of the editor iframe with the textarea.
22389 syncValue : function(){
22390 if(this.initialized){
22391 var bd = (this.doc.body || this.doc.documentElement);
22392 //this.cleanUpPaste(); -- this is done else where and causes havoc..
22393 var html = bd.innerHTML;
22395 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22396 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22398 html = '<div style="'+m[0]+'">' + html + '</div>';
22401 html = this.cleanHtml(html);
22402 // fix up the special chars.. normaly like back quotes in word...
22403 // however we do not want to do this with chinese..
22404 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22405 var cc = b.charCodeAt();
22407 (cc >= 0x4E00 && cc < 0xA000 ) ||
22408 (cc >= 0x3400 && cc < 0x4E00 ) ||
22409 (cc >= 0xf900 && cc < 0xfb00 )
22415 if(this.owner.fireEvent('beforesync', this, html) !== false){
22416 this.el.dom.value = html;
22417 this.owner.fireEvent('sync', this, html);
22423 * Protected method that will not generally be called directly. Pushes the value of the textarea
22424 * into the iframe editor.
22426 pushValue : function(){
22427 if(this.initialized){
22428 var v = this.el.dom.value.trim();
22430 // if(v.length < 1){
22434 if(this.owner.fireEvent('beforepush', this, v) !== false){
22435 var d = (this.doc.body || this.doc.documentElement);
22437 this.cleanUpPaste();
22438 this.el.dom.value = d.innerHTML;
22439 this.owner.fireEvent('push', this, v);
22445 deferFocus : function(){
22446 this.focus.defer(10, this);
22450 focus : function(){
22451 if(this.win && !this.sourceEditMode){
22458 assignDocWin: function()
22460 var iframe = this.iframe;
22463 this.doc = iframe.contentWindow.document;
22464 this.win = iframe.contentWindow;
22466 // if (!Roo.get(this.frameId)) {
22469 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22470 // this.win = Roo.get(this.frameId).dom.contentWindow;
22472 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22476 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22477 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22482 initEditor : function(){
22483 //console.log("INIT EDITOR");
22484 this.assignDocWin();
22488 this.doc.designMode="on";
22490 this.doc.write(this.getDocMarkup());
22493 var dbody = (this.doc.body || this.doc.documentElement);
22494 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22495 // this copies styles from the containing element into thsi one..
22496 // not sure why we need all of this..
22497 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22499 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22500 //ss['background-attachment'] = 'fixed'; // w3c
22501 dbody.bgProperties = 'fixed'; // ie
22502 //Roo.DomHelper.applyStyles(dbody, ss);
22503 Roo.EventManager.on(this.doc, {
22504 //'mousedown': this.onEditorEvent,
22505 'mouseup': this.onEditorEvent,
22506 'dblclick': this.onEditorEvent,
22507 'click': this.onEditorEvent,
22508 'keyup': this.onEditorEvent,
22513 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22515 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22516 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22518 this.initialized = true;
22520 this.owner.fireEvent('initialize', this);
22525 onDestroy : function(){
22531 //for (var i =0; i < this.toolbars.length;i++) {
22532 // // fixme - ask toolbars for heights?
22533 // this.toolbars[i].onDestroy();
22536 //this.wrap.dom.innerHTML = '';
22537 //this.wrap.remove();
22542 onFirstFocus : function(){
22544 this.assignDocWin();
22547 this.activated = true;
22550 if(Roo.isGecko){ // prevent silly gecko errors
22552 var s = this.win.getSelection();
22553 if(!s.focusNode || s.focusNode.nodeType != 3){
22554 var r = s.getRangeAt(0);
22555 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22560 this.execCmd('useCSS', true);
22561 this.execCmd('styleWithCSS', false);
22564 this.owner.fireEvent('activate', this);
22568 adjustFont: function(btn){
22569 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22570 //if(Roo.isSafari){ // safari
22573 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22574 if(Roo.isSafari){ // safari
22575 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22576 v = (v < 10) ? 10 : v;
22577 v = (v > 48) ? 48 : v;
22578 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22583 v = Math.max(1, v+adjust);
22585 this.execCmd('FontSize', v );
22588 onEditorEvent : function(e)
22590 this.owner.fireEvent('editorevent', this, e);
22591 // this.updateToolbar();
22592 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22595 insertTag : function(tg)
22597 // could be a bit smarter... -> wrap the current selected tRoo..
22598 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22600 range = this.createRange(this.getSelection());
22601 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22602 wrappingNode.appendChild(range.extractContents());
22603 range.insertNode(wrappingNode);
22610 this.execCmd("formatblock", tg);
22614 insertText : function(txt)
22618 var range = this.createRange();
22619 range.deleteContents();
22620 //alert(Sender.getAttribute('label'));
22622 range.insertNode(this.doc.createTextNode(txt));
22628 * Executes a Midas editor command on the editor document and performs necessary focus and
22629 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22630 * @param {String} cmd The Midas command
22631 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22633 relayCmd : function(cmd, value){
22635 this.execCmd(cmd, value);
22636 this.owner.fireEvent('editorevent', this);
22637 //this.updateToolbar();
22638 this.owner.deferFocus();
22642 * Executes a Midas editor command directly on the editor document.
22643 * For visual commands, you should use {@link #relayCmd} instead.
22644 * <b>This should only be called after the editor is initialized.</b>
22645 * @param {String} cmd The Midas command
22646 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22648 execCmd : function(cmd, value){
22649 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22656 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22658 * @param {String} text | dom node..
22660 insertAtCursor : function(text)
22663 if(!this.activated){
22669 var r = this.doc.selection.createRange();
22680 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22684 // from jquery ui (MIT licenced)
22686 var win = this.win;
22688 if (win.getSelection && win.getSelection().getRangeAt) {
22689 range = win.getSelection().getRangeAt(0);
22690 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22691 range.insertNode(node);
22692 } else if (win.document.selection && win.document.selection.createRange) {
22693 // no firefox support
22694 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22695 win.document.selection.createRange().pasteHTML(txt);
22697 // no firefox support
22698 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22699 this.execCmd('InsertHTML', txt);
22708 mozKeyPress : function(e){
22710 var c = e.getCharCode(), cmd;
22713 c = String.fromCharCode(c).toLowerCase();
22727 this.cleanUpPaste.defer(100, this);
22735 e.preventDefault();
22743 fixKeys : function(){ // load time branching for fastest keydown performance
22745 return function(e){
22746 var k = e.getKey(), r;
22749 r = this.doc.selection.createRange();
22752 r.pasteHTML('    ');
22759 r = this.doc.selection.createRange();
22761 var target = r.parentElement();
22762 if(!target || target.tagName.toLowerCase() != 'li'){
22764 r.pasteHTML('<br />');
22770 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22771 this.cleanUpPaste.defer(100, this);
22777 }else if(Roo.isOpera){
22778 return function(e){
22779 var k = e.getKey();
22783 this.execCmd('InsertHTML','    ');
22786 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22787 this.cleanUpPaste.defer(100, this);
22792 }else if(Roo.isSafari){
22793 return function(e){
22794 var k = e.getKey();
22798 this.execCmd('InsertText','\t');
22802 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22803 this.cleanUpPaste.defer(100, this);
22811 getAllAncestors: function()
22813 var p = this.getSelectedNode();
22816 a.push(p); // push blank onto stack..
22817 p = this.getParentElement();
22821 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22825 a.push(this.doc.body);
22829 lastSelNode : false,
22832 getSelection : function()
22834 this.assignDocWin();
22835 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22838 getSelectedNode: function()
22840 // this may only work on Gecko!!!
22842 // should we cache this!!!!
22847 var range = this.createRange(this.getSelection()).cloneRange();
22850 var parent = range.parentElement();
22852 var testRange = range.duplicate();
22853 testRange.moveToElementText(parent);
22854 if (testRange.inRange(range)) {
22857 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22860 parent = parent.parentElement;
22865 // is ancestor a text element.
22866 var ac = range.commonAncestorContainer;
22867 if (ac.nodeType == 3) {
22868 ac = ac.parentNode;
22871 var ar = ac.childNodes;
22874 var other_nodes = [];
22875 var has_other_nodes = false;
22876 for (var i=0;i<ar.length;i++) {
22877 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22880 // fullly contained node.
22882 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22887 // probably selected..
22888 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22889 other_nodes.push(ar[i]);
22893 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22898 has_other_nodes = true;
22900 if (!nodes.length && other_nodes.length) {
22901 nodes= other_nodes;
22903 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22909 createRange: function(sel)
22911 // this has strange effects when using with
22912 // top toolbar - not sure if it's a great idea.
22913 //this.editor.contentWindow.focus();
22914 if (typeof sel != "undefined") {
22916 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22918 return this.doc.createRange();
22921 return this.doc.createRange();
22924 getParentElement: function()
22927 this.assignDocWin();
22928 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22930 var range = this.createRange(sel);
22933 var p = range.commonAncestorContainer;
22934 while (p.nodeType == 3) { // text node
22945 * Range intersection.. the hard stuff...
22949 * [ -- selected range --- ]
22953 * if end is before start or hits it. fail.
22954 * if start is after end or hits it fail.
22956 * if either hits (but other is outside. - then it's not
22962 // @see http://www.thismuchiknow.co.uk/?p=64.
22963 rangeIntersectsNode : function(range, node)
22965 var nodeRange = node.ownerDocument.createRange();
22967 nodeRange.selectNode(node);
22969 nodeRange.selectNodeContents(node);
22972 var rangeStartRange = range.cloneRange();
22973 rangeStartRange.collapse(true);
22975 var rangeEndRange = range.cloneRange();
22976 rangeEndRange.collapse(false);
22978 var nodeStartRange = nodeRange.cloneRange();
22979 nodeStartRange.collapse(true);
22981 var nodeEndRange = nodeRange.cloneRange();
22982 nodeEndRange.collapse(false);
22984 return rangeStartRange.compareBoundaryPoints(
22985 Range.START_TO_START, nodeEndRange) == -1 &&
22986 rangeEndRange.compareBoundaryPoints(
22987 Range.START_TO_START, nodeStartRange) == 1;
22991 rangeCompareNode : function(range, node)
22993 var nodeRange = node.ownerDocument.createRange();
22995 nodeRange.selectNode(node);
22997 nodeRange.selectNodeContents(node);
23001 range.collapse(true);
23003 nodeRange.collapse(true);
23005 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
23006 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
23008 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
23010 var nodeIsBefore = ss == 1;
23011 var nodeIsAfter = ee == -1;
23013 if (nodeIsBefore && nodeIsAfter) {
23016 if (!nodeIsBefore && nodeIsAfter) {
23017 return 1; //right trailed.
23020 if (nodeIsBefore && !nodeIsAfter) {
23021 return 2; // left trailed.
23027 // private? - in a new class?
23028 cleanUpPaste : function()
23030 // cleans up the whole document..
23031 Roo.log('cleanuppaste');
23033 this.cleanUpChildren(this.doc.body);
23034 var clean = this.cleanWordChars(this.doc.body.innerHTML);
23035 if (clean != this.doc.body.innerHTML) {
23036 this.doc.body.innerHTML = clean;
23041 cleanWordChars : function(input) {// change the chars to hex code
23042 var he = Roo.HtmlEditorCore;
23044 var output = input;
23045 Roo.each(he.swapCodes, function(sw) {
23046 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23048 output = output.replace(swapper, sw[1]);
23055 cleanUpChildren : function (n)
23057 if (!n.childNodes.length) {
23060 for (var i = n.childNodes.length-1; i > -1 ; i--) {
23061 this.cleanUpChild(n.childNodes[i]);
23068 cleanUpChild : function (node)
23071 //console.log(node);
23072 if (node.nodeName == "#text") {
23073 // clean up silly Windows -- stuff?
23076 if (node.nodeName == "#comment") {
23077 node.parentNode.removeChild(node);
23078 // clean up silly Windows -- stuff?
23081 var lcname = node.tagName.toLowerCase();
23082 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23083 // whitelist of tags..
23085 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23087 node.parentNode.removeChild(node);
23092 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23094 // remove <a name=....> as rendering on yahoo mailer is borked with this.
23095 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23097 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23098 // remove_keep_children = true;
23101 if (remove_keep_children) {
23102 this.cleanUpChildren(node);
23103 // inserts everything just before this node...
23104 while (node.childNodes.length) {
23105 var cn = node.childNodes[0];
23106 node.removeChild(cn);
23107 node.parentNode.insertBefore(cn, node);
23109 node.parentNode.removeChild(node);
23113 if (!node.attributes || !node.attributes.length) {
23114 this.cleanUpChildren(node);
23118 function cleanAttr(n,v)
23121 if (v.match(/^\./) || v.match(/^\//)) {
23124 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23127 if (v.match(/^#/)) {
23130 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23131 node.removeAttribute(n);
23135 var cwhite = this.cwhite;
23136 var cblack = this.cblack;
23138 function cleanStyle(n,v)
23140 if (v.match(/expression/)) { //XSS?? should we even bother..
23141 node.removeAttribute(n);
23145 var parts = v.split(/;/);
23148 Roo.each(parts, function(p) {
23149 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23153 var l = p.split(':').shift().replace(/\s+/g,'');
23154 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23156 if ( cwhite.length && cblack.indexOf(l) > -1) {
23157 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23158 //node.removeAttribute(n);
23162 // only allow 'c whitelisted system attributes'
23163 if ( cwhite.length && cwhite.indexOf(l) < 0) {
23164 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23165 //node.removeAttribute(n);
23175 if (clean.length) {
23176 node.setAttribute(n, clean.join(';'));
23178 node.removeAttribute(n);
23184 for (var i = node.attributes.length-1; i > -1 ; i--) {
23185 var a = node.attributes[i];
23188 if (a.name.toLowerCase().substr(0,2)=='on') {
23189 node.removeAttribute(a.name);
23192 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23193 node.removeAttribute(a.name);
23196 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23197 cleanAttr(a.name,a.value); // fixme..
23200 if (a.name == 'style') {
23201 cleanStyle(a.name,a.value);
23204 /// clean up MS crap..
23205 // tecnically this should be a list of valid class'es..
23208 if (a.name == 'class') {
23209 if (a.value.match(/^Mso/)) {
23210 node.className = '';
23213 if (a.value.match(/^body$/)) {
23214 node.className = '';
23225 this.cleanUpChildren(node);
23231 * Clean up MS wordisms...
23233 cleanWord : function(node)
23238 this.cleanWord(this.doc.body);
23241 if (node.nodeName == "#text") {
23242 // clean up silly Windows -- stuff?
23245 if (node.nodeName == "#comment") {
23246 node.parentNode.removeChild(node);
23247 // clean up silly Windows -- stuff?
23251 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23252 node.parentNode.removeChild(node);
23256 // remove - but keep children..
23257 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23258 while (node.childNodes.length) {
23259 var cn = node.childNodes[0];
23260 node.removeChild(cn);
23261 node.parentNode.insertBefore(cn, node);
23263 node.parentNode.removeChild(node);
23264 this.iterateChildren(node, this.cleanWord);
23268 if (node.className.length) {
23270 var cn = node.className.split(/\W+/);
23272 Roo.each(cn, function(cls) {
23273 if (cls.match(/Mso[a-zA-Z]+/)) {
23278 node.className = cna.length ? cna.join(' ') : '';
23280 node.removeAttribute("class");
23284 if (node.hasAttribute("lang")) {
23285 node.removeAttribute("lang");
23288 if (node.hasAttribute("style")) {
23290 var styles = node.getAttribute("style").split(";");
23292 Roo.each(styles, function(s) {
23293 if (!s.match(/:/)) {
23296 var kv = s.split(":");
23297 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23300 // what ever is left... we allow.
23303 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23304 if (!nstyle.length) {
23305 node.removeAttribute('style');
23308 this.iterateChildren(node, this.cleanWord);
23314 * iterateChildren of a Node, calling fn each time, using this as the scole..
23315 * @param {DomNode} node node to iterate children of.
23316 * @param {Function} fn method of this class to call on each item.
23318 iterateChildren : function(node, fn)
23320 if (!node.childNodes.length) {
23323 for (var i = node.childNodes.length-1; i > -1 ; i--) {
23324 fn.call(this, node.childNodes[i])
23330 * cleanTableWidths.
23332 * Quite often pasting from word etc.. results in tables with column and widths.
23333 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23336 cleanTableWidths : function(node)
23341 this.cleanTableWidths(this.doc.body);
23346 if (node.nodeName == "#text" || node.nodeName == "#comment") {
23349 Roo.log(node.tagName);
23350 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23351 this.iterateChildren(node, this.cleanTableWidths);
23354 if (node.hasAttribute('width')) {
23355 node.removeAttribute('width');
23359 if (node.hasAttribute("style")) {
23362 var styles = node.getAttribute("style").split(";");
23364 Roo.each(styles, function(s) {
23365 if (!s.match(/:/)) {
23368 var kv = s.split(":");
23369 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23372 // what ever is left... we allow.
23375 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23376 if (!nstyle.length) {
23377 node.removeAttribute('style');
23381 this.iterateChildren(node, this.cleanTableWidths);
23389 domToHTML : function(currentElement, depth, nopadtext) {
23391 depth = depth || 0;
23392 nopadtext = nopadtext || false;
23394 if (!currentElement) {
23395 return this.domToHTML(this.doc.body);
23398 //Roo.log(currentElement);
23400 var allText = false;
23401 var nodeName = currentElement.nodeName;
23402 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23404 if (nodeName == '#text') {
23406 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23411 if (nodeName != 'BODY') {
23414 // Prints the node tagName, such as <A>, <IMG>, etc
23417 for(i = 0; i < currentElement.attributes.length;i++) {
23419 var aname = currentElement.attributes.item(i).name;
23420 if (!currentElement.attributes.item(i).value.length) {
23423 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23426 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23435 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23438 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23443 // Traverse the tree
23445 var currentElementChild = currentElement.childNodes.item(i);
23446 var allText = true;
23447 var innerHTML = '';
23449 while (currentElementChild) {
23450 // Formatting code (indent the tree so it looks nice on the screen)
23451 var nopad = nopadtext;
23452 if (lastnode == 'SPAN') {
23456 if (currentElementChild.nodeName == '#text') {
23457 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23458 toadd = nopadtext ? toadd : toadd.trim();
23459 if (!nopad && toadd.length > 80) {
23460 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23462 innerHTML += toadd;
23465 currentElementChild = currentElement.childNodes.item(i);
23471 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23473 // Recursively traverse the tree structure of the child node
23474 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23475 lastnode = currentElementChild.nodeName;
23477 currentElementChild=currentElement.childNodes.item(i);
23483 // The remaining code is mostly for formatting the tree
23484 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23489 ret+= "</"+tagName+">";
23495 applyBlacklists : function()
23497 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23498 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23502 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23503 if (b.indexOf(tag) > -1) {
23506 this.white.push(tag);
23510 Roo.each(w, function(tag) {
23511 if (b.indexOf(tag) > -1) {
23514 if (this.white.indexOf(tag) > -1) {
23517 this.white.push(tag);
23522 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23523 if (w.indexOf(tag) > -1) {
23526 this.black.push(tag);
23530 Roo.each(b, function(tag) {
23531 if (w.indexOf(tag) > -1) {
23534 if (this.black.indexOf(tag) > -1) {
23537 this.black.push(tag);
23542 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23543 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23547 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23548 if (b.indexOf(tag) > -1) {
23551 this.cwhite.push(tag);
23555 Roo.each(w, function(tag) {
23556 if (b.indexOf(tag) > -1) {
23559 if (this.cwhite.indexOf(tag) > -1) {
23562 this.cwhite.push(tag);
23567 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23568 if (w.indexOf(tag) > -1) {
23571 this.cblack.push(tag);
23575 Roo.each(b, function(tag) {
23576 if (w.indexOf(tag) > -1) {
23579 if (this.cblack.indexOf(tag) > -1) {
23582 this.cblack.push(tag);
23587 setStylesheets : function(stylesheets)
23589 if(typeof(stylesheets) == 'string'){
23590 Roo.get(this.iframe.contentDocument.head).createChild({
23592 rel : 'stylesheet',
23601 Roo.each(stylesheets, function(s) {
23606 Roo.get(_this.iframe.contentDocument.head).createChild({
23608 rel : 'stylesheet',
23617 removeStylesheets : function()
23621 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23626 setStyle : function(style)
23628 Roo.get(this.iframe.contentDocument.head).createChild({
23637 // hide stuff that is not compatible
23651 * @event specialkey
23655 * @cfg {String} fieldClass @hide
23658 * @cfg {String} focusClass @hide
23661 * @cfg {String} autoCreate @hide
23664 * @cfg {String} inputType @hide
23667 * @cfg {String} invalidClass @hide
23670 * @cfg {String} invalidText @hide
23673 * @cfg {String} msgFx @hide
23676 * @cfg {String} validateOnBlur @hide
23680 Roo.HtmlEditorCore.white = [
23681 'area', 'br', 'img', 'input', 'hr', 'wbr',
23683 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23684 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23685 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23686 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23687 'table', 'ul', 'xmp',
23689 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23692 'dir', 'menu', 'ol', 'ul', 'dl',
23698 Roo.HtmlEditorCore.black = [
23699 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23701 'base', 'basefont', 'bgsound', 'blink', 'body',
23702 'frame', 'frameset', 'head', 'html', 'ilayer',
23703 'iframe', 'layer', 'link', 'meta', 'object',
23704 'script', 'style' ,'title', 'xml' // clean later..
23706 Roo.HtmlEditorCore.clean = [
23707 'script', 'style', 'title', 'xml'
23709 Roo.HtmlEditorCore.remove = [
23714 Roo.HtmlEditorCore.ablack = [
23718 Roo.HtmlEditorCore.aclean = [
23719 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23723 Roo.HtmlEditorCore.pwhite= [
23724 'http', 'https', 'mailto'
23727 // white listed style attributes.
23728 Roo.HtmlEditorCore.cwhite= [
23729 // 'text-align', /// default is to allow most things..
23735 // black listed style attributes.
23736 Roo.HtmlEditorCore.cblack= [
23737 // 'font-size' -- this can be set by the project
23741 Roo.HtmlEditorCore.swapCodes =[
23760 * @class Roo.bootstrap.HtmlEditor
23761 * @extends Roo.bootstrap.TextArea
23762 * Bootstrap HtmlEditor class
23765 * Create a new HtmlEditor
23766 * @param {Object} config The config object
23769 Roo.bootstrap.HtmlEditor = function(config){
23770 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23771 if (!this.toolbars) {
23772 this.toolbars = [];
23775 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23778 * @event initialize
23779 * Fires when the editor is fully initialized (including the iframe)
23780 * @param {HtmlEditor} this
23785 * Fires when the editor is first receives the focus. Any insertion must wait
23786 * until after this event.
23787 * @param {HtmlEditor} this
23791 * @event beforesync
23792 * Fires before the textarea is updated with content from the editor iframe. Return false
23793 * to cancel the sync.
23794 * @param {HtmlEditor} this
23795 * @param {String} html
23799 * @event beforepush
23800 * Fires before the iframe editor is updated with content from the textarea. Return false
23801 * to cancel the push.
23802 * @param {HtmlEditor} this
23803 * @param {String} html
23808 * Fires when the textarea is updated with content from the editor iframe.
23809 * @param {HtmlEditor} this
23810 * @param {String} html
23815 * Fires when the iframe editor is updated with content from the textarea.
23816 * @param {HtmlEditor} this
23817 * @param {String} html
23821 * @event editmodechange
23822 * Fires when the editor switches edit modes
23823 * @param {HtmlEditor} this
23824 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23826 editmodechange: true,
23828 * @event editorevent
23829 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23830 * @param {HtmlEditor} this
23834 * @event firstfocus
23835 * Fires when on first focus - needed by toolbars..
23836 * @param {HtmlEditor} this
23841 * Auto save the htmlEditor value as a file into Events
23842 * @param {HtmlEditor} this
23846 * @event savedpreview
23847 * preview the saved version of htmlEditor
23848 * @param {HtmlEditor} this
23855 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23859 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23864 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23869 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23874 * @cfg {Number} height (in pixels)
23878 * @cfg {Number} width (in pixels)
23883 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23886 stylesheets: false,
23891 // private properties
23892 validationEvent : false,
23894 initialized : false,
23897 onFocus : Roo.emptyFn,
23899 hideMode:'offsets',
23901 tbContainer : false,
23905 toolbarContainer :function() {
23906 return this.wrap.select('.x-html-editor-tb',true).first();
23910 * Protected method that will not generally be called directly. It
23911 * is called when the editor creates its toolbar. Override this method if you need to
23912 * add custom toolbar buttons.
23913 * @param {HtmlEditor} editor
23915 createToolbar : function(){
23916 Roo.log('renewing');
23917 Roo.log("create toolbars");
23919 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23920 this.toolbars[0].render(this.toolbarContainer());
23924 // if (!editor.toolbars || !editor.toolbars.length) {
23925 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23928 // for (var i =0 ; i < editor.toolbars.length;i++) {
23929 // editor.toolbars[i] = Roo.factory(
23930 // typeof(editor.toolbars[i]) == 'string' ?
23931 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23932 // Roo.bootstrap.HtmlEditor);
23933 // editor.toolbars[i].init(editor);
23939 onRender : function(ct, position)
23941 // Roo.log("Call onRender: " + this.xtype);
23943 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23945 this.wrap = this.inputEl().wrap({
23946 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23949 this.editorcore.onRender(ct, position);
23951 if (this.resizable) {
23952 this.resizeEl = new Roo.Resizable(this.wrap, {
23956 minHeight : this.height,
23957 height: this.height,
23958 handles : this.resizable,
23961 resize : function(r, w, h) {
23962 _t.onResize(w,h); // -something
23968 this.createToolbar(this);
23971 if(!this.width && this.resizable){
23972 this.setSize(this.wrap.getSize());
23974 if (this.resizeEl) {
23975 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23976 // should trigger onReize..
23982 onResize : function(w, h)
23984 Roo.log('resize: ' +w + ',' + h );
23985 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23989 if(this.inputEl() ){
23990 if(typeof w == 'number'){
23991 var aw = w - this.wrap.getFrameWidth('lr');
23992 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23995 if(typeof h == 'number'){
23996 var tbh = -11; // fixme it needs to tool bar size!
23997 for (var i =0; i < this.toolbars.length;i++) {
23998 // fixme - ask toolbars for heights?
23999 tbh += this.toolbars[i].el.getHeight();
24000 //if (this.toolbars[i].footer) {
24001 // tbh += this.toolbars[i].footer.el.getHeight();
24009 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24010 ah -= 5; // knock a few pixes off for look..
24011 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24015 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24016 this.editorcore.onResize(ew,eh);
24021 * Toggles the editor between standard and source edit mode.
24022 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24024 toggleSourceEdit : function(sourceEditMode)
24026 this.editorcore.toggleSourceEdit(sourceEditMode);
24028 if(this.editorcore.sourceEditMode){
24029 Roo.log('editor - showing textarea');
24032 // Roo.log(this.syncValue());
24034 this.inputEl().removeClass(['hide', 'x-hidden']);
24035 this.inputEl().dom.removeAttribute('tabIndex');
24036 this.inputEl().focus();
24038 Roo.log('editor - hiding textarea');
24040 // Roo.log(this.pushValue());
24043 this.inputEl().addClass(['hide', 'x-hidden']);
24044 this.inputEl().dom.setAttribute('tabIndex', -1);
24045 //this.deferFocus();
24048 if(this.resizable){
24049 this.setSize(this.wrap.getSize());
24052 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24055 // private (for BoxComponent)
24056 adjustSize : Roo.BoxComponent.prototype.adjustSize,
24058 // private (for BoxComponent)
24059 getResizeEl : function(){
24063 // private (for BoxComponent)
24064 getPositionEl : function(){
24069 initEvents : function(){
24070 this.originalValue = this.getValue();
24074 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24077 // markInvalid : Roo.emptyFn,
24079 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24082 // clearInvalid : Roo.emptyFn,
24084 setValue : function(v){
24085 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24086 this.editorcore.pushValue();
24091 deferFocus : function(){
24092 this.focus.defer(10, this);
24096 focus : function(){
24097 this.editorcore.focus();
24103 onDestroy : function(){
24109 for (var i =0; i < this.toolbars.length;i++) {
24110 // fixme - ask toolbars for heights?
24111 this.toolbars[i].onDestroy();
24114 this.wrap.dom.innerHTML = '';
24115 this.wrap.remove();
24120 onFirstFocus : function(){
24121 //Roo.log("onFirstFocus");
24122 this.editorcore.onFirstFocus();
24123 for (var i =0; i < this.toolbars.length;i++) {
24124 this.toolbars[i].onFirstFocus();
24130 syncValue : function()
24132 this.editorcore.syncValue();
24135 pushValue : function()
24137 this.editorcore.pushValue();
24141 // hide stuff that is not compatible
24155 * @event specialkey
24159 * @cfg {String} fieldClass @hide
24162 * @cfg {String} focusClass @hide
24165 * @cfg {String} autoCreate @hide
24168 * @cfg {String} inputType @hide
24172 * @cfg {String} invalidText @hide
24175 * @cfg {String} msgFx @hide
24178 * @cfg {String} validateOnBlur @hide
24187 Roo.namespace('Roo.bootstrap.htmleditor');
24189 * @class Roo.bootstrap.HtmlEditorToolbar1
24195 new Roo.bootstrap.HtmlEditor({
24198 new Roo.bootstrap.HtmlEditorToolbar1({
24199 disable : { fonts: 1 , format: 1, ..., ... , ...],
24205 * @cfg {Object} disable List of elements to disable..
24206 * @cfg {Array} btns List of additional buttons.
24210 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24213 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24216 Roo.apply(this, config);
24218 // default disabled, based on 'good practice'..
24219 this.disable = this.disable || {};
24220 Roo.applyIf(this.disable, {
24223 specialElements : true
24225 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24227 this.editor = config.editor;
24228 this.editorcore = config.editor.editorcore;
24230 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24232 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24233 // dont call parent... till later.
24235 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
24240 editorcore : false,
24245 "h1","h2","h3","h4","h5","h6",
24247 "abbr", "acronym", "address", "cite", "samp", "var",
24251 onRender : function(ct, position)
24253 // Roo.log("Call onRender: " + this.xtype);
24255 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24257 this.el.dom.style.marginBottom = '0';
24259 var editorcore = this.editorcore;
24260 var editor= this.editor;
24263 var btn = function(id,cmd , toggle, handler, html){
24265 var event = toggle ? 'toggle' : 'click';
24270 xns: Roo.bootstrap,
24274 enableToggle:toggle !== false,
24276 pressed : toggle ? false : null,
24279 a.listeners[toggle ? 'toggle' : 'click'] = function() {
24280 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
24286 // var cb_box = function...
24291 xns: Roo.bootstrap,
24296 xns: Roo.bootstrap,
24300 Roo.each(this.formats, function(f) {
24301 style.menu.items.push({
24303 xns: Roo.bootstrap,
24304 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24309 editorcore.insertTag(this.tagname);
24316 children.push(style);
24318 btn('bold',false,true);
24319 btn('italic',false,true);
24320 btn('align-left', 'justifyleft',true);
24321 btn('align-center', 'justifycenter',true);
24322 btn('align-right' , 'justifyright',true);
24323 btn('link', false, false, function(btn) {
24324 //Roo.log("create link?");
24325 var url = prompt(this.createLinkText, this.defaultLinkValue);
24326 if(url && url != 'http:/'+'/'){
24327 this.editorcore.relayCmd('createlink', url);
24330 btn('list','insertunorderedlist',true);
24331 btn('pencil', false,true, function(btn){
24333 this.toggleSourceEdit(btn.pressed);
24336 if (this.editor.btns.length > 0) {
24337 for (var i = 0; i<this.editor.btns.length; i++) {
24338 children.push(this.editor.btns[i]);
24346 xns: Roo.bootstrap,
24351 xns: Roo.bootstrap,
24356 cog.menu.items.push({
24358 xns: Roo.bootstrap,
24359 html : Clean styles,
24364 editorcore.insertTag(this.tagname);
24373 this.xtype = 'NavSimplebar';
24375 for(var i=0;i< children.length;i++) {
24377 this.buttons.add(this.addxtypeChild(children[i]));
24381 editor.on('editorevent', this.updateToolbar, this);
24383 onBtnClick : function(id)
24385 this.editorcore.relayCmd(id);
24386 this.editorcore.focus();
24390 * Protected method that will not generally be called directly. It triggers
24391 * a toolbar update by reading the markup state of the current selection in the editor.
24393 updateToolbar: function(){
24395 if(!this.editorcore.activated){
24396 this.editor.onFirstFocus(); // is this neeed?
24400 var btns = this.buttons;
24401 var doc = this.editorcore.doc;
24402 btns.get('bold').setActive(doc.queryCommandState('bold'));
24403 btns.get('italic').setActive(doc.queryCommandState('italic'));
24404 //btns.get('underline').setActive(doc.queryCommandState('underline'));
24406 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24407 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24408 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24410 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24411 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24414 var ans = this.editorcore.getAllAncestors();
24415 if (this.formatCombo) {
24418 var store = this.formatCombo.store;
24419 this.formatCombo.setValue("");
24420 for (var i =0; i < ans.length;i++) {
24421 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24423 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24431 // hides menus... - so this cant be on a menu...
24432 Roo.bootstrap.MenuMgr.hideAll();
24434 Roo.bootstrap.MenuMgr.hideAll();
24435 //this.editorsyncValue();
24437 onFirstFocus: function() {
24438 this.buttons.each(function(item){
24442 toggleSourceEdit : function(sourceEditMode){
24445 if(sourceEditMode){
24446 Roo.log("disabling buttons");
24447 this.buttons.each( function(item){
24448 if(item.cmd != 'pencil'){
24454 Roo.log("enabling buttons");
24455 if(this.editorcore.initialized){
24456 this.buttons.each( function(item){
24462 Roo.log("calling toggole on editor");
24463 // tell the editor that it's been pressed..
24464 this.editor.toggleSourceEdit(sourceEditMode);
24474 * @class Roo.bootstrap.Table.AbstractSelectionModel
24475 * @extends Roo.util.Observable
24476 * Abstract base class for grid SelectionModels. It provides the interface that should be
24477 * implemented by descendant classes. This class should not be directly instantiated.
24480 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24481 this.locked = false;
24482 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24486 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24487 /** @ignore Called by the grid automatically. Do not call directly. */
24488 init : function(grid){
24494 * Locks the selections.
24497 this.locked = true;
24501 * Unlocks the selections.
24503 unlock : function(){
24504 this.locked = false;
24508 * Returns true if the selections are locked.
24509 * @return {Boolean}
24511 isLocked : function(){
24512 return this.locked;
24516 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24517 * @class Roo.bootstrap.Table.RowSelectionModel
24518 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24519 * It supports multiple selections and keyboard selection/navigation.
24521 * @param {Object} config
24524 Roo.bootstrap.Table.RowSelectionModel = function(config){
24525 Roo.apply(this, config);
24526 this.selections = new Roo.util.MixedCollection(false, function(o){
24531 this.lastActive = false;
24535 * @event selectionchange
24536 * Fires when the selection changes
24537 * @param {SelectionModel} this
24539 "selectionchange" : true,
24541 * @event afterselectionchange
24542 * Fires after the selection changes (eg. by key press or clicking)
24543 * @param {SelectionModel} this
24545 "afterselectionchange" : true,
24547 * @event beforerowselect
24548 * Fires when a row is selected being selected, return false to cancel.
24549 * @param {SelectionModel} this
24550 * @param {Number} rowIndex The selected index
24551 * @param {Boolean} keepExisting False if other selections will be cleared
24553 "beforerowselect" : true,
24556 * Fires when a row is selected.
24557 * @param {SelectionModel} this
24558 * @param {Number} rowIndex The selected index
24559 * @param {Roo.data.Record} r The record
24561 "rowselect" : true,
24563 * @event rowdeselect
24564 * Fires when a row is deselected.
24565 * @param {SelectionModel} this
24566 * @param {Number} rowIndex The selected index
24568 "rowdeselect" : true
24570 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24571 this.locked = false;
24574 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24576 * @cfg {Boolean} singleSelect
24577 * True to allow selection of only one row at a time (defaults to false)
24579 singleSelect : false,
24582 initEvents : function()
24585 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24586 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24587 //}else{ // allow click to work like normal
24588 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24590 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24591 this.grid.on("rowclick", this.handleMouseDown, this);
24593 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24594 "up" : function(e){
24596 this.selectPrevious(e.shiftKey);
24597 }else if(this.last !== false && this.lastActive !== false){
24598 var last = this.last;
24599 this.selectRange(this.last, this.lastActive-1);
24600 this.grid.getView().focusRow(this.lastActive);
24601 if(last !== false){
24605 this.selectFirstRow();
24607 this.fireEvent("afterselectionchange", this);
24609 "down" : function(e){
24611 this.selectNext(e.shiftKey);
24612 }else if(this.last !== false && this.lastActive !== false){
24613 var last = this.last;
24614 this.selectRange(this.last, this.lastActive+1);
24615 this.grid.getView().focusRow(this.lastActive);
24616 if(last !== false){
24620 this.selectFirstRow();
24622 this.fireEvent("afterselectionchange", this);
24626 this.grid.store.on('load', function(){
24627 this.selections.clear();
24630 var view = this.grid.view;
24631 view.on("refresh", this.onRefresh, this);
24632 view.on("rowupdated", this.onRowUpdated, this);
24633 view.on("rowremoved", this.onRemove, this);
24638 onRefresh : function()
24640 var ds = this.grid.store, i, v = this.grid.view;
24641 var s = this.selections;
24642 s.each(function(r){
24643 if((i = ds.indexOfId(r.id)) != -1){
24652 onRemove : function(v, index, r){
24653 this.selections.remove(r);
24657 onRowUpdated : function(v, index, r){
24658 if(this.isSelected(r)){
24659 v.onRowSelect(index);
24665 * @param {Array} records The records to select
24666 * @param {Boolean} keepExisting (optional) True to keep existing selections
24668 selectRecords : function(records, keepExisting)
24671 this.clearSelections();
24673 var ds = this.grid.store;
24674 for(var i = 0, len = records.length; i < len; i++){
24675 this.selectRow(ds.indexOf(records[i]), true);
24680 * Gets the number of selected rows.
24683 getCount : function(){
24684 return this.selections.length;
24688 * Selects the first row in the grid.
24690 selectFirstRow : function(){
24695 * Select the last row.
24696 * @param {Boolean} keepExisting (optional) True to keep existing selections
24698 selectLastRow : function(keepExisting){
24699 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24700 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24704 * Selects the row immediately following the last selected row.
24705 * @param {Boolean} keepExisting (optional) True to keep existing selections
24707 selectNext : function(keepExisting)
24709 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24710 this.selectRow(this.last+1, keepExisting);
24711 this.grid.getView().focusRow(this.last);
24716 * Selects the row that precedes the last selected row.
24717 * @param {Boolean} keepExisting (optional) True to keep existing selections
24719 selectPrevious : function(keepExisting){
24721 this.selectRow(this.last-1, keepExisting);
24722 this.grid.getView().focusRow(this.last);
24727 * Returns the selected records
24728 * @return {Array} Array of selected records
24730 getSelections : function(){
24731 return [].concat(this.selections.items);
24735 * Returns the first selected record.
24738 getSelected : function(){
24739 return this.selections.itemAt(0);
24744 * Clears all selections.
24746 clearSelections : function(fast)
24752 var ds = this.grid.store;
24753 var s = this.selections;
24754 s.each(function(r){
24755 this.deselectRow(ds.indexOfId(r.id));
24759 this.selections.clear();
24766 * Selects all rows.
24768 selectAll : function(){
24772 this.selections.clear();
24773 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24774 this.selectRow(i, true);
24779 * Returns True if there is a selection.
24780 * @return {Boolean}
24782 hasSelection : function(){
24783 return this.selections.length > 0;
24787 * Returns True if the specified row is selected.
24788 * @param {Number/Record} record The record or index of the record to check
24789 * @return {Boolean}
24791 isSelected : function(index){
24792 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24793 return (r && this.selections.key(r.id) ? true : false);
24797 * Returns True if the specified record id is selected.
24798 * @param {String} id The id of record to check
24799 * @return {Boolean}
24801 isIdSelected : function(id){
24802 return (this.selections.key(id) ? true : false);
24807 handleMouseDBClick : function(e, t){
24811 handleMouseDown : function(e, t)
24813 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24814 if(this.isLocked() || rowIndex < 0 ){
24817 if(e.shiftKey && this.last !== false){
24818 var last = this.last;
24819 this.selectRange(last, rowIndex, e.ctrlKey);
24820 this.last = last; // reset the last
24824 var isSelected = this.isSelected(rowIndex);
24825 //Roo.log("select row:" + rowIndex);
24827 this.deselectRow(rowIndex);
24829 this.selectRow(rowIndex, true);
24833 if(e.button !== 0 && isSelected){
24834 alert('rowIndex 2: ' + rowIndex);
24835 view.focusRow(rowIndex);
24836 }else if(e.ctrlKey && isSelected){
24837 this.deselectRow(rowIndex);
24838 }else if(!isSelected){
24839 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24840 view.focusRow(rowIndex);
24844 this.fireEvent("afterselectionchange", this);
24847 handleDragableRowClick : function(grid, rowIndex, e)
24849 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24850 this.selectRow(rowIndex, false);
24851 grid.view.focusRow(rowIndex);
24852 this.fireEvent("afterselectionchange", this);
24857 * Selects multiple rows.
24858 * @param {Array} rows Array of the indexes of the row to select
24859 * @param {Boolean} keepExisting (optional) True to keep existing selections
24861 selectRows : function(rows, keepExisting){
24863 this.clearSelections();
24865 for(var i = 0, len = rows.length; i < len; i++){
24866 this.selectRow(rows[i], true);
24871 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24872 * @param {Number} startRow The index of the first row in the range
24873 * @param {Number} endRow The index of the last row in the range
24874 * @param {Boolean} keepExisting (optional) True to retain existing selections
24876 selectRange : function(startRow, endRow, keepExisting){
24881 this.clearSelections();
24883 if(startRow <= endRow){
24884 for(var i = startRow; i <= endRow; i++){
24885 this.selectRow(i, true);
24888 for(var i = startRow; i >= endRow; i--){
24889 this.selectRow(i, true);
24895 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24896 * @param {Number} startRow The index of the first row in the range
24897 * @param {Number} endRow The index of the last row in the range
24899 deselectRange : function(startRow, endRow, preventViewNotify){
24903 for(var i = startRow; i <= endRow; i++){
24904 this.deselectRow(i, preventViewNotify);
24910 * @param {Number} row The index of the row to select
24911 * @param {Boolean} keepExisting (optional) True to keep existing selections
24913 selectRow : function(index, keepExisting, preventViewNotify)
24915 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24918 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24919 if(!keepExisting || this.singleSelect){
24920 this.clearSelections();
24923 var r = this.grid.store.getAt(index);
24924 //console.log('selectRow - record id :' + r.id);
24926 this.selections.add(r);
24927 this.last = this.lastActive = index;
24928 if(!preventViewNotify){
24929 var proxy = new Roo.Element(
24930 this.grid.getRowDom(index)
24932 proxy.addClass('bg-info info');
24934 this.fireEvent("rowselect", this, index, r);
24935 this.fireEvent("selectionchange", this);
24941 * @param {Number} row The index of the row to deselect
24943 deselectRow : function(index, preventViewNotify)
24948 if(this.last == index){
24951 if(this.lastActive == index){
24952 this.lastActive = false;
24955 var r = this.grid.store.getAt(index);
24960 this.selections.remove(r);
24961 //.console.log('deselectRow - record id :' + r.id);
24962 if(!preventViewNotify){
24964 var proxy = new Roo.Element(
24965 this.grid.getRowDom(index)
24967 proxy.removeClass('bg-info info');
24969 this.fireEvent("rowdeselect", this, index);
24970 this.fireEvent("selectionchange", this);
24974 restoreLast : function(){
24976 this.last = this._last;
24981 acceptsNav : function(row, col, cm){
24982 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24986 onEditorKey : function(field, e){
24987 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24992 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24994 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24996 }else if(k == e.ENTER && !e.ctrlKey){
25000 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
25002 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25004 }else if(k == e.ESC){
25008 g.startEditing(newCell[0], newCell[1]);
25014 * Ext JS Library 1.1.1
25015 * Copyright(c) 2006-2007, Ext JS, LLC.
25017 * Originally Released Under LGPL - original licence link has changed is not relivant.
25020 * <script type="text/javascript">
25024 * @class Roo.bootstrap.PagingToolbar
25025 * @extends Roo.bootstrap.NavSimplebar
25026 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25028 * Create a new PagingToolbar
25029 * @param {Object} config The config object
25030 * @param {Roo.data.Store} store
25032 Roo.bootstrap.PagingToolbar = function(config)
25034 // old args format still supported... - xtype is prefered..
25035 // created from xtype...
25037 this.ds = config.dataSource;
25039 if (config.store && !this.ds) {
25040 this.store= Roo.factory(config.store, Roo.data);
25041 this.ds = this.store;
25042 this.ds.xmodule = this.xmodule || false;
25045 this.toolbarItems = [];
25046 if (config.items) {
25047 this.toolbarItems = config.items;
25050 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25055 this.bind(this.ds);
25058 if (Roo.bootstrap.version == 4) {
25059 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25061 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25066 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25068 * @cfg {Roo.data.Store} dataSource
25069 * The underlying data store providing the paged data
25072 * @cfg {String/HTMLElement/Element} container
25073 * container The id or element that will contain the toolbar
25076 * @cfg {Boolean} displayInfo
25077 * True to display the displayMsg (defaults to false)
25080 * @cfg {Number} pageSize
25081 * The number of records to display per page (defaults to 20)
25085 * @cfg {String} displayMsg
25086 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25088 displayMsg : 'Displaying {0} - {1} of {2}',
25090 * @cfg {String} emptyMsg
25091 * The message to display when no records are found (defaults to "No data to display")
25093 emptyMsg : 'No data to display',
25095 * Customizable piece of the default paging text (defaults to "Page")
25098 beforePageText : "Page",
25100 * Customizable piece of the default paging text (defaults to "of %0")
25103 afterPageText : "of {0}",
25105 * Customizable piece of the default paging text (defaults to "First Page")
25108 firstText : "First Page",
25110 * Customizable piece of the default paging text (defaults to "Previous Page")
25113 prevText : "Previous Page",
25115 * Customizable piece of the default paging text (defaults to "Next Page")
25118 nextText : "Next Page",
25120 * Customizable piece of the default paging text (defaults to "Last Page")
25123 lastText : "Last Page",
25125 * Customizable piece of the default paging text (defaults to "Refresh")
25128 refreshText : "Refresh",
25132 onRender : function(ct, position)
25134 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25135 this.navgroup.parentId = this.id;
25136 this.navgroup.onRender(this.el, null);
25137 // add the buttons to the navgroup
25139 if(this.displayInfo){
25140 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25141 this.displayEl = this.el.select('.x-paging-info', true).first();
25142 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25143 // this.displayEl = navel.el.select('span',true).first();
25149 Roo.each(_this.buttons, function(e){ // this might need to use render????
25150 Roo.factory(e).render(_this.el);
25154 Roo.each(_this.toolbarItems, function(e) {
25155 _this.navgroup.addItem(e);
25159 this.first = this.navgroup.addItem({
25160 tooltip: this.firstText,
25161 cls: "prev btn-outline-secondary",
25162 html : ' <i class="fa fa-step-backward"></i>',
25164 preventDefault: true,
25165 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25168 this.prev = this.navgroup.addItem({
25169 tooltip: this.prevText,
25170 cls: "prev btn-outline-secondary",
25171 html : ' <i class="fa fa-backward"></i>',
25173 preventDefault: true,
25174 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
25176 //this.addSeparator();
25179 var field = this.navgroup.addItem( {
25181 cls : 'x-paging-position btn-outline-secondary',
25183 html : this.beforePageText +
25184 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25185 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
25188 this.field = field.el.select('input', true).first();
25189 this.field.on("keydown", this.onPagingKeydown, this);
25190 this.field.on("focus", function(){this.dom.select();});
25193 this.afterTextEl = field.el.select('.x-paging-after',true).first();
25194 //this.field.setHeight(18);
25195 //this.addSeparator();
25196 this.next = this.navgroup.addItem({
25197 tooltip: this.nextText,
25198 cls: "next btn-outline-secondary",
25199 html : ' <i class="fa fa-forward"></i>',
25201 preventDefault: true,
25202 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
25204 this.last = this.navgroup.addItem({
25205 tooltip: this.lastText,
25206 html : ' <i class="fa fa-step-forward"></i>',
25207 cls: "next btn-outline-secondary",
25209 preventDefault: true,
25210 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
25212 //this.addSeparator();
25213 this.loading = this.navgroup.addItem({
25214 tooltip: this.refreshText,
25215 cls: "btn-outline-secondary",
25216 html : ' <i class="fa fa-refresh"></i>',
25217 preventDefault: true,
25218 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25224 updateInfo : function(){
25225 if(this.displayEl){
25226 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25227 var msg = count == 0 ?
25231 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
25233 this.displayEl.update(msg);
25238 onLoad : function(ds, r, o)
25240 this.cursor = o.params.start ? o.params.start : 0;
25242 var d = this.getPageData(),
25247 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25248 this.field.dom.value = ap;
25249 this.first.setDisabled(ap == 1);
25250 this.prev.setDisabled(ap == 1);
25251 this.next.setDisabled(ap == ps);
25252 this.last.setDisabled(ap == ps);
25253 this.loading.enable();
25258 getPageData : function(){
25259 var total = this.ds.getTotalCount();
25262 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25263 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25268 onLoadError : function(){
25269 this.loading.enable();
25273 onPagingKeydown : function(e){
25274 var k = e.getKey();
25275 var d = this.getPageData();
25277 var v = this.field.dom.value, pageNum;
25278 if(!v || isNaN(pageNum = parseInt(v, 10))){
25279 this.field.dom.value = d.activePage;
25282 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25283 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25286 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))
25288 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25289 this.field.dom.value = pageNum;
25290 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25293 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25295 var v = this.field.dom.value, pageNum;
25296 var increment = (e.shiftKey) ? 10 : 1;
25297 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25300 if(!v || isNaN(pageNum = parseInt(v, 10))) {
25301 this.field.dom.value = d.activePage;
25304 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25306 this.field.dom.value = parseInt(v, 10) + increment;
25307 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25308 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25315 beforeLoad : function(){
25317 this.loading.disable();
25322 onClick : function(which){
25331 ds.load({params:{start: 0, limit: this.pageSize}});
25334 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25337 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25340 var total = ds.getTotalCount();
25341 var extra = total % this.pageSize;
25342 var lastStart = extra ? (total - extra) : total-this.pageSize;
25343 ds.load({params:{start: lastStart, limit: this.pageSize}});
25346 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25352 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25353 * @param {Roo.data.Store} store The data store to unbind
25355 unbind : function(ds){
25356 ds.un("beforeload", this.beforeLoad, this);
25357 ds.un("load", this.onLoad, this);
25358 ds.un("loadexception", this.onLoadError, this);
25359 ds.un("remove", this.updateInfo, this);
25360 ds.un("add", this.updateInfo, this);
25361 this.ds = undefined;
25365 * Binds the paging toolbar to the specified {@link Roo.data.Store}
25366 * @param {Roo.data.Store} store The data store to bind
25368 bind : function(ds){
25369 ds.on("beforeload", this.beforeLoad, this);
25370 ds.on("load", this.onLoad, this);
25371 ds.on("loadexception", this.onLoadError, this);
25372 ds.on("remove", this.updateInfo, this);
25373 ds.on("add", this.updateInfo, this);
25384 * @class Roo.bootstrap.MessageBar
25385 * @extends Roo.bootstrap.Component
25386 * Bootstrap MessageBar class
25387 * @cfg {String} html contents of the MessageBar
25388 * @cfg {String} weight (info | success | warning | danger) default info
25389 * @cfg {String} beforeClass insert the bar before the given class
25390 * @cfg {Boolean} closable (true | false) default false
25391 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25394 * Create a new Element
25395 * @param {Object} config The config object
25398 Roo.bootstrap.MessageBar = function(config){
25399 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25402 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
25408 beforeClass: 'bootstrap-sticky-wrap',
25410 getAutoCreate : function(){
25414 cls: 'alert alert-dismissable alert-' + this.weight,
25419 html: this.html || ''
25425 cfg.cls += ' alert-messages-fixed';
25439 onRender : function(ct, position)
25441 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25444 var cfg = Roo.apply({}, this.getAutoCreate());
25448 cfg.cls += ' ' + this.cls;
25451 cfg.style = this.style;
25453 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25455 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25458 this.el.select('>button.close').on('click', this.hide, this);
25464 if (!this.rendered) {
25470 this.fireEvent('show', this);
25476 if (!this.rendered) {
25482 this.fireEvent('hide', this);
25485 update : function()
25487 // var e = this.el.dom.firstChild;
25489 // if(this.closable){
25490 // e = e.nextSibling;
25493 // e.data = this.html || '';
25495 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25511 * @class Roo.bootstrap.Graph
25512 * @extends Roo.bootstrap.Component
25513 * Bootstrap Graph class
25517 @cfg {String} graphtype bar | vbar | pie
25518 @cfg {number} g_x coodinator | centre x (pie)
25519 @cfg {number} g_y coodinator | centre y (pie)
25520 @cfg {number} g_r radius (pie)
25521 @cfg {number} g_height height of the chart (respected by all elements in the set)
25522 @cfg {number} g_width width of the chart (respected by all elements in the set)
25523 @cfg {Object} title The title of the chart
25526 -opts (object) options for the chart
25528 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25529 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25531 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.
25532 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25534 o stretch (boolean)
25536 -opts (object) options for the pie
25539 o startAngle (number)
25540 o endAngle (number)
25544 * Create a new Input
25545 * @param {Object} config The config object
25548 Roo.bootstrap.Graph = function(config){
25549 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25555 * The img click event for the img.
25556 * @param {Roo.EventObject} e
25562 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25573 //g_colors: this.colors,
25580 getAutoCreate : function(){
25591 onRender : function(ct,position){
25594 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25596 if (typeof(Raphael) == 'undefined') {
25597 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25601 this.raphael = Raphael(this.el.dom);
25603 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25604 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25605 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25606 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25608 r.text(160, 10, "Single Series Chart").attr(txtattr);
25609 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25610 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25611 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25613 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25614 r.barchart(330, 10, 300, 220, data1);
25615 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25616 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25619 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25620 // r.barchart(30, 30, 560, 250, xdata, {
25621 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25622 // axis : "0 0 1 1",
25623 // axisxlabels : xdata
25624 // //yvalues : cols,
25627 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25629 // this.load(null,xdata,{
25630 // axis : "0 0 1 1",
25631 // axisxlabels : xdata
25636 load : function(graphtype,xdata,opts)
25638 this.raphael.clear();
25640 graphtype = this.graphtype;
25645 var r = this.raphael,
25646 fin = function () {
25647 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25649 fout = function () {
25650 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25652 pfin = function() {
25653 this.sector.stop();
25654 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25657 this.label[0].stop();
25658 this.label[0].attr({ r: 7.5 });
25659 this.label[1].attr({ "font-weight": 800 });
25662 pfout = function() {
25663 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25666 this.label[0].animate({ r: 5 }, 500, "bounce");
25667 this.label[1].attr({ "font-weight": 400 });
25673 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25676 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25679 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25680 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25682 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25689 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25694 setTitle: function(o)
25699 initEvents: function() {
25702 this.el.on('click', this.onClick, this);
25706 onClick : function(e)
25708 Roo.log('img onclick');
25709 this.fireEvent('click', this, e);
25721 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25724 * @class Roo.bootstrap.dash.NumberBox
25725 * @extends Roo.bootstrap.Component
25726 * Bootstrap NumberBox class
25727 * @cfg {String} headline Box headline
25728 * @cfg {String} content Box content
25729 * @cfg {String} icon Box icon
25730 * @cfg {String} footer Footer text
25731 * @cfg {String} fhref Footer href
25734 * Create a new NumberBox
25735 * @param {Object} config The config object
25739 Roo.bootstrap.dash.NumberBox = function(config){
25740 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25744 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25753 getAutoCreate : function(){
25757 cls : 'small-box ',
25765 cls : 'roo-headline',
25766 html : this.headline
25770 cls : 'roo-content',
25771 html : this.content
25785 cls : 'ion ' + this.icon
25794 cls : 'small-box-footer',
25795 href : this.fhref || '#',
25799 cfg.cn.push(footer);
25806 onRender : function(ct,position){
25807 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25814 setHeadline: function (value)
25816 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25819 setFooter: function (value, href)
25821 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25824 this.el.select('a.small-box-footer',true).first().attr('href', href);
25829 setContent: function (value)
25831 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25834 initEvents: function()
25848 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25851 * @class Roo.bootstrap.dash.TabBox
25852 * @extends Roo.bootstrap.Component
25853 * Bootstrap TabBox class
25854 * @cfg {String} title Title of the TabBox
25855 * @cfg {String} icon Icon of the TabBox
25856 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25857 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25860 * Create a new TabBox
25861 * @param {Object} config The config object
25865 Roo.bootstrap.dash.TabBox = function(config){
25866 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25871 * When a pane is added
25872 * @param {Roo.bootstrap.dash.TabPane} pane
25876 * @event activatepane
25877 * When a pane is activated
25878 * @param {Roo.bootstrap.dash.TabPane} pane
25880 "activatepane" : true
25888 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25893 tabScrollable : false,
25895 getChildContainer : function()
25897 return this.el.select('.tab-content', true).first();
25900 getAutoCreate : function(){
25904 cls: 'pull-left header',
25912 cls: 'fa ' + this.icon
25918 cls: 'nav nav-tabs pull-right',
25924 if(this.tabScrollable){
25931 cls: 'nav nav-tabs pull-right',
25942 cls: 'nav-tabs-custom',
25947 cls: 'tab-content no-padding',
25955 initEvents : function()
25957 //Roo.log('add add pane handler');
25958 this.on('addpane', this.onAddPane, this);
25961 * Updates the box title
25962 * @param {String} html to set the title to.
25964 setTitle : function(value)
25966 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25968 onAddPane : function(pane)
25970 this.panes.push(pane);
25971 //Roo.log('addpane');
25973 // tabs are rendere left to right..
25974 if(!this.showtabs){
25978 var ctr = this.el.select('.nav-tabs', true).first();
25981 var existing = ctr.select('.nav-tab',true);
25982 var qty = existing.getCount();;
25985 var tab = ctr.createChild({
25987 cls : 'nav-tab' + (qty ? '' : ' active'),
25995 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25998 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
26000 pane.el.addClass('active');
26005 onTabClick : function(ev,un,ob,pane)
26007 //Roo.log('tab - prev default');
26008 ev.preventDefault();
26011 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26012 pane.tab.addClass('active');
26013 //Roo.log(pane.title);
26014 this.getChildContainer().select('.tab-pane',true).removeClass('active');
26015 // technically we should have a deactivate event.. but maybe add later.
26016 // and it should not de-activate the selected tab...
26017 this.fireEvent('activatepane', pane);
26018 pane.el.addClass('active');
26019 pane.fireEvent('activate');
26024 getActivePane : function()
26027 Roo.each(this.panes, function(p) {
26028 if(p.el.hasClass('active')){
26049 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26051 * @class Roo.bootstrap.TabPane
26052 * @extends Roo.bootstrap.Component
26053 * Bootstrap TabPane class
26054 * @cfg {Boolean} active (false | true) Default false
26055 * @cfg {String} title title of panel
26059 * Create a new TabPane
26060 * @param {Object} config The config object
26063 Roo.bootstrap.dash.TabPane = function(config){
26064 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26070 * When a pane is activated
26071 * @param {Roo.bootstrap.dash.TabPane} pane
26078 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
26083 // the tabBox that this is attached to.
26086 getAutoCreate : function()
26094 cfg.cls += ' active';
26099 initEvents : function()
26101 //Roo.log('trigger add pane handler');
26102 this.parent().fireEvent('addpane', this)
26106 * Updates the tab title
26107 * @param {String} html to set the title to.
26109 setTitle: function(str)
26115 this.tab.select('a', true).first().dom.innerHTML = str;
26132 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26135 * @class Roo.bootstrap.menu.Menu
26136 * @extends Roo.bootstrap.Component
26137 * Bootstrap Menu class - container for Menu
26138 * @cfg {String} html Text of the menu
26139 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26140 * @cfg {String} icon Font awesome icon
26141 * @cfg {String} pos Menu align to (top | bottom) default bottom
26145 * Create a new Menu
26146 * @param {Object} config The config object
26150 Roo.bootstrap.menu.Menu = function(config){
26151 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26155 * @event beforeshow
26156 * Fires before this menu is displayed
26157 * @param {Roo.bootstrap.menu.Menu} this
26161 * @event beforehide
26162 * Fires before this menu is hidden
26163 * @param {Roo.bootstrap.menu.Menu} this
26168 * Fires after this menu is displayed
26169 * @param {Roo.bootstrap.menu.Menu} this
26174 * Fires after this menu is hidden
26175 * @param {Roo.bootstrap.menu.Menu} this
26180 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26181 * @param {Roo.bootstrap.menu.Menu} this
26182 * @param {Roo.EventObject} e
26189 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
26193 weight : 'default',
26198 getChildContainer : function() {
26199 if(this.isSubMenu){
26203 return this.el.select('ul.dropdown-menu', true).first();
26206 getAutoCreate : function()
26211 cls : 'roo-menu-text',
26219 cls : 'fa ' + this.icon
26230 cls : 'dropdown-button btn btn-' + this.weight,
26235 cls : 'dropdown-toggle btn btn-' + this.weight,
26245 cls : 'dropdown-menu'
26251 if(this.pos == 'top'){
26252 cfg.cls += ' dropup';
26255 if(this.isSubMenu){
26258 cls : 'dropdown-menu'
26265 onRender : function(ct, position)
26267 this.isSubMenu = ct.hasClass('dropdown-submenu');
26269 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26272 initEvents : function()
26274 if(this.isSubMenu){
26278 this.hidden = true;
26280 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26281 this.triggerEl.on('click', this.onTriggerPress, this);
26283 this.buttonEl = this.el.select('button.dropdown-button', true).first();
26284 this.buttonEl.on('click', this.onClick, this);
26290 if(this.isSubMenu){
26294 return this.el.select('ul.dropdown-menu', true).first();
26297 onClick : function(e)
26299 this.fireEvent("click", this, e);
26302 onTriggerPress : function(e)
26304 if (this.isVisible()) {
26311 isVisible : function(){
26312 return !this.hidden;
26317 this.fireEvent("beforeshow", this);
26319 this.hidden = false;
26320 this.el.addClass('open');
26322 Roo.get(document).on("mouseup", this.onMouseUp, this);
26324 this.fireEvent("show", this);
26331 this.fireEvent("beforehide", this);
26333 this.hidden = true;
26334 this.el.removeClass('open');
26336 Roo.get(document).un("mouseup", this.onMouseUp);
26338 this.fireEvent("hide", this);
26341 onMouseUp : function()
26355 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26358 * @class Roo.bootstrap.menu.Item
26359 * @extends Roo.bootstrap.Component
26360 * Bootstrap MenuItem class
26361 * @cfg {Boolean} submenu (true | false) default false
26362 * @cfg {String} html text of the item
26363 * @cfg {String} href the link
26364 * @cfg {Boolean} disable (true | false) default false
26365 * @cfg {Boolean} preventDefault (true | false) default true
26366 * @cfg {String} icon Font awesome icon
26367 * @cfg {String} pos Submenu align to (left | right) default right
26371 * Create a new Item
26372 * @param {Object} config The config object
26376 Roo.bootstrap.menu.Item = function(config){
26377 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26381 * Fires when the mouse is hovering over this menu
26382 * @param {Roo.bootstrap.menu.Item} this
26383 * @param {Roo.EventObject} e
26388 * Fires when the mouse exits this menu
26389 * @param {Roo.bootstrap.menu.Item} this
26390 * @param {Roo.EventObject} e
26396 * The raw click event for the entire grid.
26397 * @param {Roo.EventObject} e
26403 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
26408 preventDefault: true,
26413 getAutoCreate : function()
26418 cls : 'roo-menu-item-text',
26426 cls : 'fa ' + this.icon
26435 href : this.href || '#',
26442 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26446 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26448 if(this.pos == 'left'){
26449 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26456 initEvents : function()
26458 this.el.on('mouseover', this.onMouseOver, this);
26459 this.el.on('mouseout', this.onMouseOut, this);
26461 this.el.select('a', true).first().on('click', this.onClick, this);
26465 onClick : function(e)
26467 if(this.preventDefault){
26468 e.preventDefault();
26471 this.fireEvent("click", this, e);
26474 onMouseOver : function(e)
26476 if(this.submenu && this.pos == 'left'){
26477 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26480 this.fireEvent("mouseover", this, e);
26483 onMouseOut : function(e)
26485 this.fireEvent("mouseout", this, e);
26497 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26500 * @class Roo.bootstrap.menu.Separator
26501 * @extends Roo.bootstrap.Component
26502 * Bootstrap Separator class
26505 * Create a new Separator
26506 * @param {Object} config The config object
26510 Roo.bootstrap.menu.Separator = function(config){
26511 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26514 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26516 getAutoCreate : function(){
26537 * @class Roo.bootstrap.Tooltip
26538 * Bootstrap Tooltip class
26539 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26540 * to determine which dom element triggers the tooltip.
26542 * It needs to add support for additional attributes like tooltip-position
26545 * Create a new Toolti
26546 * @param {Object} config The config object
26549 Roo.bootstrap.Tooltip = function(config){
26550 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26552 this.alignment = Roo.bootstrap.Tooltip.alignment;
26554 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26555 this.alignment = config.alignment;
26560 Roo.apply(Roo.bootstrap.Tooltip, {
26562 * @function init initialize tooltip monitoring.
26566 currentTip : false,
26567 currentRegion : false,
26573 Roo.get(document).on('mouseover', this.enter ,this);
26574 Roo.get(document).on('mouseout', this.leave, this);
26577 this.currentTip = new Roo.bootstrap.Tooltip();
26580 enter : function(ev)
26582 var dom = ev.getTarget();
26584 //Roo.log(['enter',dom]);
26585 var el = Roo.fly(dom);
26586 if (this.currentEl) {
26588 //Roo.log(this.currentEl);
26589 //Roo.log(this.currentEl.contains(dom));
26590 if (this.currentEl == el) {
26593 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26599 if (this.currentTip.el) {
26600 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26604 if(!el || el.dom == document){
26610 // you can not look for children, as if el is the body.. then everythign is the child..
26611 if (!el.attr('tooltip')) { //
26612 if (!el.select("[tooltip]").elements.length) {
26615 // is the mouse over this child...?
26616 bindEl = el.select("[tooltip]").first();
26617 var xy = ev.getXY();
26618 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26619 //Roo.log("not in region.");
26622 //Roo.log("child element over..");
26625 this.currentEl = bindEl;
26626 this.currentTip.bind(bindEl);
26627 this.currentRegion = Roo.lib.Region.getRegion(dom);
26628 this.currentTip.enter();
26631 leave : function(ev)
26633 var dom = ev.getTarget();
26634 //Roo.log(['leave',dom]);
26635 if (!this.currentEl) {
26640 if (dom != this.currentEl.dom) {
26643 var xy = ev.getXY();
26644 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26647 // only activate leave if mouse cursor is outside... bounding box..
26652 if (this.currentTip) {
26653 this.currentTip.leave();
26655 //Roo.log('clear currentEl');
26656 this.currentEl = false;
26661 'left' : ['r-l', [-2,0], 'right'],
26662 'right' : ['l-r', [2,0], 'left'],
26663 'bottom' : ['t-b', [0,2], 'top'],
26664 'top' : [ 'b-t', [0,-2], 'bottom']
26670 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26675 delay : null, // can be { show : 300 , hide: 500}
26679 hoverState : null, //???
26681 placement : 'bottom',
26685 getAutoCreate : function(){
26692 cls : 'tooltip-arrow'
26695 cls : 'tooltip-inner'
26702 bind : function(el)
26708 enter : function () {
26710 if (this.timeout != null) {
26711 clearTimeout(this.timeout);
26714 this.hoverState = 'in';
26715 //Roo.log("enter - show");
26716 if (!this.delay || !this.delay.show) {
26721 this.timeout = setTimeout(function () {
26722 if (_t.hoverState == 'in') {
26725 }, this.delay.show);
26729 clearTimeout(this.timeout);
26731 this.hoverState = 'out';
26732 if (!this.delay || !this.delay.hide) {
26738 this.timeout = setTimeout(function () {
26739 //Roo.log("leave - timeout");
26741 if (_t.hoverState == 'out') {
26743 Roo.bootstrap.Tooltip.currentEl = false;
26748 show : function (msg)
26751 this.render(document.body);
26754 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26756 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26758 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26760 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26762 var placement = typeof this.placement == 'function' ?
26763 this.placement.call(this, this.el, on_el) :
26766 var autoToken = /\s?auto?\s?/i;
26767 var autoPlace = autoToken.test(placement);
26769 placement = placement.replace(autoToken, '') || 'top';
26773 //this.el.setXY([0,0]);
26775 //this.el.dom.style.display='block';
26777 //this.el.appendTo(on_el);
26779 var p = this.getPosition();
26780 var box = this.el.getBox();
26786 var align = this.alignment[placement];
26788 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26790 if(placement == 'top' || placement == 'bottom'){
26792 placement = 'right';
26795 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26796 placement = 'left';
26799 var scroll = Roo.select('body', true).first().getScroll();
26801 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26805 align = this.alignment[placement];
26808 this.el.alignTo(this.bindEl, align[0],align[1]);
26809 //var arrow = this.el.select('.arrow',true).first();
26810 //arrow.set(align[2],
26812 this.el.addClass(placement);
26814 this.el.addClass('in fade');
26816 this.hoverState = null;
26818 if (this.el.hasClass('fade')) {
26829 //this.el.setXY([0,0]);
26830 this.el.removeClass('in');
26846 * @class Roo.bootstrap.LocationPicker
26847 * @extends Roo.bootstrap.Component
26848 * Bootstrap LocationPicker class
26849 * @cfg {Number} latitude Position when init default 0
26850 * @cfg {Number} longitude Position when init default 0
26851 * @cfg {Number} zoom default 15
26852 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26853 * @cfg {Boolean} mapTypeControl default false
26854 * @cfg {Boolean} disableDoubleClickZoom default false
26855 * @cfg {Boolean} scrollwheel default true
26856 * @cfg {Boolean} streetViewControl default false
26857 * @cfg {Number} radius default 0
26858 * @cfg {String} locationName
26859 * @cfg {Boolean} draggable default true
26860 * @cfg {Boolean} enableAutocomplete default false
26861 * @cfg {Boolean} enableReverseGeocode default true
26862 * @cfg {String} markerTitle
26865 * Create a new LocationPicker
26866 * @param {Object} config The config object
26870 Roo.bootstrap.LocationPicker = function(config){
26872 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26877 * Fires when the picker initialized.
26878 * @param {Roo.bootstrap.LocationPicker} this
26879 * @param {Google Location} location
26883 * @event positionchanged
26884 * Fires when the picker position changed.
26885 * @param {Roo.bootstrap.LocationPicker} this
26886 * @param {Google Location} location
26888 positionchanged : true,
26891 * Fires when the map resize.
26892 * @param {Roo.bootstrap.LocationPicker} this
26897 * Fires when the map show.
26898 * @param {Roo.bootstrap.LocationPicker} this
26903 * Fires when the map hide.
26904 * @param {Roo.bootstrap.LocationPicker} this
26909 * Fires when click the map.
26910 * @param {Roo.bootstrap.LocationPicker} this
26911 * @param {Map event} e
26915 * @event mapRightClick
26916 * Fires when right click the map.
26917 * @param {Roo.bootstrap.LocationPicker} this
26918 * @param {Map event} e
26920 mapRightClick : true,
26922 * @event markerClick
26923 * Fires when click the marker.
26924 * @param {Roo.bootstrap.LocationPicker} this
26925 * @param {Map event} e
26927 markerClick : true,
26929 * @event markerRightClick
26930 * Fires when right click the marker.
26931 * @param {Roo.bootstrap.LocationPicker} this
26932 * @param {Map event} e
26934 markerRightClick : true,
26936 * @event OverlayViewDraw
26937 * Fires when OverlayView Draw
26938 * @param {Roo.bootstrap.LocationPicker} this
26940 OverlayViewDraw : true,
26942 * @event OverlayViewOnAdd
26943 * Fires when OverlayView Draw
26944 * @param {Roo.bootstrap.LocationPicker} this
26946 OverlayViewOnAdd : true,
26948 * @event OverlayViewOnRemove
26949 * Fires when OverlayView Draw
26950 * @param {Roo.bootstrap.LocationPicker} this
26952 OverlayViewOnRemove : true,
26954 * @event OverlayViewShow
26955 * Fires when OverlayView Draw
26956 * @param {Roo.bootstrap.LocationPicker} this
26957 * @param {Pixel} cpx
26959 OverlayViewShow : true,
26961 * @event OverlayViewHide
26962 * Fires when OverlayView Draw
26963 * @param {Roo.bootstrap.LocationPicker} this
26965 OverlayViewHide : true,
26967 * @event loadexception
26968 * Fires when load google lib failed.
26969 * @param {Roo.bootstrap.LocationPicker} this
26971 loadexception : true
26976 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26978 gMapContext: false,
26984 mapTypeControl: false,
26985 disableDoubleClickZoom: false,
26987 streetViewControl: false,
26991 enableAutocomplete: false,
26992 enableReverseGeocode: true,
26995 getAutoCreate: function()
27000 cls: 'roo-location-picker'
27006 initEvents: function(ct, position)
27008 if(!this.el.getWidth() || this.isApplied()){
27012 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27017 initial: function()
27019 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27020 this.fireEvent('loadexception', this);
27024 if(!this.mapTypeId){
27025 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27028 this.gMapContext = this.GMapContext();
27030 this.initOverlayView();
27032 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27036 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27037 _this.setPosition(_this.gMapContext.marker.position);
27040 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27041 _this.fireEvent('mapClick', this, event);
27045 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27046 _this.fireEvent('mapRightClick', this, event);
27050 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27051 _this.fireEvent('markerClick', this, event);
27055 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27056 _this.fireEvent('markerRightClick', this, event);
27060 this.setPosition(this.gMapContext.location);
27062 this.fireEvent('initial', this, this.gMapContext.location);
27065 initOverlayView: function()
27069 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27073 _this.fireEvent('OverlayViewDraw', _this);
27078 _this.fireEvent('OverlayViewOnAdd', _this);
27081 onRemove: function()
27083 _this.fireEvent('OverlayViewOnRemove', _this);
27086 show: function(cpx)
27088 _this.fireEvent('OverlayViewShow', _this, cpx);
27093 _this.fireEvent('OverlayViewHide', _this);
27099 fromLatLngToContainerPixel: function(event)
27101 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27104 isApplied: function()
27106 return this.getGmapContext() == false ? false : true;
27109 getGmapContext: function()
27111 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27114 GMapContext: function()
27116 var position = new google.maps.LatLng(this.latitude, this.longitude);
27118 var _map = new google.maps.Map(this.el.dom, {
27121 mapTypeId: this.mapTypeId,
27122 mapTypeControl: this.mapTypeControl,
27123 disableDoubleClickZoom: this.disableDoubleClickZoom,
27124 scrollwheel: this.scrollwheel,
27125 streetViewControl: this.streetViewControl,
27126 locationName: this.locationName,
27127 draggable: this.draggable,
27128 enableAutocomplete: this.enableAutocomplete,
27129 enableReverseGeocode: this.enableReverseGeocode
27132 var _marker = new google.maps.Marker({
27133 position: position,
27135 title: this.markerTitle,
27136 draggable: this.draggable
27143 location: position,
27144 radius: this.radius,
27145 locationName: this.locationName,
27146 addressComponents: {
27147 formatted_address: null,
27148 addressLine1: null,
27149 addressLine2: null,
27151 streetNumber: null,
27155 stateOrProvince: null
27158 domContainer: this.el.dom,
27159 geodecoder: new google.maps.Geocoder()
27163 drawCircle: function(center, radius, options)
27165 if (this.gMapContext.circle != null) {
27166 this.gMapContext.circle.setMap(null);
27170 options = Roo.apply({}, options, {
27171 strokeColor: "#0000FF",
27172 strokeOpacity: .35,
27174 fillColor: "#0000FF",
27178 options.map = this.gMapContext.map;
27179 options.radius = radius;
27180 options.center = center;
27181 this.gMapContext.circle = new google.maps.Circle(options);
27182 return this.gMapContext.circle;
27188 setPosition: function(location)
27190 this.gMapContext.location = location;
27191 this.gMapContext.marker.setPosition(location);
27192 this.gMapContext.map.panTo(location);
27193 this.drawCircle(location, this.gMapContext.radius, {});
27197 if (this.gMapContext.settings.enableReverseGeocode) {
27198 this.gMapContext.geodecoder.geocode({
27199 latLng: this.gMapContext.location
27200 }, function(results, status) {
27202 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27203 _this.gMapContext.locationName = results[0].formatted_address;
27204 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27206 _this.fireEvent('positionchanged', this, location);
27213 this.fireEvent('positionchanged', this, location);
27218 google.maps.event.trigger(this.gMapContext.map, "resize");
27220 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27222 this.fireEvent('resize', this);
27225 setPositionByLatLng: function(latitude, longitude)
27227 this.setPosition(new google.maps.LatLng(latitude, longitude));
27230 getCurrentPosition: function()
27233 latitude: this.gMapContext.location.lat(),
27234 longitude: this.gMapContext.location.lng()
27238 getAddressName: function()
27240 return this.gMapContext.locationName;
27243 getAddressComponents: function()
27245 return this.gMapContext.addressComponents;
27248 address_component_from_google_geocode: function(address_components)
27252 for (var i = 0; i < address_components.length; i++) {
27253 var component = address_components[i];
27254 if (component.types.indexOf("postal_code") >= 0) {
27255 result.postalCode = component.short_name;
27256 } else if (component.types.indexOf("street_number") >= 0) {
27257 result.streetNumber = component.short_name;
27258 } else if (component.types.indexOf("route") >= 0) {
27259 result.streetName = component.short_name;
27260 } else if (component.types.indexOf("neighborhood") >= 0) {
27261 result.city = component.short_name;
27262 } else if (component.types.indexOf("locality") >= 0) {
27263 result.city = component.short_name;
27264 } else if (component.types.indexOf("sublocality") >= 0) {
27265 result.district = component.short_name;
27266 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27267 result.stateOrProvince = component.short_name;
27268 } else if (component.types.indexOf("country") >= 0) {
27269 result.country = component.short_name;
27273 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27274 result.addressLine2 = "";
27278 setZoomLevel: function(zoom)
27280 this.gMapContext.map.setZoom(zoom);
27293 this.fireEvent('show', this);
27304 this.fireEvent('hide', this);
27309 Roo.apply(Roo.bootstrap.LocationPicker, {
27311 OverlayView : function(map, options)
27313 options = options || {};
27320 * @class Roo.bootstrap.Alert
27321 * @extends Roo.bootstrap.Component
27322 * Bootstrap Alert class - shows an alert area box
27324 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27325 Enter a valid email address
27328 * @cfg {String} title The title of alert
27329 * @cfg {String} html The content of alert
27330 * @cfg {String} weight ( success | info | warning | danger )
27331 * @cfg {String} faicon font-awesomeicon
27334 * Create a new alert
27335 * @param {Object} config The config object
27339 Roo.bootstrap.Alert = function(config){
27340 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27344 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
27351 getAutoCreate : function()
27360 cls : 'roo-alert-icon'
27365 cls : 'roo-alert-title',
27370 cls : 'roo-alert-text',
27377 cfg.cn[0].cls += ' fa ' + this.faicon;
27381 cfg.cls += ' alert-' + this.weight;
27387 initEvents: function()
27389 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27392 setTitle : function(str)
27394 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27397 setText : function(str)
27399 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27402 setWeight : function(weight)
27405 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27408 this.weight = weight;
27410 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27413 setIcon : function(icon)
27416 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27419 this.faicon = icon;
27421 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27442 * @class Roo.bootstrap.UploadCropbox
27443 * @extends Roo.bootstrap.Component
27444 * Bootstrap UploadCropbox class
27445 * @cfg {String} emptyText show when image has been loaded
27446 * @cfg {String} rotateNotify show when image too small to rotate
27447 * @cfg {Number} errorTimeout default 3000
27448 * @cfg {Number} minWidth default 300
27449 * @cfg {Number} minHeight default 300
27450 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27451 * @cfg {Boolean} isDocument (true|false) default false
27452 * @cfg {String} url action url
27453 * @cfg {String} paramName default 'imageUpload'
27454 * @cfg {String} method default POST
27455 * @cfg {Boolean} loadMask (true|false) default true
27456 * @cfg {Boolean} loadingText default 'Loading...'
27459 * Create a new UploadCropbox
27460 * @param {Object} config The config object
27463 Roo.bootstrap.UploadCropbox = function(config){
27464 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27468 * @event beforeselectfile
27469 * Fire before select file
27470 * @param {Roo.bootstrap.UploadCropbox} this
27472 "beforeselectfile" : true,
27475 * Fire after initEvent
27476 * @param {Roo.bootstrap.UploadCropbox} this
27481 * Fire after initEvent
27482 * @param {Roo.bootstrap.UploadCropbox} this
27483 * @param {String} data
27488 * Fire when preparing the file data
27489 * @param {Roo.bootstrap.UploadCropbox} this
27490 * @param {Object} file
27495 * Fire when get exception
27496 * @param {Roo.bootstrap.UploadCropbox} this
27497 * @param {XMLHttpRequest} xhr
27499 "exception" : true,
27501 * @event beforeloadcanvas
27502 * Fire before load the canvas
27503 * @param {Roo.bootstrap.UploadCropbox} this
27504 * @param {String} src
27506 "beforeloadcanvas" : true,
27509 * Fire when trash image
27510 * @param {Roo.bootstrap.UploadCropbox} this
27515 * Fire when download the image
27516 * @param {Roo.bootstrap.UploadCropbox} this
27520 * @event footerbuttonclick
27521 * Fire when footerbuttonclick
27522 * @param {Roo.bootstrap.UploadCropbox} this
27523 * @param {String} type
27525 "footerbuttonclick" : true,
27529 * @param {Roo.bootstrap.UploadCropbox} this
27534 * Fire when rotate the image
27535 * @param {Roo.bootstrap.UploadCropbox} this
27536 * @param {String} pos
27541 * Fire when inspect the file
27542 * @param {Roo.bootstrap.UploadCropbox} this
27543 * @param {Object} file
27548 * Fire when xhr upload the file
27549 * @param {Roo.bootstrap.UploadCropbox} this
27550 * @param {Object} data
27555 * Fire when arrange the file data
27556 * @param {Roo.bootstrap.UploadCropbox} this
27557 * @param {Object} formData
27562 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27565 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27567 emptyText : 'Click to upload image',
27568 rotateNotify : 'Image is too small to rotate',
27569 errorTimeout : 3000,
27583 cropType : 'image/jpeg',
27585 canvasLoaded : false,
27586 isDocument : false,
27588 paramName : 'imageUpload',
27590 loadingText : 'Loading...',
27593 getAutoCreate : function()
27597 cls : 'roo-upload-cropbox',
27601 cls : 'roo-upload-cropbox-selector',
27606 cls : 'roo-upload-cropbox-body',
27607 style : 'cursor:pointer',
27611 cls : 'roo-upload-cropbox-preview'
27615 cls : 'roo-upload-cropbox-thumb'
27619 cls : 'roo-upload-cropbox-empty-notify',
27620 html : this.emptyText
27624 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27625 html : this.rotateNotify
27631 cls : 'roo-upload-cropbox-footer',
27634 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27644 onRender : function(ct, position)
27646 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27648 if (this.buttons.length) {
27650 Roo.each(this.buttons, function(bb) {
27652 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27654 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27660 this.maskEl = this.el;
27664 initEvents : function()
27666 this.urlAPI = (window.createObjectURL && window) ||
27667 (window.URL && URL.revokeObjectURL && URL) ||
27668 (window.webkitURL && webkitURL);
27670 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27671 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27673 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27674 this.selectorEl.hide();
27676 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27677 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27679 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27680 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27681 this.thumbEl.hide();
27683 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27684 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27686 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27687 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27688 this.errorEl.hide();
27690 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27691 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27692 this.footerEl.hide();
27694 this.setThumbBoxSize();
27700 this.fireEvent('initial', this);
27707 window.addEventListener("resize", function() { _this.resize(); } );
27709 this.bodyEl.on('click', this.beforeSelectFile, this);
27712 this.bodyEl.on('touchstart', this.onTouchStart, this);
27713 this.bodyEl.on('touchmove', this.onTouchMove, this);
27714 this.bodyEl.on('touchend', this.onTouchEnd, this);
27718 this.bodyEl.on('mousedown', this.onMouseDown, this);
27719 this.bodyEl.on('mousemove', this.onMouseMove, this);
27720 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27721 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27722 Roo.get(document).on('mouseup', this.onMouseUp, this);
27725 this.selectorEl.on('change', this.onFileSelected, this);
27731 this.baseScale = 1;
27733 this.baseRotate = 1;
27734 this.dragable = false;
27735 this.pinching = false;
27738 this.cropData = false;
27739 this.notifyEl.dom.innerHTML = this.emptyText;
27741 this.selectorEl.dom.value = '';
27745 resize : function()
27747 if(this.fireEvent('resize', this) != false){
27748 this.setThumbBoxPosition();
27749 this.setCanvasPosition();
27753 onFooterButtonClick : function(e, el, o, type)
27756 case 'rotate-left' :
27757 this.onRotateLeft(e);
27759 case 'rotate-right' :
27760 this.onRotateRight(e);
27763 this.beforeSelectFile(e);
27778 this.fireEvent('footerbuttonclick', this, type);
27781 beforeSelectFile : function(e)
27783 e.preventDefault();
27785 if(this.fireEvent('beforeselectfile', this) != false){
27786 this.selectorEl.dom.click();
27790 onFileSelected : function(e)
27792 e.preventDefault();
27794 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27798 var file = this.selectorEl.dom.files[0];
27800 if(this.fireEvent('inspect', this, file) != false){
27801 this.prepare(file);
27806 trash : function(e)
27808 this.fireEvent('trash', this);
27811 download : function(e)
27813 this.fireEvent('download', this);
27816 loadCanvas : function(src)
27818 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27822 this.imageEl = document.createElement('img');
27826 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27828 this.imageEl.src = src;
27832 onLoadCanvas : function()
27834 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27835 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27837 this.bodyEl.un('click', this.beforeSelectFile, this);
27839 this.notifyEl.hide();
27840 this.thumbEl.show();
27841 this.footerEl.show();
27843 this.baseRotateLevel();
27845 if(this.isDocument){
27846 this.setThumbBoxSize();
27849 this.setThumbBoxPosition();
27851 this.baseScaleLevel();
27857 this.canvasLoaded = true;
27860 this.maskEl.unmask();
27865 setCanvasPosition : function()
27867 if(!this.canvasEl){
27871 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27872 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27874 this.previewEl.setLeft(pw);
27875 this.previewEl.setTop(ph);
27879 onMouseDown : function(e)
27883 this.dragable = true;
27884 this.pinching = false;
27886 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27887 this.dragable = false;
27891 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27892 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27896 onMouseMove : function(e)
27900 if(!this.canvasLoaded){
27904 if (!this.dragable){
27908 var minX = Math.ceil(this.thumbEl.getLeft(true));
27909 var minY = Math.ceil(this.thumbEl.getTop(true));
27911 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27912 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27914 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27915 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27917 x = x - this.mouseX;
27918 y = y - this.mouseY;
27920 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27921 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27923 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27924 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27926 this.previewEl.setLeft(bgX);
27927 this.previewEl.setTop(bgY);
27929 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27930 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27933 onMouseUp : function(e)
27937 this.dragable = false;
27940 onMouseWheel : function(e)
27944 this.startScale = this.scale;
27946 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27948 if(!this.zoomable()){
27949 this.scale = this.startScale;
27958 zoomable : function()
27960 var minScale = this.thumbEl.getWidth() / this.minWidth;
27962 if(this.minWidth < this.minHeight){
27963 minScale = this.thumbEl.getHeight() / this.minHeight;
27966 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27967 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27971 (this.rotate == 0 || this.rotate == 180) &&
27973 width > this.imageEl.OriginWidth ||
27974 height > this.imageEl.OriginHeight ||
27975 (width < this.minWidth && height < this.minHeight)
27983 (this.rotate == 90 || this.rotate == 270) &&
27985 width > this.imageEl.OriginWidth ||
27986 height > this.imageEl.OriginHeight ||
27987 (width < this.minHeight && height < this.minWidth)
27994 !this.isDocument &&
27995 (this.rotate == 0 || this.rotate == 180) &&
27997 width < this.minWidth ||
27998 width > this.imageEl.OriginWidth ||
27999 height < this.minHeight ||
28000 height > this.imageEl.OriginHeight
28007 !this.isDocument &&
28008 (this.rotate == 90 || this.rotate == 270) &&
28010 width < this.minHeight ||
28011 width > this.imageEl.OriginWidth ||
28012 height < this.minWidth ||
28013 height > this.imageEl.OriginHeight
28023 onRotateLeft : function(e)
28025 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28027 var minScale = this.thumbEl.getWidth() / this.minWidth;
28029 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28030 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28032 this.startScale = this.scale;
28034 while (this.getScaleLevel() < minScale){
28036 this.scale = this.scale + 1;
28038 if(!this.zoomable()){
28043 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28044 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28049 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28056 this.scale = this.startScale;
28058 this.onRotateFail();
28063 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28065 if(this.isDocument){
28066 this.setThumbBoxSize();
28067 this.setThumbBoxPosition();
28068 this.setCanvasPosition();
28073 this.fireEvent('rotate', this, 'left');
28077 onRotateRight : function(e)
28079 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28081 var minScale = this.thumbEl.getWidth() / this.minWidth;
28083 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28084 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28086 this.startScale = this.scale;
28088 while (this.getScaleLevel() < minScale){
28090 this.scale = this.scale + 1;
28092 if(!this.zoomable()){
28097 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28098 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28103 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28110 this.scale = this.startScale;
28112 this.onRotateFail();
28117 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28119 if(this.isDocument){
28120 this.setThumbBoxSize();
28121 this.setThumbBoxPosition();
28122 this.setCanvasPosition();
28127 this.fireEvent('rotate', this, 'right');
28130 onRotateFail : function()
28132 this.errorEl.show(true);
28136 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28141 this.previewEl.dom.innerHTML = '';
28143 var canvasEl = document.createElement("canvas");
28145 var contextEl = canvasEl.getContext("2d");
28147 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28148 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28149 var center = this.imageEl.OriginWidth / 2;
28151 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28152 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28153 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28154 center = this.imageEl.OriginHeight / 2;
28157 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28159 contextEl.translate(center, center);
28160 contextEl.rotate(this.rotate * Math.PI / 180);
28162 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28164 this.canvasEl = document.createElement("canvas");
28166 this.contextEl = this.canvasEl.getContext("2d");
28168 switch (this.rotate) {
28171 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28172 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28174 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28179 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28180 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28182 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28183 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);
28187 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28192 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28193 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28195 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28196 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);
28200 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);
28205 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28206 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28208 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28209 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28213 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);
28220 this.previewEl.appendChild(this.canvasEl);
28222 this.setCanvasPosition();
28227 if(!this.canvasLoaded){
28231 var imageCanvas = document.createElement("canvas");
28233 var imageContext = imageCanvas.getContext("2d");
28235 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28236 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28238 var center = imageCanvas.width / 2;
28240 imageContext.translate(center, center);
28242 imageContext.rotate(this.rotate * Math.PI / 180);
28244 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28246 var canvas = document.createElement("canvas");
28248 var context = canvas.getContext("2d");
28250 canvas.width = this.minWidth;
28251 canvas.height = this.minHeight;
28253 switch (this.rotate) {
28256 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28257 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28259 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28260 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28262 var targetWidth = this.minWidth - 2 * x;
28263 var targetHeight = this.minHeight - 2 * y;
28267 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28268 scale = targetWidth / width;
28271 if(x > 0 && y == 0){
28272 scale = targetHeight / height;
28275 if(x > 0 && y > 0){
28276 scale = targetWidth / width;
28278 if(width < height){
28279 scale = targetHeight / height;
28283 context.scale(scale, scale);
28285 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28286 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28288 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28289 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28291 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28296 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28297 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28299 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28300 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28302 var targetWidth = this.minWidth - 2 * x;
28303 var targetHeight = this.minHeight - 2 * y;
28307 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28308 scale = targetWidth / width;
28311 if(x > 0 && y == 0){
28312 scale = targetHeight / height;
28315 if(x > 0 && y > 0){
28316 scale = targetWidth / width;
28318 if(width < height){
28319 scale = targetHeight / height;
28323 context.scale(scale, scale);
28325 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28326 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28328 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28329 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28331 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28333 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28338 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28339 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28341 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28342 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28344 var targetWidth = this.minWidth - 2 * x;
28345 var targetHeight = this.minHeight - 2 * y;
28349 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28350 scale = targetWidth / width;
28353 if(x > 0 && y == 0){
28354 scale = targetHeight / height;
28357 if(x > 0 && y > 0){
28358 scale = targetWidth / width;
28360 if(width < height){
28361 scale = targetHeight / height;
28365 context.scale(scale, scale);
28367 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28368 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28370 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28371 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28373 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28374 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28376 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28381 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28382 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28384 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28385 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28387 var targetWidth = this.minWidth - 2 * x;
28388 var targetHeight = this.minHeight - 2 * y;
28392 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28393 scale = targetWidth / width;
28396 if(x > 0 && y == 0){
28397 scale = targetHeight / height;
28400 if(x > 0 && y > 0){
28401 scale = targetWidth / width;
28403 if(width < height){
28404 scale = targetHeight / height;
28408 context.scale(scale, scale);
28410 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28411 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28413 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28414 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28416 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28418 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28425 this.cropData = canvas.toDataURL(this.cropType);
28427 if(this.fireEvent('crop', this, this.cropData) !== false){
28428 this.process(this.file, this.cropData);
28435 setThumbBoxSize : function()
28439 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28440 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28441 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28443 this.minWidth = width;
28444 this.minHeight = height;
28446 if(this.rotate == 90 || this.rotate == 270){
28447 this.minWidth = height;
28448 this.minHeight = width;
28453 width = Math.ceil(this.minWidth * height / this.minHeight);
28455 if(this.minWidth > this.minHeight){
28457 height = Math.ceil(this.minHeight * width / this.minWidth);
28460 this.thumbEl.setStyle({
28461 width : width + 'px',
28462 height : height + 'px'
28469 setThumbBoxPosition : function()
28471 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28472 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28474 this.thumbEl.setLeft(x);
28475 this.thumbEl.setTop(y);
28479 baseRotateLevel : function()
28481 this.baseRotate = 1;
28484 typeof(this.exif) != 'undefined' &&
28485 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28486 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28488 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28491 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28495 baseScaleLevel : function()
28499 if(this.isDocument){
28501 if(this.baseRotate == 6 || this.baseRotate == 8){
28503 height = this.thumbEl.getHeight();
28504 this.baseScale = height / this.imageEl.OriginWidth;
28506 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28507 width = this.thumbEl.getWidth();
28508 this.baseScale = width / this.imageEl.OriginHeight;
28514 height = this.thumbEl.getHeight();
28515 this.baseScale = height / this.imageEl.OriginHeight;
28517 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28518 width = this.thumbEl.getWidth();
28519 this.baseScale = width / this.imageEl.OriginWidth;
28525 if(this.baseRotate == 6 || this.baseRotate == 8){
28527 width = this.thumbEl.getHeight();
28528 this.baseScale = width / this.imageEl.OriginHeight;
28530 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28531 height = this.thumbEl.getWidth();
28532 this.baseScale = height / this.imageEl.OriginHeight;
28535 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28536 height = this.thumbEl.getWidth();
28537 this.baseScale = height / this.imageEl.OriginHeight;
28539 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28540 width = this.thumbEl.getHeight();
28541 this.baseScale = width / this.imageEl.OriginWidth;
28548 width = this.thumbEl.getWidth();
28549 this.baseScale = width / this.imageEl.OriginWidth;
28551 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28552 height = this.thumbEl.getHeight();
28553 this.baseScale = height / this.imageEl.OriginHeight;
28556 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28558 height = this.thumbEl.getHeight();
28559 this.baseScale = height / this.imageEl.OriginHeight;
28561 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28562 width = this.thumbEl.getWidth();
28563 this.baseScale = width / this.imageEl.OriginWidth;
28571 getScaleLevel : function()
28573 return this.baseScale * Math.pow(1.1, this.scale);
28576 onTouchStart : function(e)
28578 if(!this.canvasLoaded){
28579 this.beforeSelectFile(e);
28583 var touches = e.browserEvent.touches;
28589 if(touches.length == 1){
28590 this.onMouseDown(e);
28594 if(touches.length != 2){
28600 for(var i = 0, finger; finger = touches[i]; i++){
28601 coords.push(finger.pageX, finger.pageY);
28604 var x = Math.pow(coords[0] - coords[2], 2);
28605 var y = Math.pow(coords[1] - coords[3], 2);
28607 this.startDistance = Math.sqrt(x + y);
28609 this.startScale = this.scale;
28611 this.pinching = true;
28612 this.dragable = false;
28616 onTouchMove : function(e)
28618 if(!this.pinching && !this.dragable){
28622 var touches = e.browserEvent.touches;
28629 this.onMouseMove(e);
28635 for(var i = 0, finger; finger = touches[i]; i++){
28636 coords.push(finger.pageX, finger.pageY);
28639 var x = Math.pow(coords[0] - coords[2], 2);
28640 var y = Math.pow(coords[1] - coords[3], 2);
28642 this.endDistance = Math.sqrt(x + y);
28644 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28646 if(!this.zoomable()){
28647 this.scale = this.startScale;
28655 onTouchEnd : function(e)
28657 this.pinching = false;
28658 this.dragable = false;
28662 process : function(file, crop)
28665 this.maskEl.mask(this.loadingText);
28668 this.xhr = new XMLHttpRequest();
28670 file.xhr = this.xhr;
28672 this.xhr.open(this.method, this.url, true);
28675 "Accept": "application/json",
28676 "Cache-Control": "no-cache",
28677 "X-Requested-With": "XMLHttpRequest"
28680 for (var headerName in headers) {
28681 var headerValue = headers[headerName];
28683 this.xhr.setRequestHeader(headerName, headerValue);
28689 this.xhr.onload = function()
28691 _this.xhrOnLoad(_this.xhr);
28694 this.xhr.onerror = function()
28696 _this.xhrOnError(_this.xhr);
28699 var formData = new FormData();
28701 formData.append('returnHTML', 'NO');
28704 formData.append('crop', crop);
28707 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28708 formData.append(this.paramName, file, file.name);
28711 if(typeof(file.filename) != 'undefined'){
28712 formData.append('filename', file.filename);
28715 if(typeof(file.mimetype) != 'undefined'){
28716 formData.append('mimetype', file.mimetype);
28719 if(this.fireEvent('arrange', this, formData) != false){
28720 this.xhr.send(formData);
28724 xhrOnLoad : function(xhr)
28727 this.maskEl.unmask();
28730 if (xhr.readyState !== 4) {
28731 this.fireEvent('exception', this, xhr);
28735 var response = Roo.decode(xhr.responseText);
28737 if(!response.success){
28738 this.fireEvent('exception', this, xhr);
28742 var response = Roo.decode(xhr.responseText);
28744 this.fireEvent('upload', this, response);
28748 xhrOnError : function()
28751 this.maskEl.unmask();
28754 Roo.log('xhr on error');
28756 var response = Roo.decode(xhr.responseText);
28762 prepare : function(file)
28765 this.maskEl.mask(this.loadingText);
28771 if(typeof(file) === 'string'){
28772 this.loadCanvas(file);
28776 if(!file || !this.urlAPI){
28781 this.cropType = file.type;
28785 if(this.fireEvent('prepare', this, this.file) != false){
28787 var reader = new FileReader();
28789 reader.onload = function (e) {
28790 if (e.target.error) {
28791 Roo.log(e.target.error);
28795 var buffer = e.target.result,
28796 dataView = new DataView(buffer),
28798 maxOffset = dataView.byteLength - 4,
28802 if (dataView.getUint16(0) === 0xffd8) {
28803 while (offset < maxOffset) {
28804 markerBytes = dataView.getUint16(offset);
28806 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28807 markerLength = dataView.getUint16(offset + 2) + 2;
28808 if (offset + markerLength > dataView.byteLength) {
28809 Roo.log('Invalid meta data: Invalid segment size.');
28813 if(markerBytes == 0xffe1){
28814 _this.parseExifData(
28821 offset += markerLength;
28831 var url = _this.urlAPI.createObjectURL(_this.file);
28833 _this.loadCanvas(url);
28838 reader.readAsArrayBuffer(this.file);
28844 parseExifData : function(dataView, offset, length)
28846 var tiffOffset = offset + 10,
28850 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28851 // No Exif data, might be XMP data instead
28855 // Check for the ASCII code for "Exif" (0x45786966):
28856 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28857 // No Exif data, might be XMP data instead
28860 if (tiffOffset + 8 > dataView.byteLength) {
28861 Roo.log('Invalid Exif data: Invalid segment size.');
28864 // Check for the two null bytes:
28865 if (dataView.getUint16(offset + 8) !== 0x0000) {
28866 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28869 // Check the byte alignment:
28870 switch (dataView.getUint16(tiffOffset)) {
28872 littleEndian = true;
28875 littleEndian = false;
28878 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28881 // Check for the TIFF tag marker (0x002A):
28882 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28883 Roo.log('Invalid Exif data: Missing TIFF marker.');
28886 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28887 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28889 this.parseExifTags(
28892 tiffOffset + dirOffset,
28897 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28902 if (dirOffset + 6 > dataView.byteLength) {
28903 Roo.log('Invalid Exif data: Invalid directory offset.');
28906 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28907 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28908 if (dirEndOffset + 4 > dataView.byteLength) {
28909 Roo.log('Invalid Exif data: Invalid directory size.');
28912 for (i = 0; i < tagsNumber; i += 1) {
28916 dirOffset + 2 + 12 * i, // tag offset
28920 // Return the offset to the next directory:
28921 return dataView.getUint32(dirEndOffset, littleEndian);
28924 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28926 var tag = dataView.getUint16(offset, littleEndian);
28928 this.exif[tag] = this.getExifValue(
28932 dataView.getUint16(offset + 2, littleEndian), // tag type
28933 dataView.getUint32(offset + 4, littleEndian), // tag length
28938 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28940 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28949 Roo.log('Invalid Exif data: Invalid tag type.');
28953 tagSize = tagType.size * length;
28954 // Determine if the value is contained in the dataOffset bytes,
28955 // or if the value at the dataOffset is a pointer to the actual data:
28956 dataOffset = tagSize > 4 ?
28957 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28958 if (dataOffset + tagSize > dataView.byteLength) {
28959 Roo.log('Invalid Exif data: Invalid data offset.');
28962 if (length === 1) {
28963 return tagType.getValue(dataView, dataOffset, littleEndian);
28966 for (i = 0; i < length; i += 1) {
28967 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28970 if (tagType.ascii) {
28972 // Concatenate the chars:
28973 for (i = 0; i < values.length; i += 1) {
28975 // Ignore the terminating NULL byte(s):
28976 if (c === '\u0000') {
28988 Roo.apply(Roo.bootstrap.UploadCropbox, {
28990 'Orientation': 0x0112
28994 1: 0, //'top-left',
28996 3: 180, //'bottom-right',
28997 // 4: 'bottom-left',
28999 6: 90, //'right-top',
29000 // 7: 'right-bottom',
29001 8: 270 //'left-bottom'
29005 // byte, 8-bit unsigned int:
29007 getValue: function (dataView, dataOffset) {
29008 return dataView.getUint8(dataOffset);
29012 // ascii, 8-bit byte:
29014 getValue: function (dataView, dataOffset) {
29015 return String.fromCharCode(dataView.getUint8(dataOffset));
29020 // short, 16 bit int:
29022 getValue: function (dataView, dataOffset, littleEndian) {
29023 return dataView.getUint16(dataOffset, littleEndian);
29027 // long, 32 bit int:
29029 getValue: function (dataView, dataOffset, littleEndian) {
29030 return dataView.getUint32(dataOffset, littleEndian);
29034 // rational = two long values, first is numerator, second is denominator:
29036 getValue: function (dataView, dataOffset, littleEndian) {
29037 return dataView.getUint32(dataOffset, littleEndian) /
29038 dataView.getUint32(dataOffset + 4, littleEndian);
29042 // slong, 32 bit signed int:
29044 getValue: function (dataView, dataOffset, littleEndian) {
29045 return dataView.getInt32(dataOffset, littleEndian);
29049 // srational, two slongs, first is numerator, second is denominator:
29051 getValue: function (dataView, dataOffset, littleEndian) {
29052 return dataView.getInt32(dataOffset, littleEndian) /
29053 dataView.getInt32(dataOffset + 4, littleEndian);
29063 cls : 'btn-group roo-upload-cropbox-rotate-left',
29064 action : 'rotate-left',
29068 cls : 'btn btn-default',
29069 html : '<i class="fa fa-undo"></i>'
29075 cls : 'btn-group roo-upload-cropbox-picture',
29076 action : 'picture',
29080 cls : 'btn btn-default',
29081 html : '<i class="fa fa-picture-o"></i>'
29087 cls : 'btn-group roo-upload-cropbox-rotate-right',
29088 action : 'rotate-right',
29092 cls : 'btn btn-default',
29093 html : '<i class="fa fa-repeat"></i>'
29101 cls : 'btn-group roo-upload-cropbox-rotate-left',
29102 action : 'rotate-left',
29106 cls : 'btn btn-default',
29107 html : '<i class="fa fa-undo"></i>'
29113 cls : 'btn-group roo-upload-cropbox-download',
29114 action : 'download',
29118 cls : 'btn btn-default',
29119 html : '<i class="fa fa-download"></i>'
29125 cls : 'btn-group roo-upload-cropbox-crop',
29130 cls : 'btn btn-default',
29131 html : '<i class="fa fa-crop"></i>'
29137 cls : 'btn-group roo-upload-cropbox-trash',
29142 cls : 'btn btn-default',
29143 html : '<i class="fa fa-trash"></i>'
29149 cls : 'btn-group roo-upload-cropbox-rotate-right',
29150 action : 'rotate-right',
29154 cls : 'btn btn-default',
29155 html : '<i class="fa fa-repeat"></i>'
29163 cls : 'btn-group roo-upload-cropbox-rotate-left',
29164 action : 'rotate-left',
29168 cls : 'btn btn-default',
29169 html : '<i class="fa fa-undo"></i>'
29175 cls : 'btn-group roo-upload-cropbox-rotate-right',
29176 action : 'rotate-right',
29180 cls : 'btn btn-default',
29181 html : '<i class="fa fa-repeat"></i>'
29194 * @class Roo.bootstrap.DocumentManager
29195 * @extends Roo.bootstrap.Component
29196 * Bootstrap DocumentManager class
29197 * @cfg {String} paramName default 'imageUpload'
29198 * @cfg {String} toolTipName default 'filename'
29199 * @cfg {String} method default POST
29200 * @cfg {String} url action url
29201 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29202 * @cfg {Boolean} multiple multiple upload default true
29203 * @cfg {Number} thumbSize default 300
29204 * @cfg {String} fieldLabel
29205 * @cfg {Number} labelWidth default 4
29206 * @cfg {String} labelAlign (left|top) default left
29207 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29208 * @cfg {Number} labellg set the width of label (1-12)
29209 * @cfg {Number} labelmd set the width of label (1-12)
29210 * @cfg {Number} labelsm set the width of label (1-12)
29211 * @cfg {Number} labelxs set the width of label (1-12)
29214 * Create a new DocumentManager
29215 * @param {Object} config The config object
29218 Roo.bootstrap.DocumentManager = function(config){
29219 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29222 this.delegates = [];
29227 * Fire when initial the DocumentManager
29228 * @param {Roo.bootstrap.DocumentManager} this
29233 * inspect selected file
29234 * @param {Roo.bootstrap.DocumentManager} this
29235 * @param {File} file
29240 * Fire when xhr load exception
29241 * @param {Roo.bootstrap.DocumentManager} this
29242 * @param {XMLHttpRequest} xhr
29244 "exception" : true,
29246 * @event afterupload
29247 * Fire when xhr load exception
29248 * @param {Roo.bootstrap.DocumentManager} this
29249 * @param {XMLHttpRequest} xhr
29251 "afterupload" : true,
29254 * prepare the form data
29255 * @param {Roo.bootstrap.DocumentManager} this
29256 * @param {Object} formData
29261 * Fire when remove the file
29262 * @param {Roo.bootstrap.DocumentManager} this
29263 * @param {Object} file
29268 * Fire after refresh the file
29269 * @param {Roo.bootstrap.DocumentManager} this
29274 * Fire after click the image
29275 * @param {Roo.bootstrap.DocumentManager} this
29276 * @param {Object} file
29281 * Fire when upload a image and editable set to true
29282 * @param {Roo.bootstrap.DocumentManager} this
29283 * @param {Object} file
29287 * @event beforeselectfile
29288 * Fire before select file
29289 * @param {Roo.bootstrap.DocumentManager} this
29291 "beforeselectfile" : true,
29294 * Fire before process file
29295 * @param {Roo.bootstrap.DocumentManager} this
29296 * @param {Object} file
29300 * @event previewrendered
29301 * Fire when preview rendered
29302 * @param {Roo.bootstrap.DocumentManager} this
29303 * @param {Object} file
29305 "previewrendered" : true,
29308 "previewResize" : true
29313 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
29322 paramName : 'imageUpload',
29323 toolTipName : 'filename',
29326 labelAlign : 'left',
29336 getAutoCreate : function()
29338 var managerWidget = {
29340 cls : 'roo-document-manager',
29344 cls : 'roo-document-manager-selector',
29349 cls : 'roo-document-manager-uploader',
29353 cls : 'roo-document-manager-upload-btn',
29354 html : '<i class="fa fa-plus"></i>'
29365 cls : 'column col-md-12',
29370 if(this.fieldLabel.length){
29375 cls : 'column col-md-12',
29376 html : this.fieldLabel
29380 cls : 'column col-md-12',
29385 if(this.labelAlign == 'left'){
29390 html : this.fieldLabel
29399 if(this.labelWidth > 12){
29400 content[0].style = "width: " + this.labelWidth + 'px';
29403 if(this.labelWidth < 13 && this.labelmd == 0){
29404 this.labelmd = this.labelWidth;
29407 if(this.labellg > 0){
29408 content[0].cls += ' col-lg-' + this.labellg;
29409 content[1].cls += ' col-lg-' + (12 - this.labellg);
29412 if(this.labelmd > 0){
29413 content[0].cls += ' col-md-' + this.labelmd;
29414 content[1].cls += ' col-md-' + (12 - this.labelmd);
29417 if(this.labelsm > 0){
29418 content[0].cls += ' col-sm-' + this.labelsm;
29419 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29422 if(this.labelxs > 0){
29423 content[0].cls += ' col-xs-' + this.labelxs;
29424 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29432 cls : 'row clearfix',
29440 initEvents : function()
29442 this.managerEl = this.el.select('.roo-document-manager', true).first();
29443 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29445 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29446 this.selectorEl.hide();
29449 this.selectorEl.attr('multiple', 'multiple');
29452 this.selectorEl.on('change', this.onFileSelected, this);
29454 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29455 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29457 this.uploader.on('click', this.onUploaderClick, this);
29459 this.renderProgressDialog();
29463 window.addEventListener("resize", function() { _this.refresh(); } );
29465 this.fireEvent('initial', this);
29468 renderProgressDialog : function()
29472 this.progressDialog = new Roo.bootstrap.Modal({
29473 cls : 'roo-document-manager-progress-dialog',
29474 allow_close : false,
29485 btnclick : function() {
29486 _this.uploadCancel();
29492 this.progressDialog.render(Roo.get(document.body));
29494 this.progress = new Roo.bootstrap.Progress({
29495 cls : 'roo-document-manager-progress',
29500 this.progress.render(this.progressDialog.getChildContainer());
29502 this.progressBar = new Roo.bootstrap.ProgressBar({
29503 cls : 'roo-document-manager-progress-bar',
29506 aria_valuemax : 12,
29510 this.progressBar.render(this.progress.getChildContainer());
29513 onUploaderClick : function(e)
29515 e.preventDefault();
29517 if(this.fireEvent('beforeselectfile', this) != false){
29518 this.selectorEl.dom.click();
29523 onFileSelected : function(e)
29525 e.preventDefault();
29527 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29531 Roo.each(this.selectorEl.dom.files, function(file){
29532 if(this.fireEvent('inspect', this, file) != false){
29533 this.files.push(file);
29543 this.selectorEl.dom.value = '';
29545 if(!this.files || !this.files.length){
29549 if(this.boxes > 0 && this.files.length > this.boxes){
29550 this.files = this.files.slice(0, this.boxes);
29553 this.uploader.show();
29555 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29556 this.uploader.hide();
29565 Roo.each(this.files, function(file){
29567 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29568 var f = this.renderPreview(file);
29573 if(file.type.indexOf('image') != -1){
29574 this.delegates.push(
29576 _this.process(file);
29577 }).createDelegate(this)
29585 _this.process(file);
29586 }).createDelegate(this)
29591 this.files = files;
29593 this.delegates = this.delegates.concat(docs);
29595 if(!this.delegates.length){
29600 this.progressBar.aria_valuemax = this.delegates.length;
29607 arrange : function()
29609 if(!this.delegates.length){
29610 this.progressDialog.hide();
29615 var delegate = this.delegates.shift();
29617 this.progressDialog.show();
29619 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29621 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29626 refresh : function()
29628 this.uploader.show();
29630 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29631 this.uploader.hide();
29634 Roo.isTouch ? this.closable(false) : this.closable(true);
29636 this.fireEvent('refresh', this);
29639 onRemove : function(e, el, o)
29641 e.preventDefault();
29643 this.fireEvent('remove', this, o);
29647 remove : function(o)
29651 Roo.each(this.files, function(file){
29652 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29661 this.files = files;
29668 Roo.each(this.files, function(file){
29673 file.target.remove();
29682 onClick : function(e, el, o)
29684 e.preventDefault();
29686 this.fireEvent('click', this, o);
29690 closable : function(closable)
29692 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29694 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29706 xhrOnLoad : function(xhr)
29708 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29712 if (xhr.readyState !== 4) {
29714 this.fireEvent('exception', this, xhr);
29718 var response = Roo.decode(xhr.responseText);
29720 if(!response.success){
29722 this.fireEvent('exception', this, xhr);
29726 var file = this.renderPreview(response.data);
29728 this.files.push(file);
29732 this.fireEvent('afterupload', this, xhr);
29736 xhrOnError : function(xhr)
29738 Roo.log('xhr on error');
29740 var response = Roo.decode(xhr.responseText);
29747 process : function(file)
29749 if(this.fireEvent('process', this, file) !== false){
29750 if(this.editable && file.type.indexOf('image') != -1){
29751 this.fireEvent('edit', this, file);
29755 this.uploadStart(file, false);
29762 uploadStart : function(file, crop)
29764 this.xhr = new XMLHttpRequest();
29766 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29771 file.xhr = this.xhr;
29773 this.managerEl.createChild({
29775 cls : 'roo-document-manager-loading',
29779 tooltip : file.name,
29780 cls : 'roo-document-manager-thumb',
29781 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29787 this.xhr.open(this.method, this.url, true);
29790 "Accept": "application/json",
29791 "Cache-Control": "no-cache",
29792 "X-Requested-With": "XMLHttpRequest"
29795 for (var headerName in headers) {
29796 var headerValue = headers[headerName];
29798 this.xhr.setRequestHeader(headerName, headerValue);
29804 this.xhr.onload = function()
29806 _this.xhrOnLoad(_this.xhr);
29809 this.xhr.onerror = function()
29811 _this.xhrOnError(_this.xhr);
29814 var formData = new FormData();
29816 formData.append('returnHTML', 'NO');
29819 formData.append('crop', crop);
29822 formData.append(this.paramName, file, file.name);
29829 if(this.fireEvent('prepare', this, formData, options) != false){
29831 if(options.manually){
29835 this.xhr.send(formData);
29839 this.uploadCancel();
29842 uploadCancel : function()
29848 this.delegates = [];
29850 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29857 renderPreview : function(file)
29859 if(typeof(file.target) != 'undefined' && file.target){
29863 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29865 var previewEl = this.managerEl.createChild({
29867 cls : 'roo-document-manager-preview',
29871 tooltip : file[this.toolTipName],
29872 cls : 'roo-document-manager-thumb',
29873 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29878 html : '<i class="fa fa-times-circle"></i>'
29883 var close = previewEl.select('button.close', true).first();
29885 close.on('click', this.onRemove, this, file);
29887 file.target = previewEl;
29889 var image = previewEl.select('img', true).first();
29893 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29895 image.on('click', this.onClick, this, file);
29897 this.fireEvent('previewrendered', this, file);
29903 onPreviewLoad : function(file, image)
29905 if(typeof(file.target) == 'undefined' || !file.target){
29909 var width = image.dom.naturalWidth || image.dom.width;
29910 var height = image.dom.naturalHeight || image.dom.height;
29912 if(!this.previewResize) {
29916 if(width > height){
29917 file.target.addClass('wide');
29921 file.target.addClass('tall');
29926 uploadFromSource : function(file, crop)
29928 this.xhr = new XMLHttpRequest();
29930 this.managerEl.createChild({
29932 cls : 'roo-document-manager-loading',
29936 tooltip : file.name,
29937 cls : 'roo-document-manager-thumb',
29938 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29944 this.xhr.open(this.method, this.url, true);
29947 "Accept": "application/json",
29948 "Cache-Control": "no-cache",
29949 "X-Requested-With": "XMLHttpRequest"
29952 for (var headerName in headers) {
29953 var headerValue = headers[headerName];
29955 this.xhr.setRequestHeader(headerName, headerValue);
29961 this.xhr.onload = function()
29963 _this.xhrOnLoad(_this.xhr);
29966 this.xhr.onerror = function()
29968 _this.xhrOnError(_this.xhr);
29971 var formData = new FormData();
29973 formData.append('returnHTML', 'NO');
29975 formData.append('crop', crop);
29977 if(typeof(file.filename) != 'undefined'){
29978 formData.append('filename', file.filename);
29981 if(typeof(file.mimetype) != 'undefined'){
29982 formData.append('mimetype', file.mimetype);
29987 if(this.fireEvent('prepare', this, formData) != false){
29988 this.xhr.send(formData);
29998 * @class Roo.bootstrap.DocumentViewer
29999 * @extends Roo.bootstrap.Component
30000 * Bootstrap DocumentViewer class
30001 * @cfg {Boolean} showDownload (true|false) show download button (default true)
30002 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30005 * Create a new DocumentViewer
30006 * @param {Object} config The config object
30009 Roo.bootstrap.DocumentViewer = function(config){
30010 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30015 * Fire after initEvent
30016 * @param {Roo.bootstrap.DocumentViewer} this
30022 * @param {Roo.bootstrap.DocumentViewer} this
30027 * Fire after download button
30028 * @param {Roo.bootstrap.DocumentViewer} this
30033 * Fire after trash button
30034 * @param {Roo.bootstrap.DocumentViewer} this
30041 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
30043 showDownload : true,
30047 getAutoCreate : function()
30051 cls : 'roo-document-viewer',
30055 cls : 'roo-document-viewer-body',
30059 cls : 'roo-document-viewer-thumb',
30063 cls : 'roo-document-viewer-image'
30071 cls : 'roo-document-viewer-footer',
30074 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30078 cls : 'btn-group roo-document-viewer-download',
30082 cls : 'btn btn-default',
30083 html : '<i class="fa fa-download"></i>'
30089 cls : 'btn-group roo-document-viewer-trash',
30093 cls : 'btn btn-default',
30094 html : '<i class="fa fa-trash"></i>'
30107 initEvents : function()
30109 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30110 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30112 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30113 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30115 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30116 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30118 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30119 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30121 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30122 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30124 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30125 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30127 this.bodyEl.on('click', this.onClick, this);
30128 this.downloadBtn.on('click', this.onDownload, this);
30129 this.trashBtn.on('click', this.onTrash, this);
30131 this.downloadBtn.hide();
30132 this.trashBtn.hide();
30134 if(this.showDownload){
30135 this.downloadBtn.show();
30138 if(this.showTrash){
30139 this.trashBtn.show();
30142 if(!this.showDownload && !this.showTrash) {
30143 this.footerEl.hide();
30148 initial : function()
30150 this.fireEvent('initial', this);
30154 onClick : function(e)
30156 e.preventDefault();
30158 this.fireEvent('click', this);
30161 onDownload : function(e)
30163 e.preventDefault();
30165 this.fireEvent('download', this);
30168 onTrash : function(e)
30170 e.preventDefault();
30172 this.fireEvent('trash', this);
30184 * @class Roo.bootstrap.NavProgressBar
30185 * @extends Roo.bootstrap.Component
30186 * Bootstrap NavProgressBar class
30189 * Create a new nav progress bar
30190 * @param {Object} config The config object
30193 Roo.bootstrap.NavProgressBar = function(config){
30194 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30196 this.bullets = this.bullets || [];
30198 // Roo.bootstrap.NavProgressBar.register(this);
30202 * Fires when the active item changes
30203 * @param {Roo.bootstrap.NavProgressBar} this
30204 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30205 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
30212 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
30217 getAutoCreate : function()
30219 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30223 cls : 'roo-navigation-bar-group',
30227 cls : 'roo-navigation-top-bar'
30231 cls : 'roo-navigation-bullets-bar',
30235 cls : 'roo-navigation-bar'
30242 cls : 'roo-navigation-bottom-bar'
30252 initEvents: function()
30257 onRender : function(ct, position)
30259 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30261 if(this.bullets.length){
30262 Roo.each(this.bullets, function(b){
30271 addItem : function(cfg)
30273 var item = new Roo.bootstrap.NavProgressItem(cfg);
30275 item.parentId = this.id;
30276 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30279 var top = new Roo.bootstrap.Element({
30281 cls : 'roo-navigation-bar-text'
30284 var bottom = new Roo.bootstrap.Element({
30286 cls : 'roo-navigation-bar-text'
30289 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30290 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30292 var topText = new Roo.bootstrap.Element({
30294 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30297 var bottomText = new Roo.bootstrap.Element({
30299 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30302 topText.onRender(top.el, null);
30303 bottomText.onRender(bottom.el, null);
30306 item.bottomEl = bottom;
30309 this.barItems.push(item);
30314 getActive : function()
30316 var active = false;
30318 Roo.each(this.barItems, function(v){
30320 if (!v.isActive()) {
30332 setActiveItem : function(item)
30336 Roo.each(this.barItems, function(v){
30337 if (v.rid == item.rid) {
30341 if (v.isActive()) {
30342 v.setActive(false);
30347 item.setActive(true);
30349 this.fireEvent('changed', this, item, prev);
30352 getBarItem: function(rid)
30356 Roo.each(this.barItems, function(e) {
30357 if (e.rid != rid) {
30368 indexOfItem : function(item)
30372 Roo.each(this.barItems, function(v, i){
30374 if (v.rid != item.rid) {
30385 setActiveNext : function()
30387 var i = this.indexOfItem(this.getActive());
30389 if (i > this.barItems.length) {
30393 this.setActiveItem(this.barItems[i+1]);
30396 setActivePrev : function()
30398 var i = this.indexOfItem(this.getActive());
30404 this.setActiveItem(this.barItems[i-1]);
30407 format : function()
30409 if(!this.barItems.length){
30413 var width = 100 / this.barItems.length;
30415 Roo.each(this.barItems, function(i){
30416 i.el.setStyle('width', width + '%');
30417 i.topEl.el.setStyle('width', width + '%');
30418 i.bottomEl.el.setStyle('width', width + '%');
30427 * Nav Progress Item
30432 * @class Roo.bootstrap.NavProgressItem
30433 * @extends Roo.bootstrap.Component
30434 * Bootstrap NavProgressItem class
30435 * @cfg {String} rid the reference id
30436 * @cfg {Boolean} active (true|false) Is item active default false
30437 * @cfg {Boolean} disabled (true|false) Is item active default false
30438 * @cfg {String} html
30439 * @cfg {String} position (top|bottom) text position default bottom
30440 * @cfg {String} icon show icon instead of number
30443 * Create a new NavProgressItem
30444 * @param {Object} config The config object
30446 Roo.bootstrap.NavProgressItem = function(config){
30447 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30452 * The raw click event for the entire grid.
30453 * @param {Roo.bootstrap.NavProgressItem} this
30454 * @param {Roo.EventObject} e
30461 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30467 position : 'bottom',
30470 getAutoCreate : function()
30472 var iconCls = 'roo-navigation-bar-item-icon';
30474 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30478 cls: 'roo-navigation-bar-item',
30488 cfg.cls += ' active';
30491 cfg.cls += ' disabled';
30497 disable : function()
30499 this.setDisabled(true);
30502 enable : function()
30504 this.setDisabled(false);
30507 initEvents: function()
30509 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30511 this.iconEl.on('click', this.onClick, this);
30514 onClick : function(e)
30516 e.preventDefault();
30522 if(this.fireEvent('click', this, e) === false){
30526 this.parent().setActiveItem(this);
30529 isActive: function ()
30531 return this.active;
30534 setActive : function(state)
30536 if(this.active == state){
30540 this.active = state;
30543 this.el.addClass('active');
30547 this.el.removeClass('active');
30552 setDisabled : function(state)
30554 if(this.disabled == state){
30558 this.disabled = state;
30561 this.el.addClass('disabled');
30565 this.el.removeClass('disabled');
30568 tooltipEl : function()
30570 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30583 * @class Roo.bootstrap.FieldLabel
30584 * @extends Roo.bootstrap.Component
30585 * Bootstrap FieldLabel class
30586 * @cfg {String} html contents of the element
30587 * @cfg {String} tag tag of the element default label
30588 * @cfg {String} cls class of the element
30589 * @cfg {String} target label target
30590 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30591 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30592 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30593 * @cfg {String} iconTooltip default "This field is required"
30594 * @cfg {String} indicatorpos (left|right) default left
30597 * Create a new FieldLabel
30598 * @param {Object} config The config object
30601 Roo.bootstrap.FieldLabel = function(config){
30602 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30607 * Fires after the field has been marked as invalid.
30608 * @param {Roo.form.FieldLabel} this
30609 * @param {String} msg The validation message
30614 * Fires after the field has been validated with no errors.
30615 * @param {Roo.form.FieldLabel} this
30621 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30628 invalidClass : 'has-warning',
30629 validClass : 'has-success',
30630 iconTooltip : 'This field is required',
30631 indicatorpos : 'left',
30633 getAutoCreate : function(){
30636 if (!this.allowBlank) {
30642 cls : 'roo-bootstrap-field-label ' + this.cls,
30647 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30648 tooltip : this.iconTooltip
30657 if(this.indicatorpos == 'right'){
30660 cls : 'roo-bootstrap-field-label ' + this.cls,
30669 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30670 tooltip : this.iconTooltip
30679 initEvents: function()
30681 Roo.bootstrap.Element.superclass.initEvents.call(this);
30683 this.indicator = this.indicatorEl();
30685 if(this.indicator){
30686 this.indicator.removeClass('visible');
30687 this.indicator.addClass('invisible');
30690 Roo.bootstrap.FieldLabel.register(this);
30693 indicatorEl : function()
30695 var indicator = this.el.select('i.roo-required-indicator',true).first();
30706 * Mark this field as valid
30708 markValid : function()
30710 if(this.indicator){
30711 this.indicator.removeClass('visible');
30712 this.indicator.addClass('invisible');
30714 if (Roo.bootstrap.version == 3) {
30715 this.el.removeClass(this.invalidClass);
30716 this.el.addClass(this.validClass);
30718 this.el.removeClass('is-invalid');
30719 this.el.addClass('is-valid');
30723 this.fireEvent('valid', this);
30727 * Mark this field as invalid
30728 * @param {String} msg The validation message
30730 markInvalid : function(msg)
30732 if(this.indicator){
30733 this.indicator.removeClass('invisible');
30734 this.indicator.addClass('visible');
30736 if (Roo.bootstrap.version == 3) {
30737 this.el.removeClass(this.validClass);
30738 this.el.addClass(this.invalidClass);
30740 this.el.removeClass('is-valid');
30741 this.el.addClass('is-invalid');
30745 this.fireEvent('invalid', this, msg);
30751 Roo.apply(Roo.bootstrap.FieldLabel, {
30756 * register a FieldLabel Group
30757 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30759 register : function(label)
30761 if(this.groups.hasOwnProperty(label.target)){
30765 this.groups[label.target] = label;
30769 * fetch a FieldLabel Group based on the target
30770 * @param {string} target
30771 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30773 get: function(target) {
30774 if (typeof(this.groups[target]) == 'undefined') {
30778 return this.groups[target] ;
30787 * page DateSplitField.
30793 * @class Roo.bootstrap.DateSplitField
30794 * @extends Roo.bootstrap.Component
30795 * Bootstrap DateSplitField class
30796 * @cfg {string} fieldLabel - the label associated
30797 * @cfg {Number} labelWidth set the width of label (0-12)
30798 * @cfg {String} labelAlign (top|left)
30799 * @cfg {Boolean} dayAllowBlank (true|false) default false
30800 * @cfg {Boolean} monthAllowBlank (true|false) default false
30801 * @cfg {Boolean} yearAllowBlank (true|false) default false
30802 * @cfg {string} dayPlaceholder
30803 * @cfg {string} monthPlaceholder
30804 * @cfg {string} yearPlaceholder
30805 * @cfg {string} dayFormat default 'd'
30806 * @cfg {string} monthFormat default 'm'
30807 * @cfg {string} yearFormat default 'Y'
30808 * @cfg {Number} labellg set the width of label (1-12)
30809 * @cfg {Number} labelmd set the width of label (1-12)
30810 * @cfg {Number} labelsm set the width of label (1-12)
30811 * @cfg {Number} labelxs set the width of label (1-12)
30815 * Create a new DateSplitField
30816 * @param {Object} config The config object
30819 Roo.bootstrap.DateSplitField = function(config){
30820 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30826 * getting the data of years
30827 * @param {Roo.bootstrap.DateSplitField} this
30828 * @param {Object} years
30833 * getting the data of days
30834 * @param {Roo.bootstrap.DateSplitField} this
30835 * @param {Object} days
30840 * Fires after the field has been marked as invalid.
30841 * @param {Roo.form.Field} this
30842 * @param {String} msg The validation message
30847 * Fires after the field has been validated with no errors.
30848 * @param {Roo.form.Field} this
30854 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30857 labelAlign : 'top',
30859 dayAllowBlank : false,
30860 monthAllowBlank : false,
30861 yearAllowBlank : false,
30862 dayPlaceholder : '',
30863 monthPlaceholder : '',
30864 yearPlaceholder : '',
30868 isFormField : true,
30874 getAutoCreate : function()
30878 cls : 'row roo-date-split-field-group',
30883 cls : 'form-hidden-field roo-date-split-field-group-value',
30889 var labelCls = 'col-md-12';
30890 var contentCls = 'col-md-4';
30892 if(this.fieldLabel){
30896 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30900 html : this.fieldLabel
30905 if(this.labelAlign == 'left'){
30907 if(this.labelWidth > 12){
30908 label.style = "width: " + this.labelWidth + 'px';
30911 if(this.labelWidth < 13 && this.labelmd == 0){
30912 this.labelmd = this.labelWidth;
30915 if(this.labellg > 0){
30916 labelCls = ' col-lg-' + this.labellg;
30917 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30920 if(this.labelmd > 0){
30921 labelCls = ' col-md-' + this.labelmd;
30922 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30925 if(this.labelsm > 0){
30926 labelCls = ' col-sm-' + this.labelsm;
30927 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30930 if(this.labelxs > 0){
30931 labelCls = ' col-xs-' + this.labelxs;
30932 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30936 label.cls += ' ' + labelCls;
30938 cfg.cn.push(label);
30941 Roo.each(['day', 'month', 'year'], function(t){
30944 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30951 inputEl: function ()
30953 return this.el.select('.roo-date-split-field-group-value', true).first();
30956 onRender : function(ct, position)
30960 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30962 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30964 this.dayField = new Roo.bootstrap.ComboBox({
30965 allowBlank : this.dayAllowBlank,
30966 alwaysQuery : true,
30967 displayField : 'value',
30970 forceSelection : true,
30972 placeholder : this.dayPlaceholder,
30973 selectOnFocus : true,
30974 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30975 triggerAction : 'all',
30977 valueField : 'value',
30978 store : new Roo.data.SimpleStore({
30979 data : (function() {
30981 _this.fireEvent('days', _this, days);
30984 fields : [ 'value' ]
30987 select : function (_self, record, index)
30989 _this.setValue(_this.getValue());
30994 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30996 this.monthField = new Roo.bootstrap.MonthField({
30997 after : '<i class=\"fa fa-calendar\"></i>',
30998 allowBlank : this.monthAllowBlank,
30999 placeholder : this.monthPlaceholder,
31002 render : function (_self)
31004 this.el.select('span.input-group-addon', true).first().on('click', function(e){
31005 e.preventDefault();
31009 select : function (_self, oldvalue, newvalue)
31011 _this.setValue(_this.getValue());
31016 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31018 this.yearField = new Roo.bootstrap.ComboBox({
31019 allowBlank : this.yearAllowBlank,
31020 alwaysQuery : true,
31021 displayField : 'value',
31024 forceSelection : true,
31026 placeholder : this.yearPlaceholder,
31027 selectOnFocus : true,
31028 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31029 triggerAction : 'all',
31031 valueField : 'value',
31032 store : new Roo.data.SimpleStore({
31033 data : (function() {
31035 _this.fireEvent('years', _this, years);
31038 fields : [ 'value' ]
31041 select : function (_self, record, index)
31043 _this.setValue(_this.getValue());
31048 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31051 setValue : function(v, format)
31053 this.inputEl.dom.value = v;
31055 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31057 var d = Date.parseDate(v, f);
31064 this.setDay(d.format(this.dayFormat));
31065 this.setMonth(d.format(this.monthFormat));
31066 this.setYear(d.format(this.yearFormat));
31073 setDay : function(v)
31075 this.dayField.setValue(v);
31076 this.inputEl.dom.value = this.getValue();
31081 setMonth : function(v)
31083 this.monthField.setValue(v, true);
31084 this.inputEl.dom.value = this.getValue();
31089 setYear : function(v)
31091 this.yearField.setValue(v);
31092 this.inputEl.dom.value = this.getValue();
31097 getDay : function()
31099 return this.dayField.getValue();
31102 getMonth : function()
31104 return this.monthField.getValue();
31107 getYear : function()
31109 return this.yearField.getValue();
31112 getValue : function()
31114 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31116 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31126 this.inputEl.dom.value = '';
31131 validate : function()
31133 var d = this.dayField.validate();
31134 var m = this.monthField.validate();
31135 var y = this.yearField.validate();
31140 (!this.dayAllowBlank && !d) ||
31141 (!this.monthAllowBlank && !m) ||
31142 (!this.yearAllowBlank && !y)
31147 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31156 this.markInvalid();
31161 markValid : function()
31164 var label = this.el.select('label', true).first();
31165 var icon = this.el.select('i.fa-star', true).first();
31171 this.fireEvent('valid', this);
31175 * Mark this field as invalid
31176 * @param {String} msg The validation message
31178 markInvalid : function(msg)
31181 var label = this.el.select('label', true).first();
31182 var icon = this.el.select('i.fa-star', true).first();
31184 if(label && !icon){
31185 this.el.select('.roo-date-split-field-label', true).createChild({
31187 cls : 'text-danger fa fa-lg fa-star',
31188 tooltip : 'This field is required',
31189 style : 'margin-right:5px;'
31193 this.fireEvent('invalid', this, msg);
31196 clearInvalid : function()
31198 var label = this.el.select('label', true).first();
31199 var icon = this.el.select('i.fa-star', true).first();
31205 this.fireEvent('valid', this);
31208 getName: function()
31218 * http://masonry.desandro.com
31220 * The idea is to render all the bricks based on vertical width...
31222 * The original code extends 'outlayer' - we might need to use that....
31228 * @class Roo.bootstrap.LayoutMasonry
31229 * @extends Roo.bootstrap.Component
31230 * Bootstrap Layout Masonry class
31233 * Create a new Element
31234 * @param {Object} config The config object
31237 Roo.bootstrap.LayoutMasonry = function(config){
31239 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31243 Roo.bootstrap.LayoutMasonry.register(this);
31249 * Fire after layout the items
31250 * @param {Roo.bootstrap.LayoutMasonry} this
31251 * @param {Roo.EventObject} e
31258 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
31261 * @cfg {Boolean} isLayoutInstant = no animation?
31263 isLayoutInstant : false, // needed?
31266 * @cfg {Number} boxWidth width of the columns
31271 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
31276 * @cfg {Number} padWidth padding below box..
31281 * @cfg {Number} gutter gutter width..
31286 * @cfg {Number} maxCols maximum number of columns
31292 * @cfg {Boolean} isAutoInitial defalut true
31294 isAutoInitial : true,
31299 * @cfg {Boolean} isHorizontal defalut false
31301 isHorizontal : false,
31303 currentSize : null,
31309 bricks: null, //CompositeElement
31313 _isLayoutInited : false,
31315 // isAlternative : false, // only use for vertical layout...
31318 * @cfg {Number} alternativePadWidth padding below box..
31320 alternativePadWidth : 50,
31322 selectedBrick : [],
31324 getAutoCreate : function(){
31326 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31330 cls: 'blog-masonary-wrapper ' + this.cls,
31332 cls : 'mas-boxes masonary'
31339 getChildContainer: function( )
31341 if (this.boxesEl) {
31342 return this.boxesEl;
31345 this.boxesEl = this.el.select('.mas-boxes').first();
31347 return this.boxesEl;
31351 initEvents : function()
31355 if(this.isAutoInitial){
31356 Roo.log('hook children rendered');
31357 this.on('childrenrendered', function() {
31358 Roo.log('children rendered');
31364 initial : function()
31366 this.selectedBrick = [];
31368 this.currentSize = this.el.getBox(true);
31370 Roo.EventManager.onWindowResize(this.resize, this);
31372 if(!this.isAutoInitial){
31380 //this.layout.defer(500,this);
31384 resize : function()
31386 var cs = this.el.getBox(true);
31389 this.currentSize.width == cs.width &&
31390 this.currentSize.x == cs.x &&
31391 this.currentSize.height == cs.height &&
31392 this.currentSize.y == cs.y
31394 Roo.log("no change in with or X or Y");
31398 this.currentSize = cs;
31404 layout : function()
31406 this._resetLayout();
31408 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31410 this.layoutItems( isInstant );
31412 this._isLayoutInited = true;
31414 this.fireEvent('layout', this);
31418 _resetLayout : function()
31420 if(this.isHorizontal){
31421 this.horizontalMeasureColumns();
31425 this.verticalMeasureColumns();
31429 verticalMeasureColumns : function()
31431 this.getContainerWidth();
31433 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31434 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31438 var boxWidth = this.boxWidth + this.padWidth;
31440 if(this.containerWidth < this.boxWidth){
31441 boxWidth = this.containerWidth
31444 var containerWidth = this.containerWidth;
31446 var cols = Math.floor(containerWidth / boxWidth);
31448 this.cols = Math.max( cols, 1 );
31450 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31452 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31454 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31456 this.colWidth = boxWidth + avail - this.padWidth;
31458 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31459 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31462 horizontalMeasureColumns : function()
31464 this.getContainerWidth();
31466 var boxWidth = this.boxWidth;
31468 if(this.containerWidth < boxWidth){
31469 boxWidth = this.containerWidth;
31472 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31474 this.el.setHeight(boxWidth);
31478 getContainerWidth : function()
31480 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31483 layoutItems : function( isInstant )
31485 Roo.log(this.bricks);
31487 var items = Roo.apply([], this.bricks);
31489 if(this.isHorizontal){
31490 this._horizontalLayoutItems( items , isInstant );
31494 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31495 // this._verticalAlternativeLayoutItems( items , isInstant );
31499 this._verticalLayoutItems( items , isInstant );
31503 _verticalLayoutItems : function ( items , isInstant)
31505 if ( !items || !items.length ) {
31510 ['xs', 'xs', 'xs', 'tall'],
31511 ['xs', 'xs', 'tall'],
31512 ['xs', 'xs', 'sm'],
31513 ['xs', 'xs', 'xs'],
31519 ['sm', 'xs', 'xs'],
31523 ['tall', 'xs', 'xs', 'xs'],
31524 ['tall', 'xs', 'xs'],
31536 Roo.each(items, function(item, k){
31538 switch (item.size) {
31539 // these layouts take up a full box,
31550 boxes.push([item]);
31573 var filterPattern = function(box, length)
31581 var pattern = box.slice(0, length);
31585 Roo.each(pattern, function(i){
31586 format.push(i.size);
31589 Roo.each(standard, function(s){
31591 if(String(s) != String(format)){
31600 if(!match && length == 1){
31605 filterPattern(box, length - 1);
31609 queue.push(pattern);
31611 box = box.slice(length, box.length);
31613 filterPattern(box, 4);
31619 Roo.each(boxes, function(box, k){
31625 if(box.length == 1){
31630 filterPattern(box, 4);
31634 this._processVerticalLayoutQueue( queue, isInstant );
31638 // _verticalAlternativeLayoutItems : function( items , isInstant )
31640 // if ( !items || !items.length ) {
31644 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31648 _horizontalLayoutItems : function ( items , isInstant)
31650 if ( !items || !items.length || items.length < 3) {
31656 var eItems = items.slice(0, 3);
31658 items = items.slice(3, items.length);
31661 ['xs', 'xs', 'xs', 'wide'],
31662 ['xs', 'xs', 'wide'],
31663 ['xs', 'xs', 'sm'],
31664 ['xs', 'xs', 'xs'],
31670 ['sm', 'xs', 'xs'],
31674 ['wide', 'xs', 'xs', 'xs'],
31675 ['wide', 'xs', 'xs'],
31688 Roo.each(items, function(item, k){
31690 switch (item.size) {
31701 boxes.push([item]);
31725 var filterPattern = function(box, length)
31733 var pattern = box.slice(0, length);
31737 Roo.each(pattern, function(i){
31738 format.push(i.size);
31741 Roo.each(standard, function(s){
31743 if(String(s) != String(format)){
31752 if(!match && length == 1){
31757 filterPattern(box, length - 1);
31761 queue.push(pattern);
31763 box = box.slice(length, box.length);
31765 filterPattern(box, 4);
31771 Roo.each(boxes, function(box, k){
31777 if(box.length == 1){
31782 filterPattern(box, 4);
31789 var pos = this.el.getBox(true);
31793 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31795 var hit_end = false;
31797 Roo.each(queue, function(box){
31801 Roo.each(box, function(b){
31803 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31813 Roo.each(box, function(b){
31815 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31818 mx = Math.max(mx, b.x);
31822 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31826 Roo.each(box, function(b){
31828 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31842 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31845 /** Sets position of item in DOM
31846 * @param {Element} item
31847 * @param {Number} x - horizontal position
31848 * @param {Number} y - vertical position
31849 * @param {Boolean} isInstant - disables transitions
31851 _processVerticalLayoutQueue : function( queue, isInstant )
31853 var pos = this.el.getBox(true);
31858 for (var i = 0; i < this.cols; i++){
31862 Roo.each(queue, function(box, k){
31864 var col = k % this.cols;
31866 Roo.each(box, function(b,kk){
31868 b.el.position('absolute');
31870 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31871 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31873 if(b.size == 'md-left' || b.size == 'md-right'){
31874 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31875 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31878 b.el.setWidth(width);
31879 b.el.setHeight(height);
31881 b.el.select('iframe',true).setSize(width,height);
31885 for (var i = 0; i < this.cols; i++){
31887 if(maxY[i] < maxY[col]){
31892 col = Math.min(col, i);
31896 x = pos.x + col * (this.colWidth + this.padWidth);
31900 var positions = [];
31902 switch (box.length){
31904 positions = this.getVerticalOneBoxColPositions(x, y, box);
31907 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31910 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31913 positions = this.getVerticalFourBoxColPositions(x, y, box);
31919 Roo.each(box, function(b,kk){
31921 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31923 var sz = b.el.getSize();
31925 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31933 for (var i = 0; i < this.cols; i++){
31934 mY = Math.max(mY, maxY[i]);
31937 this.el.setHeight(mY - pos.y);
31941 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31943 // var pos = this.el.getBox(true);
31946 // var maxX = pos.right;
31948 // var maxHeight = 0;
31950 // Roo.each(items, function(item, k){
31954 // item.el.position('absolute');
31956 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31958 // item.el.setWidth(width);
31960 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31962 // item.el.setHeight(height);
31965 // item.el.setXY([x, y], isInstant ? false : true);
31967 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31970 // y = y + height + this.alternativePadWidth;
31972 // maxHeight = maxHeight + height + this.alternativePadWidth;
31976 // this.el.setHeight(maxHeight);
31980 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31982 var pos = this.el.getBox(true);
31987 var maxX = pos.right;
31989 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31991 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31993 Roo.each(queue, function(box, k){
31995 Roo.each(box, function(b, kk){
31997 b.el.position('absolute');
31999 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32000 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32002 if(b.size == 'md-left' || b.size == 'md-right'){
32003 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32004 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32007 b.el.setWidth(width);
32008 b.el.setHeight(height);
32016 var positions = [];
32018 switch (box.length){
32020 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32023 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32026 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32029 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32035 Roo.each(box, function(b,kk){
32037 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32039 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32047 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32049 Roo.each(eItems, function(b,k){
32051 b.size = (k == 0) ? 'sm' : 'xs';
32052 b.x = (k == 0) ? 2 : 1;
32053 b.y = (k == 0) ? 2 : 1;
32055 b.el.position('absolute');
32057 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32059 b.el.setWidth(width);
32061 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32063 b.el.setHeight(height);
32067 var positions = [];
32070 x : maxX - this.unitWidth * 2 - this.gutter,
32075 x : maxX - this.unitWidth,
32076 y : minY + (this.unitWidth + this.gutter) * 2
32080 x : maxX - this.unitWidth * 3 - this.gutter * 2,
32084 Roo.each(eItems, function(b,k){
32086 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32092 getVerticalOneBoxColPositions : function(x, y, box)
32096 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32098 if(box[0].size == 'md-left'){
32102 if(box[0].size == 'md-right'){
32107 x : x + (this.unitWidth + this.gutter) * rand,
32114 getVerticalTwoBoxColPositions : function(x, y, box)
32118 if(box[0].size == 'xs'){
32122 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32126 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32140 x : x + (this.unitWidth + this.gutter) * 2,
32141 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32148 getVerticalThreeBoxColPositions : function(x, y, box)
32152 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32160 x : x + (this.unitWidth + this.gutter) * 1,
32165 x : x + (this.unitWidth + this.gutter) * 2,
32173 if(box[0].size == 'xs' && box[1].size == 'xs'){
32182 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32186 x : x + (this.unitWidth + this.gutter) * 1,
32200 x : x + (this.unitWidth + this.gutter) * 2,
32205 x : x + (this.unitWidth + this.gutter) * 2,
32206 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32213 getVerticalFourBoxColPositions : function(x, y, box)
32217 if(box[0].size == 'xs'){
32226 y : y + (this.unitHeight + this.gutter) * 1
32231 y : y + (this.unitHeight + this.gutter) * 2
32235 x : x + (this.unitWidth + this.gutter) * 1,
32249 x : x + (this.unitWidth + this.gutter) * 2,
32254 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32255 y : y + (this.unitHeight + this.gutter) * 1
32259 x : x + (this.unitWidth + this.gutter) * 2,
32260 y : y + (this.unitWidth + this.gutter) * 2
32267 getHorizontalOneBoxColPositions : function(maxX, minY, box)
32271 if(box[0].size == 'md-left'){
32273 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32280 if(box[0].size == 'md-right'){
32282 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32283 y : minY + (this.unitWidth + this.gutter) * 1
32289 var rand = Math.floor(Math.random() * (4 - box[0].y));
32292 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32293 y : minY + (this.unitWidth + this.gutter) * rand
32300 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32304 if(box[0].size == 'xs'){
32307 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32312 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32313 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32321 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32326 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32327 y : minY + (this.unitWidth + this.gutter) * 2
32334 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32338 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32341 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32346 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32347 y : minY + (this.unitWidth + this.gutter) * 1
32351 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32352 y : minY + (this.unitWidth + this.gutter) * 2
32359 if(box[0].size == 'xs' && box[1].size == 'xs'){
32362 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32367 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32372 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32373 y : minY + (this.unitWidth + this.gutter) * 1
32381 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32386 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32387 y : minY + (this.unitWidth + this.gutter) * 2
32391 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32392 y : minY + (this.unitWidth + this.gutter) * 2
32399 getHorizontalFourBoxColPositions : function(maxX, minY, box)
32403 if(box[0].size == 'xs'){
32406 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32411 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32416 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),
32421 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32422 y : minY + (this.unitWidth + this.gutter) * 1
32430 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32435 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32436 y : minY + (this.unitWidth + this.gutter) * 2
32440 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32441 y : minY + (this.unitWidth + this.gutter) * 2
32445 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),
32446 y : minY + (this.unitWidth + this.gutter) * 2
32454 * remove a Masonry Brick
32455 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32457 removeBrick : function(brick_id)
32463 for (var i = 0; i<this.bricks.length; i++) {
32464 if (this.bricks[i].id == brick_id) {
32465 this.bricks.splice(i,1);
32466 this.el.dom.removeChild(Roo.get(brick_id).dom);
32473 * adds a Masonry Brick
32474 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32476 addBrick : function(cfg)
32478 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32479 //this.register(cn);
32480 cn.parentId = this.id;
32481 cn.render(this.el);
32486 * register a Masonry Brick
32487 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32490 register : function(brick)
32492 this.bricks.push(brick);
32493 brick.masonryId = this.id;
32497 * clear all the Masonry Brick
32499 clearAll : function()
32502 //this.getChildContainer().dom.innerHTML = "";
32503 this.el.dom.innerHTML = '';
32506 getSelected : function()
32508 if (!this.selectedBrick) {
32512 return this.selectedBrick;
32516 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32520 * register a Masonry Layout
32521 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32524 register : function(layout)
32526 this.groups[layout.id] = layout;
32529 * fetch a Masonry Layout based on the masonry layout ID
32530 * @param {string} the masonry layout to add
32531 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32534 get: function(layout_id) {
32535 if (typeof(this.groups[layout_id]) == 'undefined') {
32538 return this.groups[layout_id] ;
32550 * http://masonry.desandro.com
32552 * The idea is to render all the bricks based on vertical width...
32554 * The original code extends 'outlayer' - we might need to use that....
32560 * @class Roo.bootstrap.LayoutMasonryAuto
32561 * @extends Roo.bootstrap.Component
32562 * Bootstrap Layout Masonry class
32565 * Create a new Element
32566 * @param {Object} config The config object
32569 Roo.bootstrap.LayoutMasonryAuto = function(config){
32570 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32573 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32576 * @cfg {Boolean} isFitWidth - resize the width..
32578 isFitWidth : false, // options..
32580 * @cfg {Boolean} isOriginLeft = left align?
32582 isOriginLeft : true,
32584 * @cfg {Boolean} isOriginTop = top align?
32586 isOriginTop : false,
32588 * @cfg {Boolean} isLayoutInstant = no animation?
32590 isLayoutInstant : false, // needed?
32592 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32594 isResizingContainer : true,
32596 * @cfg {Number} columnWidth width of the columns
32602 * @cfg {Number} maxCols maximum number of columns
32607 * @cfg {Number} padHeight padding below box..
32613 * @cfg {Boolean} isAutoInitial defalut true
32616 isAutoInitial : true,
32622 initialColumnWidth : 0,
32623 currentSize : null,
32625 colYs : null, // array.
32632 bricks: null, //CompositeElement
32633 cols : 0, // array?
32634 // element : null, // wrapped now this.el
32635 _isLayoutInited : null,
32638 getAutoCreate : function(){
32642 cls: 'blog-masonary-wrapper ' + this.cls,
32644 cls : 'mas-boxes masonary'
32651 getChildContainer: function( )
32653 if (this.boxesEl) {
32654 return this.boxesEl;
32657 this.boxesEl = this.el.select('.mas-boxes').first();
32659 return this.boxesEl;
32663 initEvents : function()
32667 if(this.isAutoInitial){
32668 Roo.log('hook children rendered');
32669 this.on('childrenrendered', function() {
32670 Roo.log('children rendered');
32677 initial : function()
32679 this.reloadItems();
32681 this.currentSize = this.el.getBox(true);
32683 /// was window resize... - let's see if this works..
32684 Roo.EventManager.onWindowResize(this.resize, this);
32686 if(!this.isAutoInitial){
32691 this.layout.defer(500,this);
32694 reloadItems: function()
32696 this.bricks = this.el.select('.masonry-brick', true);
32698 this.bricks.each(function(b) {
32699 //Roo.log(b.getSize());
32700 if (!b.attr('originalwidth')) {
32701 b.attr('originalwidth', b.getSize().width);
32706 Roo.log(this.bricks.elements.length);
32709 resize : function()
32712 var cs = this.el.getBox(true);
32714 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32715 Roo.log("no change in with or X");
32718 this.currentSize = cs;
32722 layout : function()
32725 this._resetLayout();
32726 //this._manageStamps();
32728 // don't animate first layout
32729 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32730 this.layoutItems( isInstant );
32732 // flag for initalized
32733 this._isLayoutInited = true;
32736 layoutItems : function( isInstant )
32738 //var items = this._getItemsForLayout( this.items );
32739 // original code supports filtering layout items.. we just ignore it..
32741 this._layoutItems( this.bricks , isInstant );
32743 this._postLayout();
32745 _layoutItems : function ( items , isInstant)
32747 //this.fireEvent( 'layout', this, items );
32750 if ( !items || !items.elements.length ) {
32751 // no items, emit event with empty array
32756 items.each(function(item) {
32757 Roo.log("layout item");
32759 // get x/y object from method
32760 var position = this._getItemLayoutPosition( item );
32762 position.item = item;
32763 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32764 queue.push( position );
32767 this._processLayoutQueue( queue );
32769 /** Sets position of item in DOM
32770 * @param {Element} item
32771 * @param {Number} x - horizontal position
32772 * @param {Number} y - vertical position
32773 * @param {Boolean} isInstant - disables transitions
32775 _processLayoutQueue : function( queue )
32777 for ( var i=0, len = queue.length; i < len; i++ ) {
32778 var obj = queue[i];
32779 obj.item.position('absolute');
32780 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32786 * Any logic you want to do after each layout,
32787 * i.e. size the container
32789 _postLayout : function()
32791 this.resizeContainer();
32794 resizeContainer : function()
32796 if ( !this.isResizingContainer ) {
32799 var size = this._getContainerSize();
32801 this.el.setSize(size.width,size.height);
32802 this.boxesEl.setSize(size.width,size.height);
32808 _resetLayout : function()
32810 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32811 this.colWidth = this.el.getWidth();
32812 //this.gutter = this.el.getWidth();
32814 this.measureColumns();
32820 this.colYs.push( 0 );
32826 measureColumns : function()
32828 this.getContainerWidth();
32829 // if columnWidth is 0, default to outerWidth of first item
32830 if ( !this.columnWidth ) {
32831 var firstItem = this.bricks.first();
32832 Roo.log(firstItem);
32833 this.columnWidth = this.containerWidth;
32834 if (firstItem && firstItem.attr('originalwidth') ) {
32835 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32837 // columnWidth fall back to item of first element
32838 Roo.log("set column width?");
32839 this.initialColumnWidth = this.columnWidth ;
32841 // if first elem has no width, default to size of container
32846 if (this.initialColumnWidth) {
32847 this.columnWidth = this.initialColumnWidth;
32852 // column width is fixed at the top - however if container width get's smaller we should
32855 // this bit calcs how man columns..
32857 var columnWidth = this.columnWidth += this.gutter;
32859 // calculate columns
32860 var containerWidth = this.containerWidth + this.gutter;
32862 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32863 // fix rounding errors, typically with gutters
32864 var excess = columnWidth - containerWidth % columnWidth;
32867 // if overshoot is less than a pixel, round up, otherwise floor it
32868 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32869 cols = Math[ mathMethod ]( cols );
32870 this.cols = Math.max( cols, 1 );
32871 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32873 // padding positioning..
32874 var totalColWidth = this.cols * this.columnWidth;
32875 var padavail = this.containerWidth - totalColWidth;
32876 // so for 2 columns - we need 3 'pads'
32878 var padNeeded = (1+this.cols) * this.padWidth;
32880 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32882 this.columnWidth += padExtra
32883 //this.padWidth = Math.floor(padavail / ( this.cols));
32885 // adjust colum width so that padding is fixed??
32887 // we have 3 columns ... total = width * 3
32888 // we have X left over... that should be used by
32890 //if (this.expandC) {
32898 getContainerWidth : function()
32900 /* // container is parent if fit width
32901 var container = this.isFitWidth ? this.element.parentNode : this.element;
32902 // check that this.size and size are there
32903 // IE8 triggers resize on body size change, so they might not be
32905 var size = getSize( container ); //FIXME
32906 this.containerWidth = size && size.innerWidth; //FIXME
32909 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32913 _getItemLayoutPosition : function( item ) // what is item?
32915 // we resize the item to our columnWidth..
32917 item.setWidth(this.columnWidth);
32918 item.autoBoxAdjust = false;
32920 var sz = item.getSize();
32922 // how many columns does this brick span
32923 var remainder = this.containerWidth % this.columnWidth;
32925 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32926 // round if off by 1 pixel, otherwise use ceil
32927 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32928 colSpan = Math.min( colSpan, this.cols );
32930 // normally this should be '1' as we dont' currently allow multi width columns..
32932 var colGroup = this._getColGroup( colSpan );
32933 // get the minimum Y value from the columns
32934 var minimumY = Math.min.apply( Math, colGroup );
32935 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32937 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32939 // position the brick
32941 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32942 y: this.currentSize.y + minimumY + this.padHeight
32946 // apply setHeight to necessary columns
32947 var setHeight = minimumY + sz.height + this.padHeight;
32948 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32950 var setSpan = this.cols + 1 - colGroup.length;
32951 for ( var i = 0; i < setSpan; i++ ) {
32952 this.colYs[ shortColIndex + i ] = setHeight ;
32959 * @param {Number} colSpan - number of columns the element spans
32960 * @returns {Array} colGroup
32962 _getColGroup : function( colSpan )
32964 if ( colSpan < 2 ) {
32965 // if brick spans only one column, use all the column Ys
32970 // how many different places could this brick fit horizontally
32971 var groupCount = this.cols + 1 - colSpan;
32972 // for each group potential horizontal position
32973 for ( var i = 0; i < groupCount; i++ ) {
32974 // make an array of colY values for that one group
32975 var groupColYs = this.colYs.slice( i, i + colSpan );
32976 // and get the max value of the array
32977 colGroup[i] = Math.max.apply( Math, groupColYs );
32982 _manageStamp : function( stamp )
32984 var stampSize = stamp.getSize();
32985 var offset = stamp.getBox();
32986 // get the columns that this stamp affects
32987 var firstX = this.isOriginLeft ? offset.x : offset.right;
32988 var lastX = firstX + stampSize.width;
32989 var firstCol = Math.floor( firstX / this.columnWidth );
32990 firstCol = Math.max( 0, firstCol );
32992 var lastCol = Math.floor( lastX / this.columnWidth );
32993 // lastCol should not go over if multiple of columnWidth #425
32994 lastCol -= lastX % this.columnWidth ? 0 : 1;
32995 lastCol = Math.min( this.cols - 1, lastCol );
32997 // set colYs to bottom of the stamp
32998 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
33001 for ( var i = firstCol; i <= lastCol; i++ ) {
33002 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33007 _getContainerSize : function()
33009 this.maxY = Math.max.apply( Math, this.colYs );
33014 if ( this.isFitWidth ) {
33015 size.width = this._getContainerFitWidth();
33021 _getContainerFitWidth : function()
33023 var unusedCols = 0;
33024 // count unused columns
33027 if ( this.colYs[i] !== 0 ) {
33032 // fit container to columns that have been used
33033 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33036 needsResizeLayout : function()
33038 var previousWidth = this.containerWidth;
33039 this.getContainerWidth();
33040 return previousWidth !== this.containerWidth;
33055 * @class Roo.bootstrap.MasonryBrick
33056 * @extends Roo.bootstrap.Component
33057 * Bootstrap MasonryBrick class
33060 * Create a new MasonryBrick
33061 * @param {Object} config The config object
33064 Roo.bootstrap.MasonryBrick = function(config){
33066 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33068 Roo.bootstrap.MasonryBrick.register(this);
33074 * When a MasonryBrick is clcik
33075 * @param {Roo.bootstrap.MasonryBrick} this
33076 * @param {Roo.EventObject} e
33082 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
33085 * @cfg {String} title
33089 * @cfg {String} html
33093 * @cfg {String} bgimage
33097 * @cfg {String} videourl
33101 * @cfg {String} cls
33105 * @cfg {String} href
33109 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33114 * @cfg {String} placetitle (center|bottom)
33119 * @cfg {Boolean} isFitContainer defalut true
33121 isFitContainer : true,
33124 * @cfg {Boolean} preventDefault defalut false
33126 preventDefault : false,
33129 * @cfg {Boolean} inverse defalut false
33131 maskInverse : false,
33133 getAutoCreate : function()
33135 if(!this.isFitContainer){
33136 return this.getSplitAutoCreate();
33139 var cls = 'masonry-brick masonry-brick-full';
33141 if(this.href.length){
33142 cls += ' masonry-brick-link';
33145 if(this.bgimage.length){
33146 cls += ' masonry-brick-image';
33149 if(this.maskInverse){
33150 cls += ' mask-inverse';
33153 if(!this.html.length && !this.maskInverse && !this.videourl.length){
33154 cls += ' enable-mask';
33158 cls += ' masonry-' + this.size + '-brick';
33161 if(this.placetitle.length){
33163 switch (this.placetitle) {
33165 cls += ' masonry-center-title';
33168 cls += ' masonry-bottom-title';
33175 if(!this.html.length && !this.bgimage.length){
33176 cls += ' masonry-center-title';
33179 if(!this.html.length && this.bgimage.length){
33180 cls += ' masonry-bottom-title';
33185 cls += ' ' + this.cls;
33189 tag: (this.href.length) ? 'a' : 'div',
33194 cls: 'masonry-brick-mask'
33198 cls: 'masonry-brick-paragraph',
33204 if(this.href.length){
33205 cfg.href = this.href;
33208 var cn = cfg.cn[1].cn;
33210 if(this.title.length){
33213 cls: 'masonry-brick-title',
33218 if(this.html.length){
33221 cls: 'masonry-brick-text',
33226 if (!this.title.length && !this.html.length) {
33227 cfg.cn[1].cls += ' hide';
33230 if(this.bgimage.length){
33233 cls: 'masonry-brick-image-view',
33238 if(this.videourl.length){
33239 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33240 // youtube support only?
33243 cls: 'masonry-brick-image-view',
33246 allowfullscreen : true
33254 getSplitAutoCreate : function()
33256 var cls = 'masonry-brick masonry-brick-split';
33258 if(this.href.length){
33259 cls += ' masonry-brick-link';
33262 if(this.bgimage.length){
33263 cls += ' masonry-brick-image';
33267 cls += ' masonry-' + this.size + '-brick';
33270 switch (this.placetitle) {
33272 cls += ' masonry-center-title';
33275 cls += ' masonry-bottom-title';
33278 if(!this.bgimage.length){
33279 cls += ' masonry-center-title';
33282 if(this.bgimage.length){
33283 cls += ' masonry-bottom-title';
33289 cls += ' ' + this.cls;
33293 tag: (this.href.length) ? 'a' : 'div',
33298 cls: 'masonry-brick-split-head',
33302 cls: 'masonry-brick-paragraph',
33309 cls: 'masonry-brick-split-body',
33315 if(this.href.length){
33316 cfg.href = this.href;
33319 if(this.title.length){
33320 cfg.cn[0].cn[0].cn.push({
33322 cls: 'masonry-brick-title',
33327 if(this.html.length){
33328 cfg.cn[1].cn.push({
33330 cls: 'masonry-brick-text',
33335 if(this.bgimage.length){
33336 cfg.cn[0].cn.push({
33338 cls: 'masonry-brick-image-view',
33343 if(this.videourl.length){
33344 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33345 // youtube support only?
33346 cfg.cn[0].cn.cn.push({
33348 cls: 'masonry-brick-image-view',
33351 allowfullscreen : true
33358 initEvents: function()
33360 switch (this.size) {
33393 this.el.on('touchstart', this.onTouchStart, this);
33394 this.el.on('touchmove', this.onTouchMove, this);
33395 this.el.on('touchend', this.onTouchEnd, this);
33396 this.el.on('contextmenu', this.onContextMenu, this);
33398 this.el.on('mouseenter' ,this.enter, this);
33399 this.el.on('mouseleave', this.leave, this);
33400 this.el.on('click', this.onClick, this);
33403 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33404 this.parent().bricks.push(this);
33409 onClick: function(e, el)
33411 var time = this.endTimer - this.startTimer;
33412 // Roo.log(e.preventDefault());
33415 e.preventDefault();
33420 if(!this.preventDefault){
33424 e.preventDefault();
33426 if (this.activeClass != '') {
33427 this.selectBrick();
33430 this.fireEvent('click', this, e);
33433 enter: function(e, el)
33435 e.preventDefault();
33437 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33441 if(this.bgimage.length && this.html.length){
33442 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33446 leave: function(e, el)
33448 e.preventDefault();
33450 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33454 if(this.bgimage.length && this.html.length){
33455 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33459 onTouchStart: function(e, el)
33461 // e.preventDefault();
33463 this.touchmoved = false;
33465 if(!this.isFitContainer){
33469 if(!this.bgimage.length || !this.html.length){
33473 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33475 this.timer = new Date().getTime();
33479 onTouchMove: function(e, el)
33481 this.touchmoved = true;
33484 onContextMenu : function(e,el)
33486 e.preventDefault();
33487 e.stopPropagation();
33491 onTouchEnd: function(e, el)
33493 // e.preventDefault();
33495 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33502 if(!this.bgimage.length || !this.html.length){
33504 if(this.href.length){
33505 window.location.href = this.href;
33511 if(!this.isFitContainer){
33515 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33517 window.location.href = this.href;
33520 //selection on single brick only
33521 selectBrick : function() {
33523 if (!this.parentId) {
33527 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33528 var index = m.selectedBrick.indexOf(this.id);
33531 m.selectedBrick.splice(index,1);
33532 this.el.removeClass(this.activeClass);
33536 for(var i = 0; i < m.selectedBrick.length; i++) {
33537 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33538 b.el.removeClass(b.activeClass);
33541 m.selectedBrick = [];
33543 m.selectedBrick.push(this.id);
33544 this.el.addClass(this.activeClass);
33548 isSelected : function(){
33549 return this.el.hasClass(this.activeClass);
33554 Roo.apply(Roo.bootstrap.MasonryBrick, {
33557 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33559 * register a Masonry Brick
33560 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33563 register : function(brick)
33565 //this.groups[brick.id] = brick;
33566 this.groups.add(brick.id, brick);
33569 * fetch a masonry brick based on the masonry brick ID
33570 * @param {string} the masonry brick to add
33571 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33574 get: function(brick_id)
33576 // if (typeof(this.groups[brick_id]) == 'undefined') {
33579 // return this.groups[brick_id] ;
33581 if(this.groups.key(brick_id)) {
33582 return this.groups.key(brick_id);
33600 * @class Roo.bootstrap.Brick
33601 * @extends Roo.bootstrap.Component
33602 * Bootstrap Brick class
33605 * Create a new Brick
33606 * @param {Object} config The config object
33609 Roo.bootstrap.Brick = function(config){
33610 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33616 * When a Brick is click
33617 * @param {Roo.bootstrap.Brick} this
33618 * @param {Roo.EventObject} e
33624 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33627 * @cfg {String} title
33631 * @cfg {String} html
33635 * @cfg {String} bgimage
33639 * @cfg {String} cls
33643 * @cfg {String} href
33647 * @cfg {String} video
33651 * @cfg {Boolean} square
33655 getAutoCreate : function()
33657 var cls = 'roo-brick';
33659 if(this.href.length){
33660 cls += ' roo-brick-link';
33663 if(this.bgimage.length){
33664 cls += ' roo-brick-image';
33667 if(!this.html.length && !this.bgimage.length){
33668 cls += ' roo-brick-center-title';
33671 if(!this.html.length && this.bgimage.length){
33672 cls += ' roo-brick-bottom-title';
33676 cls += ' ' + this.cls;
33680 tag: (this.href.length) ? 'a' : 'div',
33685 cls: 'roo-brick-paragraph',
33691 if(this.href.length){
33692 cfg.href = this.href;
33695 var cn = cfg.cn[0].cn;
33697 if(this.title.length){
33700 cls: 'roo-brick-title',
33705 if(this.html.length){
33708 cls: 'roo-brick-text',
33715 if(this.bgimage.length){
33718 cls: 'roo-brick-image-view',
33726 initEvents: function()
33728 if(this.title.length || this.html.length){
33729 this.el.on('mouseenter' ,this.enter, this);
33730 this.el.on('mouseleave', this.leave, this);
33733 Roo.EventManager.onWindowResize(this.resize, this);
33735 if(this.bgimage.length){
33736 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33737 this.imageEl.on('load', this.onImageLoad, this);
33744 onImageLoad : function()
33749 resize : function()
33751 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33753 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33755 if(this.bgimage.length){
33756 var image = this.el.select('.roo-brick-image-view', true).first();
33758 image.setWidth(paragraph.getWidth());
33761 image.setHeight(paragraph.getWidth());
33764 this.el.setHeight(image.getHeight());
33765 paragraph.setHeight(image.getHeight());
33771 enter: function(e, el)
33773 e.preventDefault();
33775 if(this.bgimage.length){
33776 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33777 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33781 leave: function(e, el)
33783 e.preventDefault();
33785 if(this.bgimage.length){
33786 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33787 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33802 * @class Roo.bootstrap.NumberField
33803 * @extends Roo.bootstrap.Input
33804 * Bootstrap NumberField class
33810 * Create a new NumberField
33811 * @param {Object} config The config object
33814 Roo.bootstrap.NumberField = function(config){
33815 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33818 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33821 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33823 allowDecimals : true,
33825 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33827 decimalSeparator : ".",
33829 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33831 decimalPrecision : 2,
33833 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33835 allowNegative : true,
33838 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33842 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33844 minValue : Number.NEGATIVE_INFINITY,
33846 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33848 maxValue : Number.MAX_VALUE,
33850 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33852 minText : "The minimum value for this field is {0}",
33854 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33856 maxText : "The maximum value for this field is {0}",
33858 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33859 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33861 nanText : "{0} is not a valid number",
33863 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33865 thousandsDelimiter : false,
33867 * @cfg {String} valueAlign alignment of value
33869 valueAlign : "left",
33871 getAutoCreate : function()
33873 var hiddenInput = {
33877 cls: 'hidden-number-input'
33881 hiddenInput.name = this.name;
33886 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33888 this.name = hiddenInput.name;
33890 if(cfg.cn.length > 0) {
33891 cfg.cn.push(hiddenInput);
33898 initEvents : function()
33900 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33902 var allowed = "0123456789";
33904 if(this.allowDecimals){
33905 allowed += this.decimalSeparator;
33908 if(this.allowNegative){
33912 if(this.thousandsDelimiter) {
33916 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33918 var keyPress = function(e){
33920 var k = e.getKey();
33922 var c = e.getCharCode();
33925 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33926 allowed.indexOf(String.fromCharCode(c)) === -1
33932 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33936 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33941 this.el.on("keypress", keyPress, this);
33944 validateValue : function(value)
33947 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33951 var num = this.parseValue(value);
33954 this.markInvalid(String.format(this.nanText, value));
33958 if(num < this.minValue){
33959 this.markInvalid(String.format(this.minText, this.minValue));
33963 if(num > this.maxValue){
33964 this.markInvalid(String.format(this.maxText, this.maxValue));
33971 getValue : function()
33973 var v = this.hiddenEl().getValue();
33975 return this.fixPrecision(this.parseValue(v));
33978 parseValue : function(value)
33980 if(this.thousandsDelimiter) {
33982 r = new RegExp(",", "g");
33983 value = value.replace(r, "");
33986 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33987 return isNaN(value) ? '' : value;
33990 fixPrecision : function(value)
33992 if(this.thousandsDelimiter) {
33994 r = new RegExp(",", "g");
33995 value = value.replace(r, "");
33998 var nan = isNaN(value);
34000 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
34001 return nan ? '' : value;
34003 return parseFloat(value).toFixed(this.decimalPrecision);
34006 setValue : function(v)
34008 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34014 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34016 this.inputEl().dom.value = (v == '') ? '' :
34017 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34019 if(!this.allowZero && v === '0') {
34020 this.hiddenEl().dom.value = '';
34021 this.inputEl().dom.value = '';
34028 decimalPrecisionFcn : function(v)
34030 return Math.floor(v);
34033 beforeBlur : function()
34035 var v = this.parseValue(this.getRawValue());
34037 if(v || v === 0 || v === ''){
34042 hiddenEl : function()
34044 return this.el.select('input.hidden-number-input',true).first();
34056 * @class Roo.bootstrap.DocumentSlider
34057 * @extends Roo.bootstrap.Component
34058 * Bootstrap DocumentSlider class
34061 * Create a new DocumentViewer
34062 * @param {Object} config The config object
34065 Roo.bootstrap.DocumentSlider = function(config){
34066 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34073 * Fire after initEvent
34074 * @param {Roo.bootstrap.DocumentSlider} this
34079 * Fire after update
34080 * @param {Roo.bootstrap.DocumentSlider} this
34086 * @param {Roo.bootstrap.DocumentSlider} this
34092 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
34098 getAutoCreate : function()
34102 cls : 'roo-document-slider',
34106 cls : 'roo-document-slider-header',
34110 cls : 'roo-document-slider-header-title'
34116 cls : 'roo-document-slider-body',
34120 cls : 'roo-document-slider-prev',
34124 cls : 'fa fa-chevron-left'
34130 cls : 'roo-document-slider-thumb',
34134 cls : 'roo-document-slider-image'
34140 cls : 'roo-document-slider-next',
34144 cls : 'fa fa-chevron-right'
34156 initEvents : function()
34158 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34159 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34161 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34162 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34164 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34165 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34167 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34168 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34170 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34171 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34173 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34174 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34176 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34177 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34179 this.thumbEl.on('click', this.onClick, this);
34181 this.prevIndicator.on('click', this.prev, this);
34183 this.nextIndicator.on('click', this.next, this);
34187 initial : function()
34189 if(this.files.length){
34190 this.indicator = 1;
34194 this.fireEvent('initial', this);
34197 update : function()
34199 this.imageEl.attr('src', this.files[this.indicator - 1]);
34201 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34203 this.prevIndicator.show();
34205 if(this.indicator == 1){
34206 this.prevIndicator.hide();
34209 this.nextIndicator.show();
34211 if(this.indicator == this.files.length){
34212 this.nextIndicator.hide();
34215 this.thumbEl.scrollTo('top');
34217 this.fireEvent('update', this);
34220 onClick : function(e)
34222 e.preventDefault();
34224 this.fireEvent('click', this);
34229 e.preventDefault();
34231 this.indicator = Math.max(1, this.indicator - 1);
34238 e.preventDefault();
34240 this.indicator = Math.min(this.files.length, this.indicator + 1);
34254 * @class Roo.bootstrap.RadioSet
34255 * @extends Roo.bootstrap.Input
34256 * Bootstrap RadioSet class
34257 * @cfg {String} indicatorpos (left|right) default left
34258 * @cfg {Boolean} inline (true|false) inline the element (default true)
34259 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34261 * Create a new RadioSet
34262 * @param {Object} config The config object
34265 Roo.bootstrap.RadioSet = function(config){
34267 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34271 Roo.bootstrap.RadioSet.register(this);
34276 * Fires when the element is checked or unchecked.
34277 * @param {Roo.bootstrap.RadioSet} this This radio
34278 * @param {Roo.bootstrap.Radio} item The checked item
34283 * Fires when the element is click.
34284 * @param {Roo.bootstrap.RadioSet} this This radio set
34285 * @param {Roo.bootstrap.Radio} item The checked item
34286 * @param {Roo.EventObject} e The event object
34293 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
34301 indicatorpos : 'left',
34303 getAutoCreate : function()
34307 cls : 'roo-radio-set-label',
34311 html : this.fieldLabel
34315 if (Roo.bootstrap.version == 3) {
34318 if(this.indicatorpos == 'left'){
34321 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34322 tooltip : 'This field is required'
34327 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34328 tooltip : 'This field is required'
34334 cls : 'roo-radio-set-items'
34337 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34339 if (align === 'left' && this.fieldLabel.length) {
34342 cls : "roo-radio-set-right",
34348 if(this.labelWidth > 12){
34349 label.style = "width: " + this.labelWidth + 'px';
34352 if(this.labelWidth < 13 && this.labelmd == 0){
34353 this.labelmd = this.labelWidth;
34356 if(this.labellg > 0){
34357 label.cls += ' col-lg-' + this.labellg;
34358 items.cls += ' col-lg-' + (12 - this.labellg);
34361 if(this.labelmd > 0){
34362 label.cls += ' col-md-' + this.labelmd;
34363 items.cls += ' col-md-' + (12 - this.labelmd);
34366 if(this.labelsm > 0){
34367 label.cls += ' col-sm-' + this.labelsm;
34368 items.cls += ' col-sm-' + (12 - this.labelsm);
34371 if(this.labelxs > 0){
34372 label.cls += ' col-xs-' + this.labelxs;
34373 items.cls += ' col-xs-' + (12 - this.labelxs);
34379 cls : 'roo-radio-set',
34383 cls : 'roo-radio-set-input',
34386 value : this.value ? this.value : ''
34393 if(this.weight.length){
34394 cfg.cls += ' roo-radio-' + this.weight;
34398 cfg.cls += ' roo-radio-set-inline';
34402 ['xs','sm','md','lg'].map(function(size){
34403 if (settings[size]) {
34404 cfg.cls += ' col-' + size + '-' + settings[size];
34412 initEvents : function()
34414 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34415 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34417 if(!this.fieldLabel.length){
34418 this.labelEl.hide();
34421 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34422 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34424 this.indicator = this.indicatorEl();
34426 if(this.indicator){
34427 this.indicator.addClass('invisible');
34430 this.originalValue = this.getValue();
34434 inputEl: function ()
34436 return this.el.select('.roo-radio-set-input', true).first();
34439 getChildContainer : function()
34441 return this.itemsEl;
34444 register : function(item)
34446 this.radioes.push(item);
34450 validate : function()
34452 if(this.getVisibilityEl().hasClass('hidden')){
34458 Roo.each(this.radioes, function(i){
34467 if(this.allowBlank) {
34471 if(this.disabled || valid){
34476 this.markInvalid();
34481 markValid : function()
34483 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34484 this.indicatorEl().removeClass('visible');
34485 this.indicatorEl().addClass('invisible');
34489 if (Roo.bootstrap.version == 3) {
34490 this.el.removeClass([this.invalidClass, this.validClass]);
34491 this.el.addClass(this.validClass);
34493 this.el.removeClass(['is-invalid','is-valid']);
34494 this.el.addClass(['is-valid']);
34496 this.fireEvent('valid', this);
34499 markInvalid : function(msg)
34501 if(this.allowBlank || this.disabled){
34505 if(this.labelEl.isVisible(true) && this.indicatorEl()){
34506 this.indicatorEl().removeClass('invisible');
34507 this.indicatorEl().addClass('visible');
34509 if (Roo.bootstrap.version == 3) {
34510 this.el.removeClass([this.invalidClass, this.validClass]);
34511 this.el.addClass(this.invalidClass);
34513 this.el.removeClass(['is-invalid','is-valid']);
34514 this.el.addClass(['is-invalid']);
34517 this.fireEvent('invalid', this, msg);
34521 setValue : function(v, suppressEvent)
34523 if(this.value === v){
34530 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34533 Roo.each(this.radioes, function(i){
34535 i.el.removeClass('checked');
34538 Roo.each(this.radioes, function(i){
34540 if(i.value === v || i.value.toString() === v.toString()){
34542 i.el.addClass('checked');
34544 if(suppressEvent !== true){
34545 this.fireEvent('check', this, i);
34556 clearInvalid : function(){
34558 if(!this.el || this.preventMark){
34562 this.el.removeClass([this.invalidClass]);
34564 this.fireEvent('valid', this);
34569 Roo.apply(Roo.bootstrap.RadioSet, {
34573 register : function(set)
34575 this.groups[set.name] = set;
34578 get: function(name)
34580 if (typeof(this.groups[name]) == 'undefined') {
34584 return this.groups[name] ;
34590 * Ext JS Library 1.1.1
34591 * Copyright(c) 2006-2007, Ext JS, LLC.
34593 * Originally Released Under LGPL - original licence link has changed is not relivant.
34596 * <script type="text/javascript">
34601 * @class Roo.bootstrap.SplitBar
34602 * @extends Roo.util.Observable
34603 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34607 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34608 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34609 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34610 split.minSize = 100;
34611 split.maxSize = 600;
34612 split.animate = true;
34613 split.on('moved', splitterMoved);
34616 * Create a new SplitBar
34617 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34618 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34619 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34620 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34621 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34622 position of the SplitBar).
34624 Roo.bootstrap.SplitBar = function(cfg){
34629 // dragElement : elm
34630 // resizingElement: el,
34632 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34633 // placement : Roo.bootstrap.SplitBar.LEFT ,
34634 // existingProxy ???
34637 this.el = Roo.get(cfg.dragElement, true);
34638 this.el.dom.unselectable = "on";
34640 this.resizingEl = Roo.get(cfg.resizingElement, true);
34644 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34645 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34648 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34651 * The minimum size of the resizing element. (Defaults to 0)
34657 * The maximum size of the resizing element. (Defaults to 2000)
34660 this.maxSize = 2000;
34663 * Whether to animate the transition to the new size
34666 this.animate = false;
34669 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34672 this.useShim = false;
34677 if(!cfg.existingProxy){
34679 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34681 this.proxy = Roo.get(cfg.existingProxy).dom;
34684 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34687 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34690 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34693 this.dragSpecs = {};
34696 * @private The adapter to use to positon and resize elements
34698 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34699 this.adapter.init(this);
34701 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34703 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34704 this.el.addClass("roo-splitbar-h");
34707 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34708 this.el.addClass("roo-splitbar-v");
34714 * Fires when the splitter is moved (alias for {@link #event-moved})
34715 * @param {Roo.bootstrap.SplitBar} this
34716 * @param {Number} newSize the new width or height
34721 * Fires when the splitter is moved
34722 * @param {Roo.bootstrap.SplitBar} this
34723 * @param {Number} newSize the new width or height
34727 * @event beforeresize
34728 * Fires before the splitter is dragged
34729 * @param {Roo.bootstrap.SplitBar} this
34731 "beforeresize" : true,
34733 "beforeapply" : true
34736 Roo.util.Observable.call(this);
34739 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34740 onStartProxyDrag : function(x, y){
34741 this.fireEvent("beforeresize", this);
34743 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34745 o.enableDisplayMode("block");
34746 // all splitbars share the same overlay
34747 Roo.bootstrap.SplitBar.prototype.overlay = o;
34749 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34750 this.overlay.show();
34751 Roo.get(this.proxy).setDisplayed("block");
34752 var size = this.adapter.getElementSize(this);
34753 this.activeMinSize = this.getMinimumSize();;
34754 this.activeMaxSize = this.getMaximumSize();;
34755 var c1 = size - this.activeMinSize;
34756 var c2 = Math.max(this.activeMaxSize - size, 0);
34757 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34758 this.dd.resetConstraints();
34759 this.dd.setXConstraint(
34760 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34761 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34763 this.dd.setYConstraint(0, 0);
34765 this.dd.resetConstraints();
34766 this.dd.setXConstraint(0, 0);
34767 this.dd.setYConstraint(
34768 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34769 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34772 this.dragSpecs.startSize = size;
34773 this.dragSpecs.startPoint = [x, y];
34774 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34778 * @private Called after the drag operation by the DDProxy
34780 onEndProxyDrag : function(e){
34781 Roo.get(this.proxy).setDisplayed(false);
34782 var endPoint = Roo.lib.Event.getXY(e);
34784 this.overlay.hide();
34787 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34788 newSize = this.dragSpecs.startSize +
34789 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34790 endPoint[0] - this.dragSpecs.startPoint[0] :
34791 this.dragSpecs.startPoint[0] - endPoint[0]
34794 newSize = this.dragSpecs.startSize +
34795 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34796 endPoint[1] - this.dragSpecs.startPoint[1] :
34797 this.dragSpecs.startPoint[1] - endPoint[1]
34800 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34801 if(newSize != this.dragSpecs.startSize){
34802 if(this.fireEvent('beforeapply', this, newSize) !== false){
34803 this.adapter.setElementSize(this, newSize);
34804 this.fireEvent("moved", this, newSize);
34805 this.fireEvent("resize", this, newSize);
34811 * Get the adapter this SplitBar uses
34812 * @return The adapter object
34814 getAdapter : function(){
34815 return this.adapter;
34819 * Set the adapter this SplitBar uses
34820 * @param {Object} adapter A SplitBar adapter object
34822 setAdapter : function(adapter){
34823 this.adapter = adapter;
34824 this.adapter.init(this);
34828 * Gets the minimum size for the resizing element
34829 * @return {Number} The minimum size
34831 getMinimumSize : function(){
34832 return this.minSize;
34836 * Sets the minimum size for the resizing element
34837 * @param {Number} minSize The minimum size
34839 setMinimumSize : function(minSize){
34840 this.minSize = minSize;
34844 * Gets the maximum size for the resizing element
34845 * @return {Number} The maximum size
34847 getMaximumSize : function(){
34848 return this.maxSize;
34852 * Sets the maximum size for the resizing element
34853 * @param {Number} maxSize The maximum size
34855 setMaximumSize : function(maxSize){
34856 this.maxSize = maxSize;
34860 * Sets the initialize size for the resizing element
34861 * @param {Number} size The initial size
34863 setCurrentSize : function(size){
34864 var oldAnimate = this.animate;
34865 this.animate = false;
34866 this.adapter.setElementSize(this, size);
34867 this.animate = oldAnimate;
34871 * Destroy this splitbar.
34872 * @param {Boolean} removeEl True to remove the element
34874 destroy : function(removeEl){
34876 this.shim.remove();
34879 this.proxy.parentNode.removeChild(this.proxy);
34887 * @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.
34889 Roo.bootstrap.SplitBar.createProxy = function(dir){
34890 var proxy = new Roo.Element(document.createElement("div"));
34891 proxy.unselectable();
34892 var cls = 'roo-splitbar-proxy';
34893 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34894 document.body.appendChild(proxy.dom);
34899 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34900 * Default Adapter. It assumes the splitter and resizing element are not positioned
34901 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34903 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34906 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34907 // do nothing for now
34908 init : function(s){
34912 * Called before drag operations to get the current size of the resizing element.
34913 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34915 getElementSize : function(s){
34916 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34917 return s.resizingEl.getWidth();
34919 return s.resizingEl.getHeight();
34924 * Called after drag operations to set the size of the resizing element.
34925 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34926 * @param {Number} newSize The new size to set
34927 * @param {Function} onComplete A function to be invoked when resizing is complete
34929 setElementSize : function(s, newSize, onComplete){
34930 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34932 s.resizingEl.setWidth(newSize);
34934 onComplete(s, newSize);
34937 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34942 s.resizingEl.setHeight(newSize);
34944 onComplete(s, newSize);
34947 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34954 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34955 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34956 * Adapter that moves the splitter element to align with the resized sizing element.
34957 * Used with an absolute positioned SplitBar.
34958 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34959 * document.body, make sure you assign an id to the body element.
34961 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34962 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34963 this.container = Roo.get(container);
34966 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34967 init : function(s){
34968 this.basic.init(s);
34971 getElementSize : function(s){
34972 return this.basic.getElementSize(s);
34975 setElementSize : function(s, newSize, onComplete){
34976 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34979 moveSplitter : function(s){
34980 var yes = Roo.bootstrap.SplitBar;
34981 switch(s.placement){
34983 s.el.setX(s.resizingEl.getRight());
34986 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34989 s.el.setY(s.resizingEl.getBottom());
34992 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34999 * Orientation constant - Create a vertical SplitBar
35003 Roo.bootstrap.SplitBar.VERTICAL = 1;
35006 * Orientation constant - Create a horizontal SplitBar
35010 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35013 * Placement constant - The resizing element is to the left of the splitter element
35017 Roo.bootstrap.SplitBar.LEFT = 1;
35020 * Placement constant - The resizing element is to the right of the splitter element
35024 Roo.bootstrap.SplitBar.RIGHT = 2;
35027 * Placement constant - The resizing element is positioned above the splitter element
35031 Roo.bootstrap.SplitBar.TOP = 3;
35034 * Placement constant - The resizing element is positioned under splitter element
35038 Roo.bootstrap.SplitBar.BOTTOM = 4;
35039 Roo.namespace("Roo.bootstrap.layout");/*
35041 * Ext JS Library 1.1.1
35042 * Copyright(c) 2006-2007, Ext JS, LLC.
35044 * Originally Released Under LGPL - original licence link has changed is not relivant.
35047 * <script type="text/javascript">
35051 * @class Roo.bootstrap.layout.Manager
35052 * @extends Roo.bootstrap.Component
35053 * Base class for layout managers.
35055 Roo.bootstrap.layout.Manager = function(config)
35057 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35063 /** false to disable window resize monitoring @type Boolean */
35064 this.monitorWindowResize = true;
35069 * Fires when a layout is performed.
35070 * @param {Roo.LayoutManager} this
35074 * @event regionresized
35075 * Fires when the user resizes a region.
35076 * @param {Roo.LayoutRegion} region The resized region
35077 * @param {Number} newSize The new size (width for east/west, height for north/south)
35079 "regionresized" : true,
35081 * @event regioncollapsed
35082 * Fires when a region is collapsed.
35083 * @param {Roo.LayoutRegion} region The collapsed region
35085 "regioncollapsed" : true,
35087 * @event regionexpanded
35088 * Fires when a region is expanded.
35089 * @param {Roo.LayoutRegion} region The expanded region
35091 "regionexpanded" : true
35093 this.updating = false;
35096 this.el = Roo.get(config.el);
35102 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35107 monitorWindowResize : true,
35113 onRender : function(ct, position)
35116 this.el = Roo.get(ct);
35119 //this.fireEvent('render',this);
35123 initEvents: function()
35127 // ie scrollbar fix
35128 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35129 document.body.scroll = "no";
35130 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35131 this.el.position('relative');
35133 this.id = this.el.id;
35134 this.el.addClass("roo-layout-container");
35135 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35136 if(this.el.dom != document.body ) {
35137 this.el.on('resize', this.layout,this);
35138 this.el.on('show', this.layout,this);
35144 * Returns true if this layout is currently being updated
35145 * @return {Boolean}
35147 isUpdating : function(){
35148 return this.updating;
35152 * Suspend the LayoutManager from doing auto-layouts while
35153 * making multiple add or remove calls
35155 beginUpdate : function(){
35156 this.updating = true;
35160 * Restore auto-layouts and optionally disable the manager from performing a layout
35161 * @param {Boolean} noLayout true to disable a layout update
35163 endUpdate : function(noLayout){
35164 this.updating = false;
35170 layout: function(){
35174 onRegionResized : function(region, newSize){
35175 this.fireEvent("regionresized", region, newSize);
35179 onRegionCollapsed : function(region){
35180 this.fireEvent("regioncollapsed", region);
35183 onRegionExpanded : function(region){
35184 this.fireEvent("regionexpanded", region);
35188 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35189 * performs box-model adjustments.
35190 * @return {Object} The size as an object {width: (the width), height: (the height)}
35192 getViewSize : function()
35195 if(this.el.dom != document.body){
35196 size = this.el.getSize();
35198 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35200 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35201 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35206 * Returns the Element this layout is bound to.
35207 * @return {Roo.Element}
35209 getEl : function(){
35214 * Returns the specified region.
35215 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35216 * @return {Roo.LayoutRegion}
35218 getRegion : function(target){
35219 return this.regions[target.toLowerCase()];
35222 onWindowResize : function(){
35223 if(this.monitorWindowResize){
35230 * Ext JS Library 1.1.1
35231 * Copyright(c) 2006-2007, Ext JS, LLC.
35233 * Originally Released Under LGPL - original licence link has changed is not relivant.
35236 * <script type="text/javascript">
35239 * @class Roo.bootstrap.layout.Border
35240 * @extends Roo.bootstrap.layout.Manager
35241 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35242 * please see: examples/bootstrap/nested.html<br><br>
35244 <b>The container the layout is rendered into can be either the body element or any other element.
35245 If it is not the body element, the container needs to either be an absolute positioned element,
35246 or you will need to add "position:relative" to the css of the container. You will also need to specify
35247 the container size if it is not the body element.</b>
35250 * Create a new Border
35251 * @param {Object} config Configuration options
35253 Roo.bootstrap.layout.Border = function(config){
35254 config = config || {};
35255 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35259 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35260 if(config[region]){
35261 config[region].region = region;
35262 this.addRegion(config[region]);
35268 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
35270 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35272 parent : false, // this might point to a 'nest' or a ???
35275 * Creates and adds a new region if it doesn't already exist.
35276 * @param {String} target The target region key (north, south, east, west or center).
35277 * @param {Object} config The regions config object
35278 * @return {BorderLayoutRegion} The new region
35280 addRegion : function(config)
35282 if(!this.regions[config.region]){
35283 var r = this.factory(config);
35284 this.bindRegion(r);
35286 return this.regions[config.region];
35290 bindRegion : function(r){
35291 this.regions[r.config.region] = r;
35293 r.on("visibilitychange", this.layout, this);
35294 r.on("paneladded", this.layout, this);
35295 r.on("panelremoved", this.layout, this);
35296 r.on("invalidated", this.layout, this);
35297 r.on("resized", this.onRegionResized, this);
35298 r.on("collapsed", this.onRegionCollapsed, this);
35299 r.on("expanded", this.onRegionExpanded, this);
35303 * Performs a layout update.
35305 layout : function()
35307 if(this.updating) {
35311 // render all the rebions if they have not been done alreayd?
35312 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35313 if(this.regions[region] && !this.regions[region].bodyEl){
35314 this.regions[region].onRender(this.el)
35318 var size = this.getViewSize();
35319 var w = size.width;
35320 var h = size.height;
35325 //var x = 0, y = 0;
35327 var rs = this.regions;
35328 var north = rs["north"];
35329 var south = rs["south"];
35330 var west = rs["west"];
35331 var east = rs["east"];
35332 var center = rs["center"];
35333 //if(this.hideOnLayout){ // not supported anymore
35334 //c.el.setStyle("display", "none");
35336 if(north && north.isVisible()){
35337 var b = north.getBox();
35338 var m = north.getMargins();
35339 b.width = w - (m.left+m.right);
35342 centerY = b.height + b.y + m.bottom;
35343 centerH -= centerY;
35344 north.updateBox(this.safeBox(b));
35346 if(south && south.isVisible()){
35347 var b = south.getBox();
35348 var m = south.getMargins();
35349 b.width = w - (m.left+m.right);
35351 var totalHeight = (b.height + m.top + m.bottom);
35352 b.y = h - totalHeight + m.top;
35353 centerH -= totalHeight;
35354 south.updateBox(this.safeBox(b));
35356 if(west && west.isVisible()){
35357 var b = west.getBox();
35358 var m = west.getMargins();
35359 b.height = centerH - (m.top+m.bottom);
35361 b.y = centerY + m.top;
35362 var totalWidth = (b.width + m.left + m.right);
35363 centerX += totalWidth;
35364 centerW -= totalWidth;
35365 west.updateBox(this.safeBox(b));
35367 if(east && east.isVisible()){
35368 var b = east.getBox();
35369 var m = east.getMargins();
35370 b.height = centerH - (m.top+m.bottom);
35371 var totalWidth = (b.width + m.left + m.right);
35372 b.x = w - totalWidth + m.left;
35373 b.y = centerY + m.top;
35374 centerW -= totalWidth;
35375 east.updateBox(this.safeBox(b));
35378 var m = center.getMargins();
35380 x: centerX + m.left,
35381 y: centerY + m.top,
35382 width: centerW - (m.left+m.right),
35383 height: centerH - (m.top+m.bottom)
35385 //if(this.hideOnLayout){
35386 //center.el.setStyle("display", "block");
35388 center.updateBox(this.safeBox(centerBox));
35391 this.fireEvent("layout", this);
35395 safeBox : function(box){
35396 box.width = Math.max(0, box.width);
35397 box.height = Math.max(0, box.height);
35402 * Adds a ContentPanel (or subclass) to this layout.
35403 * @param {String} target The target region key (north, south, east, west or center).
35404 * @param {Roo.ContentPanel} panel The panel to add
35405 * @return {Roo.ContentPanel} The added panel
35407 add : function(target, panel){
35409 target = target.toLowerCase();
35410 return this.regions[target].add(panel);
35414 * Remove a ContentPanel (or subclass) to this layout.
35415 * @param {String} target The target region key (north, south, east, west or center).
35416 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35417 * @return {Roo.ContentPanel} The removed panel
35419 remove : function(target, panel){
35420 target = target.toLowerCase();
35421 return this.regions[target].remove(panel);
35425 * Searches all regions for a panel with the specified id
35426 * @param {String} panelId
35427 * @return {Roo.ContentPanel} The panel or null if it wasn't found
35429 findPanel : function(panelId){
35430 var rs = this.regions;
35431 for(var target in rs){
35432 if(typeof rs[target] != "function"){
35433 var p = rs[target].getPanel(panelId);
35443 * Searches all regions for a panel with the specified id and activates (shows) it.
35444 * @param {String/ContentPanel} panelId The panels id or the panel itself
35445 * @return {Roo.ContentPanel} The shown panel or null
35447 showPanel : function(panelId) {
35448 var rs = this.regions;
35449 for(var target in rs){
35450 var r = rs[target];
35451 if(typeof r != "function"){
35452 if(r.hasPanel(panelId)){
35453 return r.showPanel(panelId);
35461 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35462 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35465 restoreState : function(provider){
35467 provider = Roo.state.Manager;
35469 var sm = new Roo.LayoutStateManager();
35470 sm.init(this, provider);
35476 * Adds a xtype elements to the layout.
35480 xtype : 'ContentPanel',
35487 xtype : 'NestedLayoutPanel',
35493 items : [ ... list of content panels or nested layout panels.. ]
35497 * @param {Object} cfg Xtype definition of item to add.
35499 addxtype : function(cfg)
35501 // basically accepts a pannel...
35502 // can accept a layout region..!?!?
35503 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35506 // theory? children can only be panels??
35508 //if (!cfg.xtype.match(/Panel$/)) {
35513 if (typeof(cfg.region) == 'undefined') {
35514 Roo.log("Failed to add Panel, region was not set");
35518 var region = cfg.region;
35524 xitems = cfg.items;
35529 if ( region == 'center') {
35530 Roo.log("Center: " + cfg.title);
35536 case 'Content': // ContentPanel (el, cfg)
35537 case 'Scroll': // ContentPanel (el, cfg)
35539 cfg.autoCreate = cfg.autoCreate || true;
35540 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35542 // var el = this.el.createChild();
35543 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35546 this.add(region, ret);
35550 case 'TreePanel': // our new panel!
35551 cfg.el = this.el.createChild();
35552 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35553 this.add(region, ret);
35558 // create a new Layout (which is a Border Layout...
35560 var clayout = cfg.layout;
35561 clayout.el = this.el.createChild();
35562 clayout.items = clayout.items || [];
35566 // replace this exitems with the clayout ones..
35567 xitems = clayout.items;
35569 // force background off if it's in center...
35570 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35571 cfg.background = false;
35573 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35576 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35577 //console.log('adding nested layout panel ' + cfg.toSource());
35578 this.add(region, ret);
35579 nb = {}; /// find first...
35584 // needs grid and region
35586 //var el = this.getRegion(region).el.createChild();
35588 *var el = this.el.createChild();
35589 // create the grid first...
35590 cfg.grid.container = el;
35591 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35594 if (region == 'center' && this.active ) {
35595 cfg.background = false;
35598 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35600 this.add(region, ret);
35602 if (cfg.background) {
35603 // render grid on panel activation (if panel background)
35604 ret.on('activate', function(gp) {
35605 if (!gp.grid.rendered) {
35606 // gp.grid.render(el);
35610 // cfg.grid.render(el);
35616 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35617 // it was the old xcomponent building that caused this before.
35618 // espeically if border is the top element in the tree.
35628 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35630 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35631 this.add(region, ret);
35635 throw "Can not add '" + cfg.xtype + "' to Border";
35641 this.beginUpdate();
35645 Roo.each(xitems, function(i) {
35646 region = nb && i.region ? i.region : false;
35648 var add = ret.addxtype(i);
35651 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35652 if (!i.background) {
35653 abn[region] = nb[region] ;
35660 // make the last non-background panel active..
35661 //if (nb) { Roo.log(abn); }
35664 for(var r in abn) {
35665 region = this.getRegion(r);
35667 // tried using nb[r], but it does not work..
35669 region.showPanel(abn[r]);
35680 factory : function(cfg)
35683 var validRegions = Roo.bootstrap.layout.Border.regions;
35685 var target = cfg.region;
35688 var r = Roo.bootstrap.layout;
35692 return new r.North(cfg);
35694 return new r.South(cfg);
35696 return new r.East(cfg);
35698 return new r.West(cfg);
35700 return new r.Center(cfg);
35702 throw 'Layout region "'+target+'" not supported.';
35709 * Ext JS Library 1.1.1
35710 * Copyright(c) 2006-2007, Ext JS, LLC.
35712 * Originally Released Under LGPL - original licence link has changed is not relivant.
35715 * <script type="text/javascript">
35719 * @class Roo.bootstrap.layout.Basic
35720 * @extends Roo.util.Observable
35721 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35722 * and does not have a titlebar, tabs or any other features. All it does is size and position
35723 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35724 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35725 * @cfg {string} region the region that it inhabits..
35726 * @cfg {bool} skipConfig skip config?
35730 Roo.bootstrap.layout.Basic = function(config){
35732 this.mgr = config.mgr;
35734 this.position = config.region;
35736 var skipConfig = config.skipConfig;
35740 * @scope Roo.BasicLayoutRegion
35744 * @event beforeremove
35745 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35746 * @param {Roo.LayoutRegion} this
35747 * @param {Roo.ContentPanel} panel The panel
35748 * @param {Object} e The cancel event object
35750 "beforeremove" : true,
35752 * @event invalidated
35753 * Fires when the layout for this region is changed.
35754 * @param {Roo.LayoutRegion} this
35756 "invalidated" : true,
35758 * @event visibilitychange
35759 * Fires when this region is shown or hidden
35760 * @param {Roo.LayoutRegion} this
35761 * @param {Boolean} visibility true or false
35763 "visibilitychange" : true,
35765 * @event paneladded
35766 * Fires when a panel is added.
35767 * @param {Roo.LayoutRegion} this
35768 * @param {Roo.ContentPanel} panel The panel
35770 "paneladded" : true,
35772 * @event panelremoved
35773 * Fires when a panel is removed.
35774 * @param {Roo.LayoutRegion} this
35775 * @param {Roo.ContentPanel} panel The panel
35777 "panelremoved" : true,
35779 * @event beforecollapse
35780 * Fires when this region before collapse.
35781 * @param {Roo.LayoutRegion} this
35783 "beforecollapse" : true,
35786 * Fires when this region is collapsed.
35787 * @param {Roo.LayoutRegion} this
35789 "collapsed" : true,
35792 * Fires when this region is expanded.
35793 * @param {Roo.LayoutRegion} this
35798 * Fires when this region is slid into view.
35799 * @param {Roo.LayoutRegion} this
35801 "slideshow" : true,
35804 * Fires when this region slides out of view.
35805 * @param {Roo.LayoutRegion} this
35807 "slidehide" : true,
35809 * @event panelactivated
35810 * Fires when a panel is activated.
35811 * @param {Roo.LayoutRegion} this
35812 * @param {Roo.ContentPanel} panel The activated panel
35814 "panelactivated" : true,
35817 * Fires when the user resizes this region.
35818 * @param {Roo.LayoutRegion} this
35819 * @param {Number} newSize The new size (width for east/west, height for north/south)
35823 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35824 this.panels = new Roo.util.MixedCollection();
35825 this.panels.getKey = this.getPanelId.createDelegate(this);
35827 this.activePanel = null;
35828 // ensure listeners are added...
35830 if (config.listeners || config.events) {
35831 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35832 listeners : config.listeners || {},
35833 events : config.events || {}
35837 if(skipConfig !== true){
35838 this.applyConfig(config);
35842 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35844 getPanelId : function(p){
35848 applyConfig : function(config){
35849 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35850 this.config = config;
35855 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35856 * the width, for horizontal (north, south) the height.
35857 * @param {Number} newSize The new width or height
35859 resizeTo : function(newSize){
35860 var el = this.el ? this.el :
35861 (this.activePanel ? this.activePanel.getEl() : null);
35863 switch(this.position){
35866 el.setWidth(newSize);
35867 this.fireEvent("resized", this, newSize);
35871 el.setHeight(newSize);
35872 this.fireEvent("resized", this, newSize);
35878 getBox : function(){
35879 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35882 getMargins : function(){
35883 return this.margins;
35886 updateBox : function(box){
35888 var el = this.activePanel.getEl();
35889 el.dom.style.left = box.x + "px";
35890 el.dom.style.top = box.y + "px";
35891 this.activePanel.setSize(box.width, box.height);
35895 * Returns the container element for this region.
35896 * @return {Roo.Element}
35898 getEl : function(){
35899 return this.activePanel;
35903 * Returns true if this region is currently visible.
35904 * @return {Boolean}
35906 isVisible : function(){
35907 return this.activePanel ? true : false;
35910 setActivePanel : function(panel){
35911 panel = this.getPanel(panel);
35912 if(this.activePanel && this.activePanel != panel){
35913 this.activePanel.setActiveState(false);
35914 this.activePanel.getEl().setLeftTop(-10000,-10000);
35916 this.activePanel = panel;
35917 panel.setActiveState(true);
35919 panel.setSize(this.box.width, this.box.height);
35921 this.fireEvent("panelactivated", this, panel);
35922 this.fireEvent("invalidated");
35926 * Show the specified panel.
35927 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35928 * @return {Roo.ContentPanel} The shown panel or null
35930 showPanel : function(panel){
35931 panel = this.getPanel(panel);
35933 this.setActivePanel(panel);
35939 * Get the active panel for this region.
35940 * @return {Roo.ContentPanel} The active panel or null
35942 getActivePanel : function(){
35943 return this.activePanel;
35947 * Add the passed ContentPanel(s)
35948 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35949 * @return {Roo.ContentPanel} The panel added (if only one was added)
35951 add : function(panel){
35952 if(arguments.length > 1){
35953 for(var i = 0, len = arguments.length; i < len; i++) {
35954 this.add(arguments[i]);
35958 if(this.hasPanel(panel)){
35959 this.showPanel(panel);
35962 var el = panel.getEl();
35963 if(el.dom.parentNode != this.mgr.el.dom){
35964 this.mgr.el.dom.appendChild(el.dom);
35966 if(panel.setRegion){
35967 panel.setRegion(this);
35969 this.panels.add(panel);
35970 el.setStyle("position", "absolute");
35971 if(!panel.background){
35972 this.setActivePanel(panel);
35973 if(this.config.initialSize && this.panels.getCount()==1){
35974 this.resizeTo(this.config.initialSize);
35977 this.fireEvent("paneladded", this, panel);
35982 * Returns true if the panel is in this region.
35983 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35984 * @return {Boolean}
35986 hasPanel : function(panel){
35987 if(typeof panel == "object"){ // must be panel obj
35988 panel = panel.getId();
35990 return this.getPanel(panel) ? true : false;
35994 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35995 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35996 * @param {Boolean} preservePanel Overrides the config preservePanel option
35997 * @return {Roo.ContentPanel} The panel that was removed
35999 remove : function(panel, preservePanel){
36000 panel = this.getPanel(panel);
36005 this.fireEvent("beforeremove", this, panel, e);
36006 if(e.cancel === true){
36009 var panelId = panel.getId();
36010 this.panels.removeKey(panelId);
36015 * Returns the panel specified or null if it's not in this region.
36016 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36017 * @return {Roo.ContentPanel}
36019 getPanel : function(id){
36020 if(typeof id == "object"){ // must be panel obj
36023 return this.panels.get(id);
36027 * Returns this regions position (north/south/east/west/center).
36030 getPosition: function(){
36031 return this.position;
36035 * Ext JS Library 1.1.1
36036 * Copyright(c) 2006-2007, Ext JS, LLC.
36038 * Originally Released Under LGPL - original licence link has changed is not relivant.
36041 * <script type="text/javascript">
36045 * @class Roo.bootstrap.layout.Region
36046 * @extends Roo.bootstrap.layout.Basic
36047 * This class represents a region in a layout manager.
36049 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36050 * @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})
36051 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
36052 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
36053 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
36054 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
36055 * @cfg {String} title The title for the region (overrides panel titles)
36056 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
36057 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36058 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
36059 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36060 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
36061 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36062 * the space available, similar to FireFox 1.5 tabs (defaults to false)
36063 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
36064 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
36065 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
36067 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
36068 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
36069 * @cfg {Boolean} disableTabTips True to disable tab tooltips
36070 * @cfg {Number} width For East/West panels
36071 * @cfg {Number} height For North/South panels
36072 * @cfg {Boolean} split To show the splitter
36073 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
36075 * @cfg {string} cls Extra CSS classes to add to region
36077 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
36078 * @cfg {string} region the region that it inhabits..
36081 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
36082 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
36084 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
36085 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
36086 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
36088 Roo.bootstrap.layout.Region = function(config)
36090 this.applyConfig(config);
36092 var mgr = config.mgr;
36093 var pos = config.region;
36094 config.skipConfig = true;
36095 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36098 this.onRender(mgr.el);
36101 this.visible = true;
36102 this.collapsed = false;
36103 this.unrendered_panels = [];
36106 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36108 position: '', // set by wrapper (eg. north/south etc..)
36109 unrendered_panels : null, // unrendered panels.
36111 tabPosition : false,
36113 mgr: false, // points to 'Border'
36116 createBody : function(){
36117 /** This region's body element
36118 * @type Roo.Element */
36119 this.bodyEl = this.el.createChild({
36121 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36125 onRender: function(ctr, pos)
36127 var dh = Roo.DomHelper;
36128 /** This region's container element
36129 * @type Roo.Element */
36130 this.el = dh.append(ctr.dom, {
36132 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36134 /** This region's title element
36135 * @type Roo.Element */
36137 this.titleEl = dh.append(this.el.dom, {
36139 unselectable: "on",
36140 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36142 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
36143 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36147 this.titleEl.enableDisplayMode();
36148 /** This region's title text element
36149 * @type HTMLElement */
36150 this.titleTextEl = this.titleEl.dom.firstChild;
36151 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36153 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36154 this.closeBtn.enableDisplayMode();
36155 this.closeBtn.on("click", this.closeClicked, this);
36156 this.closeBtn.hide();
36158 this.createBody(this.config);
36159 if(this.config.hideWhenEmpty){
36161 this.on("paneladded", this.validateVisibility, this);
36162 this.on("panelremoved", this.validateVisibility, this);
36164 if(this.autoScroll){
36165 this.bodyEl.setStyle("overflow", "auto");
36167 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36169 //if(c.titlebar !== false){
36170 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36171 this.titleEl.hide();
36173 this.titleEl.show();
36174 if(this.config.title){
36175 this.titleTextEl.innerHTML = this.config.title;
36179 if(this.config.collapsed){
36180 this.collapse(true);
36182 if(this.config.hidden){
36186 if (this.unrendered_panels && this.unrendered_panels.length) {
36187 for (var i =0;i< this.unrendered_panels.length; i++) {
36188 this.add(this.unrendered_panels[i]);
36190 this.unrendered_panels = null;
36196 applyConfig : function(c)
36199 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36200 var dh = Roo.DomHelper;
36201 if(c.titlebar !== false){
36202 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36203 this.collapseBtn.on("click", this.collapse, this);
36204 this.collapseBtn.enableDisplayMode();
36206 if(c.showPin === true || this.showPin){
36207 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36208 this.stickBtn.enableDisplayMode();
36209 this.stickBtn.on("click", this.expand, this);
36210 this.stickBtn.hide();
36215 /** This region's collapsed element
36216 * @type Roo.Element */
36219 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36220 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36223 if(c.floatable !== false){
36224 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36225 this.collapsedEl.on("click", this.collapseClick, this);
36228 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36229 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36230 id: "message", unselectable: "on", style:{"float":"left"}});
36231 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36233 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36234 this.expandBtn.on("click", this.expand, this);
36238 if(this.collapseBtn){
36239 this.collapseBtn.setVisible(c.collapsible == true);
36242 this.cmargins = c.cmargins || this.cmargins ||
36243 (this.position == "west" || this.position == "east" ?
36244 {top: 0, left: 2, right:2, bottom: 0} :
36245 {top: 2, left: 0, right:0, bottom: 2});
36247 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36250 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36252 this.autoScroll = c.autoScroll || false;
36257 this.duration = c.duration || .30;
36258 this.slideDuration = c.slideDuration || .45;
36263 * Returns true if this region is currently visible.
36264 * @return {Boolean}
36266 isVisible : function(){
36267 return this.visible;
36271 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36272 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
36274 //setCollapsedTitle : function(title){
36275 // title = title || " ";
36276 // if(this.collapsedTitleTextEl){
36277 // this.collapsedTitleTextEl.innerHTML = title;
36281 getBox : function(){
36283 // if(!this.collapsed){
36284 b = this.el.getBox(false, true);
36286 // b = this.collapsedEl.getBox(false, true);
36291 getMargins : function(){
36292 return this.margins;
36293 //return this.collapsed ? this.cmargins : this.margins;
36296 highlight : function(){
36297 this.el.addClass("x-layout-panel-dragover");
36300 unhighlight : function(){
36301 this.el.removeClass("x-layout-panel-dragover");
36304 updateBox : function(box)
36306 if (!this.bodyEl) {
36307 return; // not rendered yet..
36311 if(!this.collapsed){
36312 this.el.dom.style.left = box.x + "px";
36313 this.el.dom.style.top = box.y + "px";
36314 this.updateBody(box.width, box.height);
36316 this.collapsedEl.dom.style.left = box.x + "px";
36317 this.collapsedEl.dom.style.top = box.y + "px";
36318 this.collapsedEl.setSize(box.width, box.height);
36321 this.tabs.autoSizeTabs();
36325 updateBody : function(w, h)
36328 this.el.setWidth(w);
36329 w -= this.el.getBorderWidth("rl");
36330 if(this.config.adjustments){
36331 w += this.config.adjustments[0];
36334 if(h !== null && h > 0){
36335 this.el.setHeight(h);
36336 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36337 h -= this.el.getBorderWidth("tb");
36338 if(this.config.adjustments){
36339 h += this.config.adjustments[1];
36341 this.bodyEl.setHeight(h);
36343 h = this.tabs.syncHeight(h);
36346 if(this.panelSize){
36347 w = w !== null ? w : this.panelSize.width;
36348 h = h !== null ? h : this.panelSize.height;
36350 if(this.activePanel){
36351 var el = this.activePanel.getEl();
36352 w = w !== null ? w : el.getWidth();
36353 h = h !== null ? h : el.getHeight();
36354 this.panelSize = {width: w, height: h};
36355 this.activePanel.setSize(w, h);
36357 if(Roo.isIE && this.tabs){
36358 this.tabs.el.repaint();
36363 * Returns the container element for this region.
36364 * @return {Roo.Element}
36366 getEl : function(){
36371 * Hides this region.
36374 //if(!this.collapsed){
36375 this.el.dom.style.left = "-2000px";
36378 // this.collapsedEl.dom.style.left = "-2000px";
36379 // this.collapsedEl.hide();
36381 this.visible = false;
36382 this.fireEvent("visibilitychange", this, false);
36386 * Shows this region if it was previously hidden.
36389 //if(!this.collapsed){
36392 // this.collapsedEl.show();
36394 this.visible = true;
36395 this.fireEvent("visibilitychange", this, true);
36398 closeClicked : function(){
36399 if(this.activePanel){
36400 this.remove(this.activePanel);
36404 collapseClick : function(e){
36406 e.stopPropagation();
36409 e.stopPropagation();
36415 * Collapses this region.
36416 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36419 collapse : function(skipAnim, skipCheck = false){
36420 if(this.collapsed) {
36424 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36426 this.collapsed = true;
36428 this.split.el.hide();
36430 if(this.config.animate && skipAnim !== true){
36431 this.fireEvent("invalidated", this);
36432 this.animateCollapse();
36434 this.el.setLocation(-20000,-20000);
36436 this.collapsedEl.show();
36437 this.fireEvent("collapsed", this);
36438 this.fireEvent("invalidated", this);
36444 animateCollapse : function(){
36449 * Expands this region if it was previously collapsed.
36450 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36451 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36454 expand : function(e, skipAnim){
36456 e.stopPropagation();
36458 if(!this.collapsed || this.el.hasActiveFx()) {
36462 this.afterSlideIn();
36465 this.collapsed = false;
36466 if(this.config.animate && skipAnim !== true){
36467 this.animateExpand();
36471 this.split.el.show();
36473 this.collapsedEl.setLocation(-2000,-2000);
36474 this.collapsedEl.hide();
36475 this.fireEvent("invalidated", this);
36476 this.fireEvent("expanded", this);
36480 animateExpand : function(){
36484 initTabs : function()
36486 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36488 var ts = new Roo.bootstrap.panel.Tabs({
36489 el: this.bodyEl.dom,
36491 tabPosition: this.tabPosition ? this.tabPosition : 'top',
36492 disableTooltips: this.config.disableTabTips,
36493 toolbar : this.config.toolbar
36496 if(this.config.hideTabs){
36497 ts.stripWrap.setDisplayed(false);
36500 ts.resizeTabs = this.config.resizeTabs === true;
36501 ts.minTabWidth = this.config.minTabWidth || 40;
36502 ts.maxTabWidth = this.config.maxTabWidth || 250;
36503 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36504 ts.monitorResize = false;
36505 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36506 ts.bodyEl.addClass('roo-layout-tabs-body');
36507 this.panels.each(this.initPanelAsTab, this);
36510 initPanelAsTab : function(panel){
36511 var ti = this.tabs.addTab(
36515 this.config.closeOnTab && panel.isClosable(),
36518 if(panel.tabTip !== undefined){
36519 ti.setTooltip(panel.tabTip);
36521 ti.on("activate", function(){
36522 this.setActivePanel(panel);
36525 if(this.config.closeOnTab){
36526 ti.on("beforeclose", function(t, e){
36528 this.remove(panel);
36532 panel.tabItem = ti;
36537 updatePanelTitle : function(panel, title)
36539 if(this.activePanel == panel){
36540 this.updateTitle(title);
36543 var ti = this.tabs.getTab(panel.getEl().id);
36545 if(panel.tabTip !== undefined){
36546 ti.setTooltip(panel.tabTip);
36551 updateTitle : function(title){
36552 if(this.titleTextEl && !this.config.title){
36553 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36557 setActivePanel : function(panel)
36559 panel = this.getPanel(panel);
36560 if(this.activePanel && this.activePanel != panel){
36561 if(this.activePanel.setActiveState(false) === false){
36565 this.activePanel = panel;
36566 panel.setActiveState(true);
36567 if(this.panelSize){
36568 panel.setSize(this.panelSize.width, this.panelSize.height);
36571 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36573 this.updateTitle(panel.getTitle());
36575 this.fireEvent("invalidated", this);
36577 this.fireEvent("panelactivated", this, panel);
36581 * Shows the specified panel.
36582 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36583 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36585 showPanel : function(panel)
36587 panel = this.getPanel(panel);
36590 var tab = this.tabs.getTab(panel.getEl().id);
36591 if(tab.isHidden()){
36592 this.tabs.unhideTab(tab.id);
36596 this.setActivePanel(panel);
36603 * Get the active panel for this region.
36604 * @return {Roo.ContentPanel} The active panel or null
36606 getActivePanel : function(){
36607 return this.activePanel;
36610 validateVisibility : function(){
36611 if(this.panels.getCount() < 1){
36612 this.updateTitle(" ");
36613 this.closeBtn.hide();
36616 if(!this.isVisible()){
36623 * Adds the passed ContentPanel(s) to this region.
36624 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36625 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36627 add : function(panel)
36629 if(arguments.length > 1){
36630 for(var i = 0, len = arguments.length; i < len; i++) {
36631 this.add(arguments[i]);
36636 // if we have not been rendered yet, then we can not really do much of this..
36637 if (!this.bodyEl) {
36638 this.unrendered_panels.push(panel);
36645 if(this.hasPanel(panel)){
36646 this.showPanel(panel);
36649 panel.setRegion(this);
36650 this.panels.add(panel);
36651 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36652 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36653 // and hide them... ???
36654 this.bodyEl.dom.appendChild(panel.getEl().dom);
36655 if(panel.background !== true){
36656 this.setActivePanel(panel);
36658 this.fireEvent("paneladded", this, panel);
36665 this.initPanelAsTab(panel);
36669 if(panel.background !== true){
36670 this.tabs.activate(panel.getEl().id);
36672 this.fireEvent("paneladded", this, panel);
36677 * Hides the tab for the specified panel.
36678 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36680 hidePanel : function(panel){
36681 if(this.tabs && (panel = this.getPanel(panel))){
36682 this.tabs.hideTab(panel.getEl().id);
36687 * Unhides the tab for a previously hidden panel.
36688 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36690 unhidePanel : function(panel){
36691 if(this.tabs && (panel = this.getPanel(panel))){
36692 this.tabs.unhideTab(panel.getEl().id);
36696 clearPanels : function(){
36697 while(this.panels.getCount() > 0){
36698 this.remove(this.panels.first());
36703 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36704 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36705 * @param {Boolean} preservePanel Overrides the config preservePanel option
36706 * @return {Roo.ContentPanel} The panel that was removed
36708 remove : function(panel, preservePanel)
36710 panel = this.getPanel(panel);
36715 this.fireEvent("beforeremove", this, panel, e);
36716 if(e.cancel === true){
36719 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36720 var panelId = panel.getId();
36721 this.panels.removeKey(panelId);
36723 document.body.appendChild(panel.getEl().dom);
36726 this.tabs.removeTab(panel.getEl().id);
36727 }else if (!preservePanel){
36728 this.bodyEl.dom.removeChild(panel.getEl().dom);
36730 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36731 var p = this.panels.first();
36732 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36733 tempEl.appendChild(p.getEl().dom);
36734 this.bodyEl.update("");
36735 this.bodyEl.dom.appendChild(p.getEl().dom);
36737 this.updateTitle(p.getTitle());
36739 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36740 this.setActivePanel(p);
36742 panel.setRegion(null);
36743 if(this.activePanel == panel){
36744 this.activePanel = null;
36746 if(this.config.autoDestroy !== false && preservePanel !== true){
36747 try{panel.destroy();}catch(e){}
36749 this.fireEvent("panelremoved", this, panel);
36754 * Returns the TabPanel component used by this region
36755 * @return {Roo.TabPanel}
36757 getTabs : function(){
36761 createTool : function(parentEl, className){
36762 var btn = Roo.DomHelper.append(parentEl, {
36764 cls: "x-layout-tools-button",
36767 cls: "roo-layout-tools-button-inner " + className,
36771 btn.addClassOnOver("roo-layout-tools-button-over");
36776 * Ext JS Library 1.1.1
36777 * Copyright(c) 2006-2007, Ext JS, LLC.
36779 * Originally Released Under LGPL - original licence link has changed is not relivant.
36782 * <script type="text/javascript">
36788 * @class Roo.SplitLayoutRegion
36789 * @extends Roo.LayoutRegion
36790 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36792 Roo.bootstrap.layout.Split = function(config){
36793 this.cursor = config.cursor;
36794 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36797 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36799 splitTip : "Drag to resize.",
36800 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36801 useSplitTips : false,
36803 applyConfig : function(config){
36804 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36807 onRender : function(ctr,pos) {
36809 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36810 if(!this.config.split){
36815 var splitEl = Roo.DomHelper.append(ctr.dom, {
36817 id: this.el.id + "-split",
36818 cls: "roo-layout-split roo-layout-split-"+this.position,
36821 /** The SplitBar for this region
36822 * @type Roo.SplitBar */
36823 // does not exist yet...
36824 Roo.log([this.position, this.orientation]);
36826 this.split = new Roo.bootstrap.SplitBar({
36827 dragElement : splitEl,
36828 resizingElement: this.el,
36829 orientation : this.orientation
36832 this.split.on("moved", this.onSplitMove, this);
36833 this.split.useShim = this.config.useShim === true;
36834 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36835 if(this.useSplitTips){
36836 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36838 //if(config.collapsible){
36839 // this.split.el.on("dblclick", this.collapse, this);
36842 if(typeof this.config.minSize != "undefined"){
36843 this.split.minSize = this.config.minSize;
36845 if(typeof this.config.maxSize != "undefined"){
36846 this.split.maxSize = this.config.maxSize;
36848 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36849 this.hideSplitter();
36854 getHMaxSize : function(){
36855 var cmax = this.config.maxSize || 10000;
36856 var center = this.mgr.getRegion("center");
36857 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36860 getVMaxSize : function(){
36861 var cmax = this.config.maxSize || 10000;
36862 var center = this.mgr.getRegion("center");
36863 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36866 onSplitMove : function(split, newSize){
36867 this.fireEvent("resized", this, newSize);
36871 * Returns the {@link Roo.SplitBar} for this region.
36872 * @return {Roo.SplitBar}
36874 getSplitBar : function(){
36879 this.hideSplitter();
36880 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36883 hideSplitter : function(){
36885 this.split.el.setLocation(-2000,-2000);
36886 this.split.el.hide();
36892 this.split.el.show();
36894 Roo.bootstrap.layout.Split.superclass.show.call(this);
36897 beforeSlide: function(){
36898 if(Roo.isGecko){// firefox overflow auto bug workaround
36899 this.bodyEl.clip();
36901 this.tabs.bodyEl.clip();
36903 if(this.activePanel){
36904 this.activePanel.getEl().clip();
36906 if(this.activePanel.beforeSlide){
36907 this.activePanel.beforeSlide();
36913 afterSlide : function(){
36914 if(Roo.isGecko){// firefox overflow auto bug workaround
36915 this.bodyEl.unclip();
36917 this.tabs.bodyEl.unclip();
36919 if(this.activePanel){
36920 this.activePanel.getEl().unclip();
36921 if(this.activePanel.afterSlide){
36922 this.activePanel.afterSlide();
36928 initAutoHide : function(){
36929 if(this.autoHide !== false){
36930 if(!this.autoHideHd){
36931 var st = new Roo.util.DelayedTask(this.slideIn, this);
36932 this.autoHideHd = {
36933 "mouseout": function(e){
36934 if(!e.within(this.el, true)){
36938 "mouseover" : function(e){
36944 this.el.on(this.autoHideHd);
36948 clearAutoHide : function(){
36949 if(this.autoHide !== false){
36950 this.el.un("mouseout", this.autoHideHd.mouseout);
36951 this.el.un("mouseover", this.autoHideHd.mouseover);
36955 clearMonitor : function(){
36956 Roo.get(document).un("click", this.slideInIf, this);
36959 // these names are backwards but not changed for compat
36960 slideOut : function(){
36961 if(this.isSlid || this.el.hasActiveFx()){
36964 this.isSlid = true;
36965 if(this.collapseBtn){
36966 this.collapseBtn.hide();
36968 this.closeBtnState = this.closeBtn.getStyle('display');
36969 this.closeBtn.hide();
36971 this.stickBtn.show();
36974 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36975 this.beforeSlide();
36976 this.el.setStyle("z-index", 10001);
36977 this.el.slideIn(this.getSlideAnchor(), {
36978 callback: function(){
36980 this.initAutoHide();
36981 Roo.get(document).on("click", this.slideInIf, this);
36982 this.fireEvent("slideshow", this);
36989 afterSlideIn : function(){
36990 this.clearAutoHide();
36991 this.isSlid = false;
36992 this.clearMonitor();
36993 this.el.setStyle("z-index", "");
36994 if(this.collapseBtn){
36995 this.collapseBtn.show();
36997 this.closeBtn.setStyle('display', this.closeBtnState);
36999 this.stickBtn.hide();
37001 this.fireEvent("slidehide", this);
37004 slideIn : function(cb){
37005 if(!this.isSlid || this.el.hasActiveFx()){
37009 this.isSlid = false;
37010 this.beforeSlide();
37011 this.el.slideOut(this.getSlideAnchor(), {
37012 callback: function(){
37013 this.el.setLeftTop(-10000, -10000);
37015 this.afterSlideIn();
37023 slideInIf : function(e){
37024 if(!e.within(this.el)){
37029 animateCollapse : function(){
37030 this.beforeSlide();
37031 this.el.setStyle("z-index", 20000);
37032 var anchor = this.getSlideAnchor();
37033 this.el.slideOut(anchor, {
37034 callback : function(){
37035 this.el.setStyle("z-index", "");
37036 this.collapsedEl.slideIn(anchor, {duration:.3});
37038 this.el.setLocation(-10000,-10000);
37040 this.fireEvent("collapsed", this);
37047 animateExpand : function(){
37048 this.beforeSlide();
37049 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37050 this.el.setStyle("z-index", 20000);
37051 this.collapsedEl.hide({
37054 this.el.slideIn(this.getSlideAnchor(), {
37055 callback : function(){
37056 this.el.setStyle("z-index", "");
37059 this.split.el.show();
37061 this.fireEvent("invalidated", this);
37062 this.fireEvent("expanded", this);
37090 getAnchor : function(){
37091 return this.anchors[this.position];
37094 getCollapseAnchor : function(){
37095 return this.canchors[this.position];
37098 getSlideAnchor : function(){
37099 return this.sanchors[this.position];
37102 getAlignAdj : function(){
37103 var cm = this.cmargins;
37104 switch(this.position){
37120 getExpandAdj : function(){
37121 var c = this.collapsedEl, cm = this.cmargins;
37122 switch(this.position){
37124 return [-(cm.right+c.getWidth()+cm.left), 0];
37127 return [cm.right+c.getWidth()+cm.left, 0];
37130 return [0, -(cm.top+cm.bottom+c.getHeight())];
37133 return [0, cm.top+cm.bottom+c.getHeight()];
37139 * Ext JS Library 1.1.1
37140 * Copyright(c) 2006-2007, Ext JS, LLC.
37142 * Originally Released Under LGPL - original licence link has changed is not relivant.
37145 * <script type="text/javascript">
37148 * These classes are private internal classes
37150 Roo.bootstrap.layout.Center = function(config){
37151 config.region = "center";
37152 Roo.bootstrap.layout.Region.call(this, config);
37153 this.visible = true;
37154 this.minWidth = config.minWidth || 20;
37155 this.minHeight = config.minHeight || 20;
37158 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37160 // center panel can't be hidden
37164 // center panel can't be hidden
37167 getMinWidth: function(){
37168 return this.minWidth;
37171 getMinHeight: function(){
37172 return this.minHeight;
37186 Roo.bootstrap.layout.North = function(config)
37188 config.region = 'north';
37189 config.cursor = 'n-resize';
37191 Roo.bootstrap.layout.Split.call(this, config);
37195 this.split.placement = Roo.bootstrap.SplitBar.TOP;
37196 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37197 this.split.el.addClass("roo-layout-split-v");
37199 var size = config.initialSize || config.height;
37200 if(typeof size != "undefined"){
37201 this.el.setHeight(size);
37204 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37206 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37210 getBox : function(){
37211 if(this.collapsed){
37212 return this.collapsedEl.getBox();
37214 var box = this.el.getBox();
37216 box.height += this.split.el.getHeight();
37221 updateBox : function(box){
37222 if(this.split && !this.collapsed){
37223 box.height -= this.split.el.getHeight();
37224 this.split.el.setLeft(box.x);
37225 this.split.el.setTop(box.y+box.height);
37226 this.split.el.setWidth(box.width);
37228 if(this.collapsed){
37229 this.updateBody(box.width, null);
37231 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37239 Roo.bootstrap.layout.South = function(config){
37240 config.region = 'south';
37241 config.cursor = 's-resize';
37242 Roo.bootstrap.layout.Split.call(this, config);
37244 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37245 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37246 this.split.el.addClass("roo-layout-split-v");
37248 var size = config.initialSize || config.height;
37249 if(typeof size != "undefined"){
37250 this.el.setHeight(size);
37254 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37255 orientation: Roo.bootstrap.SplitBar.VERTICAL,
37256 getBox : function(){
37257 if(this.collapsed){
37258 return this.collapsedEl.getBox();
37260 var box = this.el.getBox();
37262 var sh = this.split.el.getHeight();
37269 updateBox : function(box){
37270 if(this.split && !this.collapsed){
37271 var sh = this.split.el.getHeight();
37274 this.split.el.setLeft(box.x);
37275 this.split.el.setTop(box.y-sh);
37276 this.split.el.setWidth(box.width);
37278 if(this.collapsed){
37279 this.updateBody(box.width, null);
37281 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37285 Roo.bootstrap.layout.East = function(config){
37286 config.region = "east";
37287 config.cursor = "e-resize";
37288 Roo.bootstrap.layout.Split.call(this, config);
37290 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37291 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37292 this.split.el.addClass("roo-layout-split-h");
37294 var size = config.initialSize || config.width;
37295 if(typeof size != "undefined"){
37296 this.el.setWidth(size);
37299 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37300 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37301 getBox : function(){
37302 if(this.collapsed){
37303 return this.collapsedEl.getBox();
37305 var box = this.el.getBox();
37307 var sw = this.split.el.getWidth();
37314 updateBox : function(box){
37315 if(this.split && !this.collapsed){
37316 var sw = this.split.el.getWidth();
37318 this.split.el.setLeft(box.x);
37319 this.split.el.setTop(box.y);
37320 this.split.el.setHeight(box.height);
37323 if(this.collapsed){
37324 this.updateBody(null, box.height);
37326 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37330 Roo.bootstrap.layout.West = function(config){
37331 config.region = "west";
37332 config.cursor = "w-resize";
37334 Roo.bootstrap.layout.Split.call(this, config);
37336 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37337 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37338 this.split.el.addClass("roo-layout-split-h");
37342 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37343 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37345 onRender: function(ctr, pos)
37347 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37348 var size = this.config.initialSize || this.config.width;
37349 if(typeof size != "undefined"){
37350 this.el.setWidth(size);
37354 getBox : function(){
37355 if(this.collapsed){
37356 return this.collapsedEl.getBox();
37358 var box = this.el.getBox();
37360 box.width += this.split.el.getWidth();
37365 updateBox : function(box){
37366 if(this.split && !this.collapsed){
37367 var sw = this.split.el.getWidth();
37369 this.split.el.setLeft(box.x+box.width);
37370 this.split.el.setTop(box.y);
37371 this.split.el.setHeight(box.height);
37373 if(this.collapsed){
37374 this.updateBody(null, box.height);
37376 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37378 });Roo.namespace("Roo.bootstrap.panel");/*
37380 * Ext JS Library 1.1.1
37381 * Copyright(c) 2006-2007, Ext JS, LLC.
37383 * Originally Released Under LGPL - original licence link has changed is not relivant.
37386 * <script type="text/javascript">
37389 * @class Roo.ContentPanel
37390 * @extends Roo.util.Observable
37391 * A basic ContentPanel element.
37392 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
37393 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
37394 * @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
37395 * @cfg {Boolean} closable True if the panel can be closed/removed
37396 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
37397 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37398 * @cfg {Toolbar} toolbar A toolbar for this panel
37399 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
37400 * @cfg {String} title The title for this panel
37401 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37402 * @cfg {String} url Calls {@link #setUrl} with this value
37403 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37404 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
37405 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
37406 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
37407 * @cfg {Boolean} badges render the badges
37410 * Create a new ContentPanel.
37411 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37412 * @param {String/Object} config A string to set only the title or a config object
37413 * @param {String} content (optional) Set the HTML content for this panel
37414 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37416 Roo.bootstrap.panel.Content = function( config){
37418 this.tpl = config.tpl || false;
37420 var el = config.el;
37421 var content = config.content;
37423 if(config.autoCreate){ // xtype is available if this is called from factory
37426 this.el = Roo.get(el);
37427 if(!this.el && config && config.autoCreate){
37428 if(typeof config.autoCreate == "object"){
37429 if(!config.autoCreate.id){
37430 config.autoCreate.id = config.id||el;
37432 this.el = Roo.DomHelper.append(document.body,
37433 config.autoCreate, true);
37435 var elcfg = { tag: "div",
37436 cls: "roo-layout-inactive-content",
37440 elcfg.html = config.html;
37444 this.el = Roo.DomHelper.append(document.body, elcfg , true);
37447 this.closable = false;
37448 this.loaded = false;
37449 this.active = false;
37452 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37454 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37456 this.wrapEl = this.el; //this.el.wrap();
37458 if (config.toolbar.items) {
37459 ti = config.toolbar.items ;
37460 delete config.toolbar.items ;
37464 this.toolbar.render(this.wrapEl, 'before');
37465 for(var i =0;i < ti.length;i++) {
37466 // Roo.log(['add child', items[i]]);
37467 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37469 this.toolbar.items = nitems;
37470 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37471 delete config.toolbar;
37475 // xtype created footer. - not sure if will work as we normally have to render first..
37476 if (this.footer && !this.footer.el && this.footer.xtype) {
37477 if (!this.wrapEl) {
37478 this.wrapEl = this.el.wrap();
37481 this.footer.container = this.wrapEl.createChild();
37483 this.footer = Roo.factory(this.footer, Roo);
37488 if(typeof config == "string"){
37489 this.title = config;
37491 Roo.apply(this, config);
37495 this.resizeEl = Roo.get(this.resizeEl, true);
37497 this.resizeEl = this.el;
37499 // handle view.xtype
37507 * Fires when this panel is activated.
37508 * @param {Roo.ContentPanel} this
37512 * @event deactivate
37513 * Fires when this panel is activated.
37514 * @param {Roo.ContentPanel} this
37516 "deactivate" : true,
37520 * Fires when this panel is resized if fitToFrame is true.
37521 * @param {Roo.ContentPanel} this
37522 * @param {Number} width The width after any component adjustments
37523 * @param {Number} height The height after any component adjustments
37529 * Fires when this tab is created
37530 * @param {Roo.ContentPanel} this
37541 if(this.autoScroll){
37542 this.resizeEl.setStyle("overflow", "auto");
37544 // fix randome scrolling
37545 //this.el.on('scroll', function() {
37546 // Roo.log('fix random scolling');
37547 // this.scrollTo('top',0);
37550 content = content || this.content;
37552 this.setContent(content);
37554 if(config && config.url){
37555 this.setUrl(this.url, this.params, this.loadOnce);
37560 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37562 if (this.view && typeof(this.view.xtype) != 'undefined') {
37563 this.view.el = this.el.appendChild(document.createElement("div"));
37564 this.view = Roo.factory(this.view);
37565 this.view.render && this.view.render(false, '');
37569 this.fireEvent('render', this);
37572 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37576 setRegion : function(region){
37577 this.region = region;
37578 this.setActiveClass(region && !this.background);
37582 setActiveClass: function(state)
37585 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37586 this.el.setStyle('position','relative');
37588 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37589 this.el.setStyle('position', 'absolute');
37594 * Returns the toolbar for this Panel if one was configured.
37595 * @return {Roo.Toolbar}
37597 getToolbar : function(){
37598 return this.toolbar;
37601 setActiveState : function(active)
37603 this.active = active;
37604 this.setActiveClass(active);
37606 if(this.fireEvent("deactivate", this) === false){
37611 this.fireEvent("activate", this);
37615 * Updates this panel's element
37616 * @param {String} content The new content
37617 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37619 setContent : function(content, loadScripts){
37620 this.el.update(content, loadScripts);
37623 ignoreResize : function(w, h){
37624 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37627 this.lastSize = {width: w, height: h};
37632 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37633 * @return {Roo.UpdateManager} The UpdateManager
37635 getUpdateManager : function(){
37636 return this.el.getUpdateManager();
37639 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37640 * @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:
37643 url: "your-url.php",
37644 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37645 callback: yourFunction,
37646 scope: yourObject, //(optional scope)
37649 text: "Loading...",
37654 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37655 * 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.
37656 * @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}
37657 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37658 * @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.
37659 * @return {Roo.ContentPanel} this
37662 var um = this.el.getUpdateManager();
37663 um.update.apply(um, arguments);
37669 * 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.
37670 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37671 * @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)
37672 * @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)
37673 * @return {Roo.UpdateManager} The UpdateManager
37675 setUrl : function(url, params, loadOnce){
37676 if(this.refreshDelegate){
37677 this.removeListener("activate", this.refreshDelegate);
37679 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37680 this.on("activate", this.refreshDelegate);
37681 return this.el.getUpdateManager();
37684 _handleRefresh : function(url, params, loadOnce){
37685 if(!loadOnce || !this.loaded){
37686 var updater = this.el.getUpdateManager();
37687 updater.update(url, params, this._setLoaded.createDelegate(this));
37691 _setLoaded : function(){
37692 this.loaded = true;
37696 * Returns this panel's id
37699 getId : function(){
37704 * Returns this panel's element - used by regiosn to add.
37705 * @return {Roo.Element}
37707 getEl : function(){
37708 return this.wrapEl || this.el;
37713 adjustForComponents : function(width, height)
37715 //Roo.log('adjustForComponents ');
37716 if(this.resizeEl != this.el){
37717 width -= this.el.getFrameWidth('lr');
37718 height -= this.el.getFrameWidth('tb');
37721 var te = this.toolbar.getEl();
37722 te.setWidth(width);
37723 height -= te.getHeight();
37726 var te = this.footer.getEl();
37727 te.setWidth(width);
37728 height -= te.getHeight();
37732 if(this.adjustments){
37733 width += this.adjustments[0];
37734 height += this.adjustments[1];
37736 return {"width": width, "height": height};
37739 setSize : function(width, height){
37740 if(this.fitToFrame && !this.ignoreResize(width, height)){
37741 if(this.fitContainer && this.resizeEl != this.el){
37742 this.el.setSize(width, height);
37744 var size = this.adjustForComponents(width, height);
37745 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37746 this.fireEvent('resize', this, size.width, size.height);
37751 * Returns this panel's title
37754 getTitle : function(){
37756 if (typeof(this.title) != 'object') {
37761 for (var k in this.title) {
37762 if (!this.title.hasOwnProperty(k)) {
37766 if (k.indexOf('-') >= 0) {
37767 var s = k.split('-');
37768 for (var i = 0; i<s.length; i++) {
37769 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37772 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37779 * Set this panel's title
37780 * @param {String} title
37782 setTitle : function(title){
37783 this.title = title;
37785 this.region.updatePanelTitle(this, title);
37790 * Returns true is this panel was configured to be closable
37791 * @return {Boolean}
37793 isClosable : function(){
37794 return this.closable;
37797 beforeSlide : function(){
37799 this.resizeEl.clip();
37802 afterSlide : function(){
37804 this.resizeEl.unclip();
37808 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37809 * Will fail silently if the {@link #setUrl} method has not been called.
37810 * This does not activate the panel, just updates its content.
37812 refresh : function(){
37813 if(this.refreshDelegate){
37814 this.loaded = false;
37815 this.refreshDelegate();
37820 * Destroys this panel
37822 destroy : function(){
37823 this.el.removeAllListeners();
37824 var tempEl = document.createElement("span");
37825 tempEl.appendChild(this.el.dom);
37826 tempEl.innerHTML = "";
37832 * form - if the content panel contains a form - this is a reference to it.
37833 * @type {Roo.form.Form}
37837 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37838 * This contains a reference to it.
37844 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37854 * @param {Object} cfg Xtype definition of item to add.
37858 getChildContainer: function () {
37859 return this.getEl();
37864 var ret = new Roo.factory(cfg);
37869 if (cfg.xtype.match(/^Form$/)) {
37872 //if (this.footer) {
37873 // el = this.footer.container.insertSibling(false, 'before');
37875 el = this.el.createChild();
37878 this.form = new Roo.form.Form(cfg);
37881 if ( this.form.allItems.length) {
37882 this.form.render(el.dom);
37886 // should only have one of theses..
37887 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37888 // views.. should not be just added - used named prop 'view''
37890 cfg.el = this.el.appendChild(document.createElement("div"));
37893 var ret = new Roo.factory(cfg);
37895 ret.render && ret.render(false, ''); // render blank..
37905 * @class Roo.bootstrap.panel.Grid
37906 * @extends Roo.bootstrap.panel.Content
37908 * Create a new GridPanel.
37909 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37910 * @param {Object} config A the config object
37916 Roo.bootstrap.panel.Grid = function(config)
37920 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37921 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37923 config.el = this.wrapper;
37924 //this.el = this.wrapper;
37926 if (config.container) {
37927 // ctor'ed from a Border/panel.grid
37930 this.wrapper.setStyle("overflow", "hidden");
37931 this.wrapper.addClass('roo-grid-container');
37936 if(config.toolbar){
37937 var tool_el = this.wrapper.createChild();
37938 this.toolbar = Roo.factory(config.toolbar);
37940 if (config.toolbar.items) {
37941 ti = config.toolbar.items ;
37942 delete config.toolbar.items ;
37946 this.toolbar.render(tool_el);
37947 for(var i =0;i < ti.length;i++) {
37948 // Roo.log(['add child', items[i]]);
37949 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37951 this.toolbar.items = nitems;
37953 delete config.toolbar;
37956 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37957 config.grid.scrollBody = true;;
37958 config.grid.monitorWindowResize = false; // turn off autosizing
37959 config.grid.autoHeight = false;
37960 config.grid.autoWidth = false;
37962 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37964 if (config.background) {
37965 // render grid on panel activation (if panel background)
37966 this.on('activate', function(gp) {
37967 if (!gp.grid.rendered) {
37968 gp.grid.render(this.wrapper);
37969 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37974 this.grid.render(this.wrapper);
37975 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37978 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37979 // ??? needed ??? config.el = this.wrapper;
37984 // xtype created footer. - not sure if will work as we normally have to render first..
37985 if (this.footer && !this.footer.el && this.footer.xtype) {
37987 var ctr = this.grid.getView().getFooterPanel(true);
37988 this.footer.dataSource = this.grid.dataSource;
37989 this.footer = Roo.factory(this.footer, Roo);
37990 this.footer.render(ctr);
38000 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
38001 getId : function(){
38002 return this.grid.id;
38006 * Returns the grid for this panel
38007 * @return {Roo.bootstrap.Table}
38009 getGrid : function(){
38013 setSize : function(width, height){
38014 if(!this.ignoreResize(width, height)){
38015 var grid = this.grid;
38016 var size = this.adjustForComponents(width, height);
38017 var gridel = grid.getGridEl();
38018 gridel.setSize(size.width, size.height);
38020 var thd = grid.getGridEl().select('thead',true).first();
38021 var tbd = grid.getGridEl().select('tbody', true).first();
38023 tbd.setSize(width, height - thd.getHeight());
38032 beforeSlide : function(){
38033 this.grid.getView().scroller.clip();
38036 afterSlide : function(){
38037 this.grid.getView().scroller.unclip();
38040 destroy : function(){
38041 this.grid.destroy();
38043 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
38048 * @class Roo.bootstrap.panel.Nest
38049 * @extends Roo.bootstrap.panel.Content
38051 * Create a new Panel, that can contain a layout.Border.
38054 * @param {Roo.BorderLayout} layout The layout for this panel
38055 * @param {String/Object} config A string to set only the title or a config object
38057 Roo.bootstrap.panel.Nest = function(config)
38059 // construct with only one argument..
38060 /* FIXME - implement nicer consturctors
38061 if (layout.layout) {
38063 layout = config.layout;
38064 delete config.layout;
38066 if (layout.xtype && !layout.getEl) {
38067 // then layout needs constructing..
38068 layout = Roo.factory(layout, Roo);
38072 config.el = config.layout.getEl();
38074 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38076 config.layout.monitorWindowResize = false; // turn off autosizing
38077 this.layout = config.layout;
38078 this.layout.getEl().addClass("roo-layout-nested-layout");
38079 this.layout.parent = this;
38086 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38088 setSize : function(width, height){
38089 if(!this.ignoreResize(width, height)){
38090 var size = this.adjustForComponents(width, height);
38091 var el = this.layout.getEl();
38092 if (size.height < 1) {
38093 el.setWidth(size.width);
38095 el.setSize(size.width, size.height);
38097 var touch = el.dom.offsetWidth;
38098 this.layout.layout();
38099 // ie requires a double layout on the first pass
38100 if(Roo.isIE && !this.initialized){
38101 this.initialized = true;
38102 this.layout.layout();
38107 // activate all subpanels if not currently active..
38109 setActiveState : function(active){
38110 this.active = active;
38111 this.setActiveClass(active);
38114 this.fireEvent("deactivate", this);
38118 this.fireEvent("activate", this);
38119 // not sure if this should happen before or after..
38120 if (!this.layout) {
38121 return; // should not happen..
38124 for (var r in this.layout.regions) {
38125 reg = this.layout.getRegion(r);
38126 if (reg.getActivePanel()) {
38127 //reg.showPanel(reg.getActivePanel()); // force it to activate..
38128 reg.setActivePanel(reg.getActivePanel());
38131 if (!reg.panels.length) {
38134 reg.showPanel(reg.getPanel(0));
38143 * Returns the nested BorderLayout for this panel
38144 * @return {Roo.BorderLayout}
38146 getLayout : function(){
38147 return this.layout;
38151 * Adds a xtype elements to the layout of the nested panel
38155 xtype : 'ContentPanel',
38162 xtype : 'NestedLayoutPanel',
38168 items : [ ... list of content panels or nested layout panels.. ]
38172 * @param {Object} cfg Xtype definition of item to add.
38174 addxtype : function(cfg) {
38175 return this.layout.addxtype(cfg);
38180 * Ext JS Library 1.1.1
38181 * Copyright(c) 2006-2007, Ext JS, LLC.
38183 * Originally Released Under LGPL - original licence link has changed is not relivant.
38186 * <script type="text/javascript">
38189 * @class Roo.TabPanel
38190 * @extends Roo.util.Observable
38191 * A lightweight tab container.
38195 // basic tabs 1, built from existing content
38196 var tabs = new Roo.TabPanel("tabs1");
38197 tabs.addTab("script", "View Script");
38198 tabs.addTab("markup", "View Markup");
38199 tabs.activate("script");
38201 // more advanced tabs, built from javascript
38202 var jtabs = new Roo.TabPanel("jtabs");
38203 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38205 // set up the UpdateManager
38206 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38207 var updater = tab2.getUpdateManager();
38208 updater.setDefaultUrl("ajax1.htm");
38209 tab2.on('activate', updater.refresh, updater, true);
38211 // Use setUrl for Ajax loading
38212 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38213 tab3.setUrl("ajax2.htm", null, true);
38216 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38219 jtabs.activate("jtabs-1");
38222 * Create a new TabPanel.
38223 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38224 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38226 Roo.bootstrap.panel.Tabs = function(config){
38228 * The container element for this TabPanel.
38229 * @type Roo.Element
38231 this.el = Roo.get(config.el);
38234 if(typeof config == "boolean"){
38235 this.tabPosition = config ? "bottom" : "top";
38237 Roo.apply(this, config);
38241 if(this.tabPosition == "bottom"){
38242 // if tabs are at the bottom = create the body first.
38243 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38244 this.el.addClass("roo-tabs-bottom");
38246 // next create the tabs holders
38248 if (this.tabPosition == "west"){
38250 var reg = this.region; // fake it..
38252 if (!reg.mgr.parent) {
38255 reg = reg.mgr.parent.region;
38257 Roo.log("got nest?");
38259 if (reg.mgr.getRegion('west')) {
38260 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38261 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38262 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38263 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38264 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38272 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38273 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38274 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38275 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38280 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38283 // finally - if tabs are at the top, then create the body last..
38284 if(this.tabPosition != "bottom"){
38285 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38286 * @type Roo.Element
38288 this.bodyEl = Roo.get(this.createBody(this.el.dom));
38289 this.el.addClass("roo-tabs-top");
38293 this.bodyEl.setStyle("position", "relative");
38295 this.active = null;
38296 this.activateDelegate = this.activate.createDelegate(this);
38301 * Fires when the active tab changes
38302 * @param {Roo.TabPanel} this
38303 * @param {Roo.TabPanelItem} activePanel The new active tab
38307 * @event beforetabchange
38308 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38309 * @param {Roo.TabPanel} this
38310 * @param {Object} e Set cancel to true on this object to cancel the tab change
38311 * @param {Roo.TabPanelItem} tab The tab being changed to
38313 "beforetabchange" : true
38316 Roo.EventManager.onWindowResize(this.onResize, this);
38317 this.cpad = this.el.getPadding("lr");
38318 this.hiddenCount = 0;
38321 // toolbar on the tabbar support...
38322 if (this.toolbar) {
38323 alert("no toolbar support yet");
38324 this.toolbar = false;
38326 var tcfg = this.toolbar;
38327 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
38328 this.toolbar = new Roo.Toolbar(tcfg);
38329 if (Roo.isSafari) {
38330 var tbl = tcfg.container.child('table', true);
38331 tbl.setAttribute('width', '100%');
38339 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38342 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38344 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38346 tabPosition : "top",
38348 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38350 currentTabWidth : 0,
38352 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38356 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38360 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38362 preferredTabWidth : 175,
38364 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38366 resizeTabs : false,
38368 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38370 monitorResize : true,
38372 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
38374 toolbar : false, // set by caller..
38376 region : false, /// set by caller
38378 disableTooltips : true, // not used yet...
38381 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38382 * @param {String} id The id of the div to use <b>or create</b>
38383 * @param {String} text The text for the tab
38384 * @param {String} content (optional) Content to put in the TabPanelItem body
38385 * @param {Boolean} closable (optional) True to create a close icon on the tab
38386 * @return {Roo.TabPanelItem} The created TabPanelItem
38388 addTab : function(id, text, content, closable, tpl)
38390 var item = new Roo.bootstrap.panel.TabItem({
38394 closable : closable,
38397 this.addTabItem(item);
38399 item.setContent(content);
38405 * Returns the {@link Roo.TabPanelItem} with the specified id/index
38406 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38407 * @return {Roo.TabPanelItem}
38409 getTab : function(id){
38410 return this.items[id];
38414 * Hides the {@link Roo.TabPanelItem} with the specified id/index
38415 * @param {String/Number} id The id or index of the TabPanelItem to hide.
38417 hideTab : function(id){
38418 var t = this.items[id];
38421 this.hiddenCount++;
38422 this.autoSizeTabs();
38427 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38428 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38430 unhideTab : function(id){
38431 var t = this.items[id];
38433 t.setHidden(false);
38434 this.hiddenCount--;
38435 this.autoSizeTabs();
38440 * Adds an existing {@link Roo.TabPanelItem}.
38441 * @param {Roo.TabPanelItem} item The TabPanelItem to add
38443 addTabItem : function(item)
38445 this.items[item.id] = item;
38446 this.items.push(item);
38447 this.autoSizeTabs();
38448 // if(this.resizeTabs){
38449 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38450 // this.autoSizeTabs();
38452 // item.autoSize();
38457 * Removes a {@link Roo.TabPanelItem}.
38458 * @param {String/Number} id The id or index of the TabPanelItem to remove.
38460 removeTab : function(id){
38461 var items = this.items;
38462 var tab = items[id];
38463 if(!tab) { return; }
38464 var index = items.indexOf(tab);
38465 if(this.active == tab && items.length > 1){
38466 var newTab = this.getNextAvailable(index);
38471 this.stripEl.dom.removeChild(tab.pnode.dom);
38472 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38473 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38475 items.splice(index, 1);
38476 delete this.items[tab.id];
38477 tab.fireEvent("close", tab);
38478 tab.purgeListeners();
38479 this.autoSizeTabs();
38482 getNextAvailable : function(start){
38483 var items = this.items;
38485 // look for a next tab that will slide over to
38486 // replace the one being removed
38487 while(index < items.length){
38488 var item = items[++index];
38489 if(item && !item.isHidden()){
38493 // if one isn't found select the previous tab (on the left)
38496 var item = items[--index];
38497 if(item && !item.isHidden()){
38505 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38506 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38508 disableTab : function(id){
38509 var tab = this.items[id];
38510 if(tab && this.active != tab){
38516 * Enables a {@link Roo.TabPanelItem} that is disabled.
38517 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38519 enableTab : function(id){
38520 var tab = this.items[id];
38525 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38526 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38527 * @return {Roo.TabPanelItem} The TabPanelItem.
38529 activate : function(id)
38531 //Roo.log('activite:' + id);
38533 var tab = this.items[id];
38537 if(tab == this.active || tab.disabled){
38541 this.fireEvent("beforetabchange", this, e, tab);
38542 if(e.cancel !== true && !tab.disabled){
38544 this.active.hide();
38546 this.active = this.items[id];
38547 this.active.show();
38548 this.fireEvent("tabchange", this, this.active);
38554 * Gets the active {@link Roo.TabPanelItem}.
38555 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38557 getActiveTab : function(){
38558 return this.active;
38562 * Updates the tab body element to fit the height of the container element
38563 * for overflow scrolling
38564 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38566 syncHeight : function(targetHeight){
38567 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38568 var bm = this.bodyEl.getMargins();
38569 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38570 this.bodyEl.setHeight(newHeight);
38574 onResize : function(){
38575 if(this.monitorResize){
38576 this.autoSizeTabs();
38581 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38583 beginUpdate : function(){
38584 this.updating = true;
38588 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38590 endUpdate : function(){
38591 this.updating = false;
38592 this.autoSizeTabs();
38596 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38598 autoSizeTabs : function()
38600 var count = this.items.length;
38601 var vcount = count - this.hiddenCount;
38604 this.stripEl.hide();
38606 this.stripEl.show();
38609 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38614 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38615 var availWidth = Math.floor(w / vcount);
38616 var b = this.stripBody;
38617 if(b.getWidth() > w){
38618 var tabs = this.items;
38619 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38620 if(availWidth < this.minTabWidth){
38621 /*if(!this.sleft){ // incomplete scrolling code
38622 this.createScrollButtons();
38625 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38628 if(this.currentTabWidth < this.preferredTabWidth){
38629 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38635 * Returns the number of tabs in this TabPanel.
38638 getCount : function(){
38639 return this.items.length;
38643 * Resizes all the tabs to the passed width
38644 * @param {Number} The new width
38646 setTabWidth : function(width){
38647 this.currentTabWidth = width;
38648 for(var i = 0, len = this.items.length; i < len; i++) {
38649 if(!this.items[i].isHidden()) {
38650 this.items[i].setWidth(width);
38656 * Destroys this TabPanel
38657 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38659 destroy : function(removeEl){
38660 Roo.EventManager.removeResizeListener(this.onResize, this);
38661 for(var i = 0, len = this.items.length; i < len; i++){
38662 this.items[i].purgeListeners();
38664 if(removeEl === true){
38665 this.el.update("");
38670 createStrip : function(container)
38672 var strip = document.createElement("nav");
38673 strip.className = Roo.bootstrap.version == 4 ?
38674 "navbar-light bg-light" :
38675 "navbar navbar-default"; //"x-tabs-wrap";
38676 container.appendChild(strip);
38680 createStripList : function(strip)
38682 // div wrapper for retard IE
38683 // returns the "tr" element.
38684 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38685 //'<div class="x-tabs-strip-wrap">'+
38686 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38687 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38688 return strip.firstChild; //.firstChild.firstChild.firstChild;
38690 createBody : function(container)
38692 var body = document.createElement("div");
38693 Roo.id(body, "tab-body");
38694 //Roo.fly(body).addClass("x-tabs-body");
38695 Roo.fly(body).addClass("tab-content");
38696 container.appendChild(body);
38699 createItemBody :function(bodyEl, id){
38700 var body = Roo.getDom(id);
38702 body = document.createElement("div");
38705 //Roo.fly(body).addClass("x-tabs-item-body");
38706 Roo.fly(body).addClass("tab-pane");
38707 bodyEl.insertBefore(body, bodyEl.firstChild);
38711 createStripElements : function(stripEl, text, closable, tpl)
38713 var td = document.createElement("li"); // was td..
38714 td.className = 'nav-item';
38716 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38719 stripEl.appendChild(td);
38721 td.className = "x-tabs-closable";
38722 if(!this.closeTpl){
38723 this.closeTpl = new Roo.Template(
38724 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38725 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38726 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38729 var el = this.closeTpl.overwrite(td, {"text": text});
38730 var close = el.getElementsByTagName("div")[0];
38731 var inner = el.getElementsByTagName("em")[0];
38732 return {"el": el, "close": close, "inner": inner};
38735 // not sure what this is..
38736 // if(!this.tabTpl){
38737 //this.tabTpl = new Roo.Template(
38738 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38739 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38741 // this.tabTpl = new Roo.Template(
38742 // '<a href="#">' +
38743 // '<span unselectable="on"' +
38744 // (this.disableTooltips ? '' : ' title="{text}"') +
38745 // ' >{text}</span></a>'
38751 var template = tpl || this.tabTpl || false;
38754 template = new Roo.Template(
38755 Roo.bootstrap.version == 4 ?
38757 '<a class="nav-link" href="#" unselectable="on"' +
38758 (this.disableTooltips ? '' : ' title="{text}"') +
38761 '<a class="nav-link" href="#">' +
38762 '<span unselectable="on"' +
38763 (this.disableTooltips ? '' : ' title="{text}"') +
38764 ' >{text}</span></a>'
38769 switch (typeof(template)) {
38773 template = new Roo.Template(template);
38779 var el = template.overwrite(td, {"text": text});
38781 var inner = el.getElementsByTagName("span")[0];
38783 return {"el": el, "inner": inner};
38791 * @class Roo.TabPanelItem
38792 * @extends Roo.util.Observable
38793 * Represents an individual item (tab plus body) in a TabPanel.
38794 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38795 * @param {String} id The id of this TabPanelItem
38796 * @param {String} text The text for the tab of this TabPanelItem
38797 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38799 Roo.bootstrap.panel.TabItem = function(config){
38801 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38802 * @type Roo.TabPanel
38804 this.tabPanel = config.panel;
38806 * The id for this TabPanelItem
38809 this.id = config.id;
38811 this.disabled = false;
38813 this.text = config.text;
38815 this.loaded = false;
38816 this.closable = config.closable;
38819 * The body element for this TabPanelItem.
38820 * @type Roo.Element
38822 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38823 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38824 this.bodyEl.setStyle("display", "block");
38825 this.bodyEl.setStyle("zoom", "1");
38826 //this.hideAction();
38828 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38830 this.el = Roo.get(els.el);
38831 this.inner = Roo.get(els.inner, true);
38832 this.textEl = Roo.bootstrap.version == 4 ?
38833 this.el : Roo.get(this.el.dom.firstChild, true);
38835 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38836 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38839 // this.el.on("mousedown", this.onTabMouseDown, this);
38840 this.el.on("click", this.onTabClick, this);
38842 if(config.closable){
38843 var c = Roo.get(els.close, true);
38844 c.dom.title = this.closeText;
38845 c.addClassOnOver("close-over");
38846 c.on("click", this.closeClick, this);
38852 * Fires when this tab becomes the active tab.
38853 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38854 * @param {Roo.TabPanelItem} this
38858 * @event beforeclose
38859 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38860 * @param {Roo.TabPanelItem} this
38861 * @param {Object} e Set cancel to true on this object to cancel the close.
38863 "beforeclose": true,
38866 * Fires when this tab is closed.
38867 * @param {Roo.TabPanelItem} this
38871 * @event deactivate
38872 * Fires when this tab is no longer the active tab.
38873 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38874 * @param {Roo.TabPanelItem} this
38876 "deactivate" : true
38878 this.hidden = false;
38880 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38883 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38885 purgeListeners : function(){
38886 Roo.util.Observable.prototype.purgeListeners.call(this);
38887 this.el.removeAllListeners();
38890 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38893 this.status_node.addClass("active");
38896 this.tabPanel.stripWrap.repaint();
38898 this.fireEvent("activate", this.tabPanel, this);
38902 * Returns true if this tab is the active tab.
38903 * @return {Boolean}
38905 isActive : function(){
38906 return this.tabPanel.getActiveTab() == this;
38910 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38913 this.status_node.removeClass("active");
38915 this.fireEvent("deactivate", this.tabPanel, this);
38918 hideAction : function(){
38919 this.bodyEl.hide();
38920 this.bodyEl.setStyle("position", "absolute");
38921 this.bodyEl.setLeft("-20000px");
38922 this.bodyEl.setTop("-20000px");
38925 showAction : function(){
38926 this.bodyEl.setStyle("position", "relative");
38927 this.bodyEl.setTop("");
38928 this.bodyEl.setLeft("");
38929 this.bodyEl.show();
38933 * Set the tooltip for the tab.
38934 * @param {String} tooltip The tab's tooltip
38936 setTooltip : function(text){
38937 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38938 this.textEl.dom.qtip = text;
38939 this.textEl.dom.removeAttribute('title');
38941 this.textEl.dom.title = text;
38945 onTabClick : function(e){
38946 e.preventDefault();
38947 this.tabPanel.activate(this.id);
38950 onTabMouseDown : function(e){
38951 e.preventDefault();
38952 this.tabPanel.activate(this.id);
38955 getWidth : function(){
38956 return this.inner.getWidth();
38959 setWidth : function(width){
38960 var iwidth = width - this.linode.getPadding("lr");
38961 this.inner.setWidth(iwidth);
38962 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38963 this.linode.setWidth(width);
38967 * Show or hide the tab
38968 * @param {Boolean} hidden True to hide or false to show.
38970 setHidden : function(hidden){
38971 this.hidden = hidden;
38972 this.linode.setStyle("display", hidden ? "none" : "");
38976 * Returns true if this tab is "hidden"
38977 * @return {Boolean}
38979 isHidden : function(){
38980 return this.hidden;
38984 * Returns the text for this tab
38987 getText : function(){
38991 autoSize : function(){
38992 //this.el.beginMeasure();
38993 this.textEl.setWidth(1);
38995 * #2804 [new] Tabs in Roojs
38996 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38998 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38999 //this.el.endMeasure();
39003 * Sets the text for the tab (Note: this also sets the tooltip text)
39004 * @param {String} text The tab's text and tooltip
39006 setText : function(text){
39008 this.textEl.update(text);
39009 this.setTooltip(text);
39010 //if(!this.tabPanel.resizeTabs){
39011 // this.autoSize();
39015 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39017 activate : function(){
39018 this.tabPanel.activate(this.id);
39022 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39024 disable : function(){
39025 if(this.tabPanel.active != this){
39026 this.disabled = true;
39027 this.status_node.addClass("disabled");
39032 * Enables this TabPanelItem if it was previously disabled.
39034 enable : function(){
39035 this.disabled = false;
39036 this.status_node.removeClass("disabled");
39040 * Sets the content for this TabPanelItem.
39041 * @param {String} content The content
39042 * @param {Boolean} loadScripts true to look for and load scripts
39044 setContent : function(content, loadScripts){
39045 this.bodyEl.update(content, loadScripts);
39049 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39050 * @return {Roo.UpdateManager} The UpdateManager
39052 getUpdateManager : function(){
39053 return this.bodyEl.getUpdateManager();
39057 * Set a URL to be used to load the content for this TabPanelItem.
39058 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39059 * @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)
39060 * @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)
39061 * @return {Roo.UpdateManager} The UpdateManager
39063 setUrl : function(url, params, loadOnce){
39064 if(this.refreshDelegate){
39065 this.un('activate', this.refreshDelegate);
39067 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39068 this.on("activate", this.refreshDelegate);
39069 return this.bodyEl.getUpdateManager();
39073 _handleRefresh : function(url, params, loadOnce){
39074 if(!loadOnce || !this.loaded){
39075 var updater = this.bodyEl.getUpdateManager();
39076 updater.update(url, params, this._setLoaded.createDelegate(this));
39081 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
39082 * Will fail silently if the setUrl method has not been called.
39083 * This does not activate the panel, just updates its content.
39085 refresh : function(){
39086 if(this.refreshDelegate){
39087 this.loaded = false;
39088 this.refreshDelegate();
39093 _setLoaded : function(){
39094 this.loaded = true;
39098 closeClick : function(e){
39101 this.fireEvent("beforeclose", this, o);
39102 if(o.cancel !== true){
39103 this.tabPanel.removeTab(this.id);
39107 * The text displayed in the tooltip for the close icon.
39110 closeText : "Close this tab"
39113 * This script refer to:
39114 * Title: International Telephone Input
39115 * Author: Jack O'Connor
39116 * Code version: v12.1.12
39117 * Availability: https://github.com/jackocnr/intl-tel-input.git
39120 Roo.bootstrap.PhoneInputData = function() {
39123 "Afghanistan (افغانستان)",
39128 "Albania (Shqipëri)",
39133 "Algeria (الجزائر)",
39158 "Antigua and Barbuda",
39168 "Armenia (Հայաստան)",
39184 "Austria (Österreich)",
39189 "Azerbaijan (Azərbaycan)",
39199 "Bahrain (البحرين)",
39204 "Bangladesh (বাংলাদেশ)",
39214 "Belarus (Беларусь)",
39219 "Belgium (België)",
39249 "Bosnia and Herzegovina (Босна и Херцеговина)",
39264 "British Indian Ocean Territory",
39269 "British Virgin Islands",
39279 "Bulgaria (България)",
39289 "Burundi (Uburundi)",
39294 "Cambodia (កម្ពុជា)",
39299 "Cameroon (Cameroun)",
39308 ["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"]
39311 "Cape Verde (Kabu Verdi)",
39316 "Caribbean Netherlands",
39327 "Central African Republic (République centrafricaine)",
39347 "Christmas Island",
39353 "Cocos (Keeling) Islands",
39364 "Comoros (جزر القمر)",
39369 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39374 "Congo (Republic) (Congo-Brazzaville)",
39394 "Croatia (Hrvatska)",
39415 "Czech Republic (Česká republika)",
39420 "Denmark (Danmark)",
39435 "Dominican Republic (República Dominicana)",
39439 ["809", "829", "849"]
39457 "Equatorial Guinea (Guinea Ecuatorial)",
39477 "Falkland Islands (Islas Malvinas)",
39482 "Faroe Islands (Føroyar)",
39503 "French Guiana (Guyane française)",
39508 "French Polynesia (Polynésie française)",
39523 "Georgia (საქართველო)",
39528 "Germany (Deutschland)",
39548 "Greenland (Kalaallit Nunaat)",
39585 "Guinea-Bissau (Guiné Bissau)",
39610 "Hungary (Magyarország)",
39615 "Iceland (Ísland)",
39635 "Iraq (العراق)",
39651 "Israel (ישראל)",
39678 "Jordan (الأردن)",
39683 "Kazakhstan (Казахстан)",
39704 "Kuwait (الكويت)",
39709 "Kyrgyzstan (Кыргызстан)",
39719 "Latvia (Latvija)",
39724 "Lebanon (لبنان)",
39739 "Libya (ليبيا)",
39749 "Lithuania (Lietuva)",
39764 "Macedonia (FYROM) (Македонија)",
39769 "Madagascar (Madagasikara)",
39799 "Marshall Islands",
39809 "Mauritania (موريتانيا)",
39814 "Mauritius (Moris)",
39835 "Moldova (Republica Moldova)",
39845 "Mongolia (Монгол)",
39850 "Montenegro (Crna Gora)",
39860 "Morocco (المغرب)",
39866 "Mozambique (Moçambique)",
39871 "Myanmar (Burma) (မြန်မာ)",
39876 "Namibia (Namibië)",
39891 "Netherlands (Nederland)",
39896 "New Caledonia (Nouvelle-Calédonie)",
39931 "North Korea (조선 민주주의 인민 공화국)",
39936 "Northern Mariana Islands",
39952 "Pakistan (پاکستان)",
39962 "Palestine (فلسطين)",
39972 "Papua New Guinea",
40014 "Réunion (La Réunion)",
40020 "Romania (România)",
40036 "Saint Barthélemy",
40047 "Saint Kitts and Nevis",
40057 "Saint Martin (Saint-Martin (partie française))",
40063 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40068 "Saint Vincent and the Grenadines",
40083 "São Tomé and Príncipe (São Tomé e Príncipe)",
40088 "Saudi Arabia (المملكة العربية السعودية)",
40093 "Senegal (Sénégal)",
40123 "Slovakia (Slovensko)",
40128 "Slovenia (Slovenija)",
40138 "Somalia (Soomaaliya)",
40148 "South Korea (대한민국)",
40153 "South Sudan (جنوب السودان)",
40163 "Sri Lanka (ශ්රී ලංකාව)",
40168 "Sudan (السودان)",
40178 "Svalbard and Jan Mayen",
40189 "Sweden (Sverige)",
40194 "Switzerland (Schweiz)",
40199 "Syria (سوريا)",
40244 "Trinidad and Tobago",
40249 "Tunisia (تونس)",
40254 "Turkey (Türkiye)",
40264 "Turks and Caicos Islands",
40274 "U.S. Virgin Islands",
40284 "Ukraine (Україна)",
40289 "United Arab Emirates (الإمارات العربية المتحدة)",
40311 "Uzbekistan (Oʻzbekiston)",
40321 "Vatican City (Città del Vaticano)",
40332 "Vietnam (Việt Nam)",
40337 "Wallis and Futuna (Wallis-et-Futuna)",
40342 "Western Sahara (الصحراء الغربية)",
40348 "Yemen (اليمن)",
40372 * This script refer to:
40373 * Title: International Telephone Input
40374 * Author: Jack O'Connor
40375 * Code version: v12.1.12
40376 * Availability: https://github.com/jackocnr/intl-tel-input.git
40380 * @class Roo.bootstrap.PhoneInput
40381 * @extends Roo.bootstrap.TriggerField
40382 * An input with International dial-code selection
40384 * @cfg {String} defaultDialCode default '+852'
40385 * @cfg {Array} preferedCountries default []
40388 * Create a new PhoneInput.
40389 * @param {Object} config Configuration options
40392 Roo.bootstrap.PhoneInput = function(config) {
40393 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40396 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40398 listWidth: undefined,
40400 selectedClass: 'active',
40402 invalidClass : "has-warning",
40404 validClass: 'has-success',
40406 allowed: '0123456789',
40411 * @cfg {String} defaultDialCode The default dial code when initializing the input
40413 defaultDialCode: '+852',
40416 * @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
40418 preferedCountries: false,
40420 getAutoCreate : function()
40422 var data = Roo.bootstrap.PhoneInputData();
40423 var align = this.labelAlign || this.parentLabelAlign();
40426 this.allCountries = [];
40427 this.dialCodeMapping = [];
40429 for (var i = 0; i < data.length; i++) {
40431 this.allCountries[i] = {
40435 priority: c[3] || 0,
40436 areaCodes: c[4] || null
40438 this.dialCodeMapping[c[2]] = {
40441 priority: c[3] || 0,
40442 areaCodes: c[4] || null
40454 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40455 maxlength: this.max_length,
40456 cls : 'form-control tel-input',
40457 autocomplete: 'new-password'
40460 var hiddenInput = {
40463 cls: 'hidden-tel-input'
40467 hiddenInput.name = this.name;
40470 if (this.disabled) {
40471 input.disabled = true;
40474 var flag_container = {
40491 cls: this.hasFeedback ? 'has-feedback' : '',
40497 cls: 'dial-code-holder',
40504 cls: 'roo-select2-container input-group',
40511 if (this.fieldLabel.length) {
40514 tooltip: 'This field is required'
40520 cls: 'control-label',
40526 html: this.fieldLabel
40529 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40535 if(this.indicatorpos == 'right') {
40536 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40543 if(align == 'left') {
40551 if(this.labelWidth > 12){
40552 label.style = "width: " + this.labelWidth + 'px';
40554 if(this.labelWidth < 13 && this.labelmd == 0){
40555 this.labelmd = this.labelWidth;
40557 if(this.labellg > 0){
40558 label.cls += ' col-lg-' + this.labellg;
40559 input.cls += ' col-lg-' + (12 - this.labellg);
40561 if(this.labelmd > 0){
40562 label.cls += ' col-md-' + this.labelmd;
40563 container.cls += ' col-md-' + (12 - this.labelmd);
40565 if(this.labelsm > 0){
40566 label.cls += ' col-sm-' + this.labelsm;
40567 container.cls += ' col-sm-' + (12 - this.labelsm);
40569 if(this.labelxs > 0){
40570 label.cls += ' col-xs-' + this.labelxs;
40571 container.cls += ' col-xs-' + (12 - this.labelxs);
40581 var settings = this;
40583 ['xs','sm','md','lg'].map(function(size){
40584 if (settings[size]) {
40585 cfg.cls += ' col-' + size + '-' + settings[size];
40589 this.store = new Roo.data.Store({
40590 proxy : new Roo.data.MemoryProxy({}),
40591 reader : new Roo.data.JsonReader({
40602 'name' : 'dialCode',
40606 'name' : 'priority',
40610 'name' : 'areaCodes',
40617 if(!this.preferedCountries) {
40618 this.preferedCountries = [
40625 var p = this.preferedCountries.reverse();
40628 for (var i = 0; i < p.length; i++) {
40629 for (var j = 0; j < this.allCountries.length; j++) {
40630 if(this.allCountries[j].iso2 == p[i]) {
40631 var t = this.allCountries[j];
40632 this.allCountries.splice(j,1);
40633 this.allCountries.unshift(t);
40639 this.store.proxy.data = {
40641 data: this.allCountries
40647 initEvents : function()
40650 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40652 this.indicator = this.indicatorEl();
40653 this.flag = this.flagEl();
40654 this.dialCodeHolder = this.dialCodeHolderEl();
40656 this.trigger = this.el.select('div.flag-box',true).first();
40657 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40662 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40663 _this.list.setWidth(lw);
40666 this.list.on('mouseover', this.onViewOver, this);
40667 this.list.on('mousemove', this.onViewMove, this);
40668 this.inputEl().on("keyup", this.onKeyUp, this);
40669 this.inputEl().on("keypress", this.onKeyPress, this);
40671 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40673 this.view = new Roo.View(this.list, this.tpl, {
40674 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40677 this.view.on('click', this.onViewClick, this);
40678 this.setValue(this.defaultDialCode);
40681 onTriggerClick : function(e)
40683 Roo.log('trigger click');
40688 if(this.isExpanded()){
40690 this.hasFocus = false;
40692 this.store.load({});
40693 this.hasFocus = true;
40698 isExpanded : function()
40700 return this.list.isVisible();
40703 collapse : function()
40705 if(!this.isExpanded()){
40709 Roo.get(document).un('mousedown', this.collapseIf, this);
40710 Roo.get(document).un('mousewheel', this.collapseIf, this);
40711 this.fireEvent('collapse', this);
40715 expand : function()
40719 if(this.isExpanded() || !this.hasFocus){
40723 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40724 this.list.setWidth(lw);
40727 this.restrictHeight();
40729 Roo.get(document).on('mousedown', this.collapseIf, this);
40730 Roo.get(document).on('mousewheel', this.collapseIf, this);
40732 this.fireEvent('expand', this);
40735 restrictHeight : function()
40737 this.list.alignTo(this.inputEl(), this.listAlign);
40738 this.list.alignTo(this.inputEl(), this.listAlign);
40741 onViewOver : function(e, t)
40743 if(this.inKeyMode){
40746 var item = this.view.findItemFromChild(t);
40749 var index = this.view.indexOf(item);
40750 this.select(index, false);
40755 onViewClick : function(view, doFocus, el, e)
40757 var index = this.view.getSelectedIndexes()[0];
40759 var r = this.store.getAt(index);
40762 this.onSelect(r, index);
40764 if(doFocus !== false && !this.blockFocus){
40765 this.inputEl().focus();
40769 onViewMove : function(e, t)
40771 this.inKeyMode = false;
40774 select : function(index, scrollIntoView)
40776 this.selectedIndex = index;
40777 this.view.select(index);
40778 if(scrollIntoView !== false){
40779 var el = this.view.getNode(index);
40781 this.list.scrollChildIntoView(el, false);
40786 createList : function()
40788 this.list = Roo.get(document.body).createChild({
40790 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40791 style: 'display:none'
40794 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40797 collapseIf : function(e)
40799 var in_combo = e.within(this.el);
40800 var in_list = e.within(this.list);
40801 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40803 if (in_combo || in_list || is_list) {
40809 onSelect : function(record, index)
40811 if(this.fireEvent('beforeselect', this, record, index) !== false){
40813 this.setFlagClass(record.data.iso2);
40814 this.setDialCode(record.data.dialCode);
40815 this.hasFocus = false;
40817 this.fireEvent('select', this, record, index);
40821 flagEl : function()
40823 var flag = this.el.select('div.flag',true).first();
40830 dialCodeHolderEl : function()
40832 var d = this.el.select('input.dial-code-holder',true).first();
40839 setDialCode : function(v)
40841 this.dialCodeHolder.dom.value = '+'+v;
40844 setFlagClass : function(n)
40846 this.flag.dom.className = 'flag '+n;
40849 getValue : function()
40851 var v = this.inputEl().getValue();
40852 if(this.dialCodeHolder) {
40853 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40858 setValue : function(v)
40860 var d = this.getDialCode(v);
40862 //invalid dial code
40863 if(v.length == 0 || !d || d.length == 0) {
40865 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40866 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40872 this.setFlagClass(this.dialCodeMapping[d].iso2);
40873 this.setDialCode(d);
40874 this.inputEl().dom.value = v.replace('+'+d,'');
40875 this.hiddenEl().dom.value = this.getValue();
40880 getDialCode : function(v)
40884 if (v.length == 0) {
40885 return this.dialCodeHolder.dom.value;
40889 if (v.charAt(0) != "+") {
40892 var numericChars = "";
40893 for (var i = 1; i < v.length; i++) {
40894 var c = v.charAt(i);
40897 if (this.dialCodeMapping[numericChars]) {
40898 dialCode = v.substr(1, i);
40900 if (numericChars.length == 4) {
40910 this.setValue(this.defaultDialCode);
40914 hiddenEl : function()
40916 return this.el.select('input.hidden-tel-input',true).first();
40919 // after setting val
40920 onKeyUp : function(e){
40921 this.setValue(this.getValue());
40924 onKeyPress : function(e){
40925 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40932 * @class Roo.bootstrap.MoneyField
40933 * @extends Roo.bootstrap.ComboBox
40934 * Bootstrap MoneyField class
40937 * Create a new MoneyField.
40938 * @param {Object} config Configuration options
40941 Roo.bootstrap.MoneyField = function(config) {
40943 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40947 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40950 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40952 allowDecimals : true,
40954 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40956 decimalSeparator : ".",
40958 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40960 decimalPrecision : 0,
40962 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40964 allowNegative : true,
40966 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40970 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40972 minValue : Number.NEGATIVE_INFINITY,
40974 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40976 maxValue : Number.MAX_VALUE,
40978 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40980 minText : "The minimum value for this field is {0}",
40982 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40984 maxText : "The maximum value for this field is {0}",
40986 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40987 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40989 nanText : "{0} is not a valid number",
40991 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40995 * @cfg {String} defaults currency of the MoneyField
40996 * value should be in lkey
40998 defaultCurrency : false,
41000 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41002 thousandsDelimiter : false,
41004 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41015 getAutoCreate : function()
41017 var align = this.labelAlign || this.parentLabelAlign();
41029 cls : 'form-control roo-money-amount-input',
41030 autocomplete: 'new-password'
41033 var hiddenInput = {
41037 cls: 'hidden-number-input'
41040 if(this.max_length) {
41041 input.maxlength = this.max_length;
41045 hiddenInput.name = this.name;
41048 if (this.disabled) {
41049 input.disabled = true;
41052 var clg = 12 - this.inputlg;
41053 var cmd = 12 - this.inputmd;
41054 var csm = 12 - this.inputsm;
41055 var cxs = 12 - this.inputxs;
41059 cls : 'row roo-money-field',
41063 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41067 cls: 'roo-select2-container input-group',
41071 cls : 'form-control roo-money-currency-input',
41072 autocomplete: 'new-password',
41074 name : this.currencyName
41078 cls : 'input-group-addon',
41092 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41096 cls: this.hasFeedback ? 'has-feedback' : '',
41107 if (this.fieldLabel.length) {
41110 tooltip: 'This field is required'
41116 cls: 'control-label',
41122 html: this.fieldLabel
41125 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41131 if(this.indicatorpos == 'right') {
41132 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41139 if(align == 'left') {
41147 if(this.labelWidth > 12){
41148 label.style = "width: " + this.labelWidth + 'px';
41150 if(this.labelWidth < 13 && this.labelmd == 0){
41151 this.labelmd = this.labelWidth;
41153 if(this.labellg > 0){
41154 label.cls += ' col-lg-' + this.labellg;
41155 input.cls += ' col-lg-' + (12 - this.labellg);
41157 if(this.labelmd > 0){
41158 label.cls += ' col-md-' + this.labelmd;
41159 container.cls += ' col-md-' + (12 - this.labelmd);
41161 if(this.labelsm > 0){
41162 label.cls += ' col-sm-' + this.labelsm;
41163 container.cls += ' col-sm-' + (12 - this.labelsm);
41165 if(this.labelxs > 0){
41166 label.cls += ' col-xs-' + this.labelxs;
41167 container.cls += ' col-xs-' + (12 - this.labelxs);
41178 var settings = this;
41180 ['xs','sm','md','lg'].map(function(size){
41181 if (settings[size]) {
41182 cfg.cls += ' col-' + size + '-' + settings[size];
41189 initEvents : function()
41191 this.indicator = this.indicatorEl();
41193 this.initCurrencyEvent();
41195 this.initNumberEvent();
41198 initCurrencyEvent : function()
41201 throw "can not find store for combo";
41204 this.store = Roo.factory(this.store, Roo.data);
41205 this.store.parent = this;
41209 this.triggerEl = this.el.select('.input-group-addon', true).first();
41211 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41216 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41217 _this.list.setWidth(lw);
41220 this.list.on('mouseover', this.onViewOver, this);
41221 this.list.on('mousemove', this.onViewMove, this);
41222 this.list.on('scroll', this.onViewScroll, this);
41225 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41228 this.view = new Roo.View(this.list, this.tpl, {
41229 singleSelect:true, store: this.store, selectedClass: this.selectedClass
41232 this.view.on('click', this.onViewClick, this);
41234 this.store.on('beforeload', this.onBeforeLoad, this);
41235 this.store.on('load', this.onLoad, this);
41236 this.store.on('loadexception', this.onLoadException, this);
41238 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41239 "up" : function(e){
41240 this.inKeyMode = true;
41244 "down" : function(e){
41245 if(!this.isExpanded()){
41246 this.onTriggerClick();
41248 this.inKeyMode = true;
41253 "enter" : function(e){
41256 if(this.fireEvent("specialkey", this, e)){
41257 this.onViewClick(false);
41263 "esc" : function(e){
41267 "tab" : function(e){
41270 if(this.fireEvent("specialkey", this, e)){
41271 this.onViewClick(false);
41279 doRelay : function(foo, bar, hname){
41280 if(hname == 'down' || this.scope.isExpanded()){
41281 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41289 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41293 initNumberEvent : function(e)
41295 this.inputEl().on("keydown" , this.fireKey, this);
41296 this.inputEl().on("focus", this.onFocus, this);
41297 this.inputEl().on("blur", this.onBlur, this);
41299 this.inputEl().relayEvent('keyup', this);
41301 if(this.indicator){
41302 this.indicator.addClass('invisible');
41305 this.originalValue = this.getValue();
41307 if(this.validationEvent == 'keyup'){
41308 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41309 this.inputEl().on('keyup', this.filterValidation, this);
41311 else if(this.validationEvent !== false){
41312 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41315 if(this.selectOnFocus){
41316 this.on("focus", this.preFocus, this);
41319 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41320 this.inputEl().on("keypress", this.filterKeys, this);
41322 this.inputEl().relayEvent('keypress', this);
41325 var allowed = "0123456789";
41327 if(this.allowDecimals){
41328 allowed += this.decimalSeparator;
41331 if(this.allowNegative){
41335 if(this.thousandsDelimiter) {
41339 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41341 var keyPress = function(e){
41343 var k = e.getKey();
41345 var c = e.getCharCode();
41348 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41349 allowed.indexOf(String.fromCharCode(c)) === -1
41355 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41359 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41364 this.inputEl().on("keypress", keyPress, this);
41368 onTriggerClick : function(e)
41375 this.loadNext = false;
41377 if(this.isExpanded()){
41382 this.hasFocus = true;
41384 if(this.triggerAction == 'all') {
41385 this.doQuery(this.allQuery, true);
41389 this.doQuery(this.getRawValue());
41392 getCurrency : function()
41394 var v = this.currencyEl().getValue();
41399 restrictHeight : function()
41401 this.list.alignTo(this.currencyEl(), this.listAlign);
41402 this.list.alignTo(this.currencyEl(), this.listAlign);
41405 onViewClick : function(view, doFocus, el, e)
41407 var index = this.view.getSelectedIndexes()[0];
41409 var r = this.store.getAt(index);
41412 this.onSelect(r, index);
41416 onSelect : function(record, index){
41418 if(this.fireEvent('beforeselect', this, record, index) !== false){
41420 this.setFromCurrencyData(index > -1 ? record.data : false);
41424 this.fireEvent('select', this, record, index);
41428 setFromCurrencyData : function(o)
41432 this.lastCurrency = o;
41434 if (this.currencyField) {
41435 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41437 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
41440 this.lastSelectionText = currency;
41442 //setting default currency
41443 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41444 this.setCurrency(this.defaultCurrency);
41448 this.setCurrency(currency);
41451 setFromData : function(o)
41455 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41457 this.setFromCurrencyData(c);
41462 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41464 Roo.log('no value set for '+ (this.name ? this.name : this.id));
41467 this.setValue(value);
41471 setCurrency : function(v)
41473 this.currencyValue = v;
41476 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41481 setValue : function(v)
41483 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41489 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41491 this.inputEl().dom.value = (v == '') ? '' :
41492 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41494 if(!this.allowZero && v === '0') {
41495 this.hiddenEl().dom.value = '';
41496 this.inputEl().dom.value = '';
41503 getRawValue : function()
41505 var v = this.inputEl().getValue();
41510 getValue : function()
41512 return this.fixPrecision(this.parseValue(this.getRawValue()));
41515 parseValue : function(value)
41517 if(this.thousandsDelimiter) {
41519 r = new RegExp(",", "g");
41520 value = value.replace(r, "");
41523 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41524 return isNaN(value) ? '' : value;
41528 fixPrecision : function(value)
41530 if(this.thousandsDelimiter) {
41532 r = new RegExp(",", "g");
41533 value = value.replace(r, "");
41536 var nan = isNaN(value);
41538 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41539 return nan ? '' : value;
41541 return parseFloat(value).toFixed(this.decimalPrecision);
41544 decimalPrecisionFcn : function(v)
41546 return Math.floor(v);
41549 validateValue : function(value)
41551 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41555 var num = this.parseValue(value);
41558 this.markInvalid(String.format(this.nanText, value));
41562 if(num < this.minValue){
41563 this.markInvalid(String.format(this.minText, this.minValue));
41567 if(num > this.maxValue){
41568 this.markInvalid(String.format(this.maxText, this.maxValue));
41575 validate : function()
41577 if(this.disabled || this.allowBlank){
41582 var currency = this.getCurrency();
41584 if(this.validateValue(this.getRawValue()) && currency.length){
41589 this.markInvalid();
41593 getName: function()
41598 beforeBlur : function()
41604 var v = this.parseValue(this.getRawValue());
41611 onBlur : function()
41615 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41616 //this.el.removeClass(this.focusClass);
41619 this.hasFocus = false;
41621 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41625 var v = this.getValue();
41627 if(String(v) !== String(this.startValue)){
41628 this.fireEvent('change', this, v, this.startValue);
41631 this.fireEvent("blur", this);
41634 inputEl : function()
41636 return this.el.select('.roo-money-amount-input', true).first();
41639 currencyEl : function()
41641 return this.el.select('.roo-money-currency-input', true).first();
41644 hiddenEl : function()
41646 return this.el.select('input.hidden-number-input',true).first();
41650 * @class Roo.bootstrap.BezierSignature
41651 * @extends Roo.bootstrap.Component
41652 * Bootstrap BezierSignature class
41653 * This script refer to:
41654 * Title: Signature Pad
41656 * Availability: https://github.com/szimek/signature_pad
41659 * Create a new BezierSignature
41660 * @param {Object} config The config object
41663 Roo.bootstrap.BezierSignature = function(config){
41664 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41670 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41677 mouse_btn_down: true,
41680 * @cfg {int} canvas height
41682 canvas_height: '200px',
41685 * @cfg {float|function} Radius of a single dot.
41690 * @cfg {float} Minimum width of a line. Defaults to 0.5.
41695 * @cfg {float} Maximum width of a line. Defaults to 2.5.
41700 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41705 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41710 * @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.
41712 bg_color: 'rgba(0, 0, 0, 0)',
41715 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41717 dot_color: 'black',
41720 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41722 velocity_filter_weight: 0.7,
41725 * @cfg {function} Callback when stroke begin.
41730 * @cfg {function} Callback when stroke end.
41734 getAutoCreate : function()
41736 var cls = 'roo-signature column';
41739 cls += ' ' + this.cls;
41749 for(var i = 0; i < col_sizes.length; i++) {
41750 if(this[col_sizes[i]]) {
41751 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41761 cls: 'roo-signature-body',
41765 cls: 'roo-signature-body-canvas',
41766 height: this.canvas_height,
41767 width: this.canvas_width
41774 style: 'display: none'
41782 initEvents: function()
41784 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41786 var canvas = this.canvasEl();
41788 // mouse && touch event swapping...
41789 canvas.dom.style.touchAction = 'none';
41790 canvas.dom.style.msTouchAction = 'none';
41792 this.mouse_btn_down = false;
41793 canvas.on('mousedown', this._handleMouseDown, this);
41794 canvas.on('mousemove', this._handleMouseMove, this);
41795 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41797 if (window.PointerEvent) {
41798 canvas.on('pointerdown', this._handleMouseDown, this);
41799 canvas.on('pointermove', this._handleMouseMove, this);
41800 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41803 if ('ontouchstart' in window) {
41804 canvas.on('touchstart', this._handleTouchStart, this);
41805 canvas.on('touchmove', this._handleTouchMove, this);
41806 canvas.on('touchend', this._handleTouchEnd, this);
41809 Roo.EventManager.onWindowResize(this.resize, this, true);
41811 // file input event
41812 this.fileEl().on('change', this.uploadImage, this);
41819 resize: function(){
41821 var canvas = this.canvasEl().dom;
41822 var ctx = this.canvasElCtx();
41823 var img_data = false;
41825 if(canvas.width > 0) {
41826 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41828 // setting canvas width will clean img data
41831 var style = window.getComputedStyle ?
41832 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41834 var padding_left = parseInt(style.paddingLeft) || 0;
41835 var padding_right = parseInt(style.paddingRight) || 0;
41837 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41840 ctx.putImageData(img_data, 0, 0);
41844 _handleMouseDown: function(e)
41846 if (e.browserEvent.which === 1) {
41847 this.mouse_btn_down = true;
41848 this.strokeBegin(e);
41852 _handleMouseMove: function (e)
41854 if (this.mouse_btn_down) {
41855 this.strokeMoveUpdate(e);
41859 _handleMouseUp: function (e)
41861 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41862 this.mouse_btn_down = false;
41867 _handleTouchStart: function (e) {
41869 e.preventDefault();
41870 if (e.browserEvent.targetTouches.length === 1) {
41871 // var touch = e.browserEvent.changedTouches[0];
41872 // this.strokeBegin(touch);
41874 this.strokeBegin(e); // assume e catching the correct xy...
41878 _handleTouchMove: function (e) {
41879 e.preventDefault();
41880 // var touch = event.targetTouches[0];
41881 // _this._strokeMoveUpdate(touch);
41882 this.strokeMoveUpdate(e);
41885 _handleTouchEnd: function (e) {
41886 var wasCanvasTouched = e.target === this.canvasEl().dom;
41887 if (wasCanvasTouched) {
41888 e.preventDefault();
41889 // var touch = event.changedTouches[0];
41890 // _this._strokeEnd(touch);
41895 reset: function () {
41896 this._lastPoints = [];
41897 this._lastVelocity = 0;
41898 this._lastWidth = (this.min_width + this.max_width) / 2;
41899 this.canvasElCtx().fillStyle = this.dot_color;
41902 strokeMoveUpdate: function(e)
41904 this.strokeUpdate(e);
41906 if (this.throttle) {
41907 this.throttleStroke(this.strokeUpdate, this.throttle);
41910 this.strokeUpdate(e);
41914 strokeBegin: function(e)
41916 var newPointGroup = {
41917 color: this.dot_color,
41921 if (typeof this.onBegin === 'function') {
41925 this.curve_data.push(newPointGroup);
41927 this.strokeUpdate(e);
41930 strokeUpdate: function(e)
41932 var rect = this.canvasEl().dom.getBoundingClientRect();
41933 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41934 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41935 var lastPoints = lastPointGroup.points;
41936 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41937 var isLastPointTooClose = lastPoint
41938 ? point.distanceTo(lastPoint) <= this.min_distance
41940 var color = lastPointGroup.color;
41941 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41942 var curve = this.addPoint(point);
41944 this.drawDot({color: color, point: point});
41947 this.drawCurve({color: color, curve: curve});
41957 strokeEnd: function(e)
41959 this.strokeUpdate(e);
41960 if (typeof this.onEnd === 'function') {
41965 addPoint: function (point) {
41966 var _lastPoints = this._lastPoints;
41967 _lastPoints.push(point);
41968 if (_lastPoints.length > 2) {
41969 if (_lastPoints.length === 3) {
41970 _lastPoints.unshift(_lastPoints[0]);
41972 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41973 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41974 _lastPoints.shift();
41980 calculateCurveWidths: function (startPoint, endPoint) {
41981 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41982 (1 - this.velocity_filter_weight) * this._lastVelocity;
41984 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41987 start: this._lastWidth
41990 this._lastVelocity = velocity;
41991 this._lastWidth = newWidth;
41995 drawDot: function (_a) {
41996 var color = _a.color, point = _a.point;
41997 var ctx = this.canvasElCtx();
41998 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
42000 this.drawCurveSegment(point.x, point.y, width);
42002 ctx.fillStyle = color;
42006 drawCurve: function (_a) {
42007 var color = _a.color, curve = _a.curve;
42008 var ctx = this.canvasElCtx();
42009 var widthDelta = curve.endWidth - curve.startWidth;
42010 var drawSteps = Math.floor(curve.length()) * 2;
42012 ctx.fillStyle = color;
42013 for (var i = 0; i < drawSteps; i += 1) {
42014 var t = i / drawSteps;
42020 var x = uuu * curve.startPoint.x;
42021 x += 3 * uu * t * curve.control1.x;
42022 x += 3 * u * tt * curve.control2.x;
42023 x += ttt * curve.endPoint.x;
42024 var y = uuu * curve.startPoint.y;
42025 y += 3 * uu * t * curve.control1.y;
42026 y += 3 * u * tt * curve.control2.y;
42027 y += ttt * curve.endPoint.y;
42028 var width = curve.startWidth + ttt * widthDelta;
42029 this.drawCurveSegment(x, y, width);
42035 drawCurveSegment: function (x, y, width) {
42036 var ctx = this.canvasElCtx();
42038 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42039 this.is_empty = false;
42044 var ctx = this.canvasElCtx();
42045 var canvas = this.canvasEl().dom;
42046 ctx.fillStyle = this.bg_color;
42047 ctx.clearRect(0, 0, canvas.width, canvas.height);
42048 ctx.fillRect(0, 0, canvas.width, canvas.height);
42049 this.curve_data = [];
42051 this.is_empty = true;
42056 return this.el.select('input',true).first();
42059 canvasEl: function()
42061 return this.el.select('canvas',true).first();
42064 canvasElCtx: function()
42066 return this.el.select('canvas',true).first().dom.getContext('2d');
42069 getImage: function(type)
42071 if(this.is_empty) {
42076 return this.canvasEl().dom.toDataURL('image/'+type, 1);
42079 drawFromImage: function(img_src)
42081 var img = new Image();
42083 img.onload = function(){
42084 this.canvasElCtx().drawImage(img, 0, 0);
42089 this.is_empty = false;
42092 selectImage: function()
42094 this.fileEl().dom.click();
42097 uploadImage: function(e)
42099 var reader = new FileReader();
42101 reader.onload = function(e){
42102 var img = new Image();
42103 img.onload = function(){
42105 this.canvasElCtx().drawImage(img, 0, 0);
42107 img.src = e.target.result;
42110 reader.readAsDataURL(e.target.files[0]);
42113 // Bezier Point Constructor
42114 Point: (function () {
42115 function Point(x, y, time) {
42118 this.time = time || Date.now();
42120 Point.prototype.distanceTo = function (start) {
42121 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42123 Point.prototype.equals = function (other) {
42124 return this.x === other.x && this.y === other.y && this.time === other.time;
42126 Point.prototype.velocityFrom = function (start) {
42127 return this.time !== start.time
42128 ? this.distanceTo(start) / (this.time - start.time)
42135 // Bezier Constructor
42136 Bezier: (function () {
42137 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42138 this.startPoint = startPoint;
42139 this.control2 = control2;
42140 this.control1 = control1;
42141 this.endPoint = endPoint;
42142 this.startWidth = startWidth;
42143 this.endWidth = endWidth;
42145 Bezier.fromPoints = function (points, widths, scope) {
42146 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42147 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42148 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42150 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42151 var dx1 = s1.x - s2.x;
42152 var dy1 = s1.y - s2.y;
42153 var dx2 = s2.x - s3.x;
42154 var dy2 = s2.y - s3.y;
42155 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42156 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42157 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42158 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42159 var dxm = m1.x - m2.x;
42160 var dym = m1.y - m2.y;
42161 var k = l2 / (l1 + l2);
42162 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42163 var tx = s2.x - cm.x;
42164 var ty = s2.y - cm.y;
42166 c1: new scope.Point(m1.x + tx, m1.y + ty),
42167 c2: new scope.Point(m2.x + tx, m2.y + ty)
42170 Bezier.prototype.length = function () {
42175 for (var i = 0; i <= steps; i += 1) {
42177 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42178 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42180 var xdiff = cx - px;
42181 var ydiff = cy - py;
42182 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42189 Bezier.prototype.point = function (t, start, c1, c2, end) {
42190 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42191 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42192 + (3.0 * c2 * (1.0 - t) * t * t)
42193 + (end * t * t * t);
42198 throttleStroke: function(fn, wait) {
42199 if (wait === void 0) { wait = 250; }
42201 var timeout = null;
42205 var later = function () {
42206 previous = Date.now();
42208 result = fn.apply(storedContext, storedArgs);
42210 storedContext = null;
42214 return function wrapper() {
42216 for (var _i = 0; _i < arguments.length; _i++) {
42217 args[_i] = arguments[_i];
42219 var now = Date.now();
42220 var remaining = wait - (now - previous);
42221 storedContext = this;
42223 if (remaining <= 0 || remaining > wait) {
42225 clearTimeout(timeout);
42229 result = fn.apply(storedContext, storedArgs);
42231 storedContext = null;
42235 else if (!timeout) {
42236 timeout = window.setTimeout(later, remaining);