4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921 setWeight : function(str)
923 this.el.removeClass(this.weightClass);
924 this.el.addClass('btn-' + str);
938 * @class Roo.bootstrap.Column
939 * @extends Roo.bootstrap.Component
940 * Bootstrap Column class
941 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
951 * @cfg {Boolean} hidden (true|false) hide the element
952 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953 * @cfg {String} fa (ban|check|...) font awesome icon
954 * @cfg {Number} fasize (1|2|....) font awsome size
956 * @cfg {String} icon (info-sign|check|...) glyphicon name
958 * @cfg {String} html content of column.
961 * Create a new Column
962 * @param {Object} config The config object
965 Roo.bootstrap.Column = function(config){
966 Roo.bootstrap.Column.superclass.constructor.call(this, config);
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
987 getAutoCreate : function(){
988 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
996 ['xs','sm','md','lg'].map(function(size){
997 //Roo.log( size + ':' + settings[size]);
999 if (settings[size+'off'] !== false) {
1000 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1003 if (settings[size] === false) {
1007 if (!settings[size]) { // 0 = hidden
1008 cfg.cls += ' hidden-' + size;
1011 cfg.cls += ' col-' + size + '-' + settings[size];
1016 cfg.cls += ' hidden';
1019 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020 cfg.cls +=' alert alert-' + this.alert;
1024 if (this.html.length) {
1025 cfg.html = this.html;
1029 if (this.fasize > 1) {
1030 fasize = ' fa-' + this.fasize + 'x';
1032 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1056 * @class Roo.bootstrap.Container
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Container class
1059 * @cfg {Boolean} jumbotron is it a jumbotron element
1060 * @cfg {String} html content of element
1061 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1063 * @cfg {String} header content of header (for panel)
1064 * @cfg {String} footer content of footer (for panel)
1065 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066 * @cfg {String} tag (header|aside|section) type of HTML tag.
1067 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068 * @cfg {String} fa font awesome icon
1069 * @cfg {String} icon (info-sign|check|...) glyphicon name
1070 * @cfg {Boolean} hidden (true|false) hide the element
1071 * @cfg {Boolean} expandable (true|false) default false
1072 * @cfg {Boolean} expanded (true|false) default true
1073 * @cfg {String} rheader contet on the right of header
1074 * @cfg {Boolean} clickable (true|false) default false
1078 * Create a new Container
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Container = function(config){
1083 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1089 * After the panel has been expand
1091 * @param {Roo.bootstrap.Container} this
1096 * After the panel has been collapsed
1098 * @param {Roo.bootstrap.Container} this
1103 * When a element is chick
1104 * @param {Roo.bootstrap.Container} this
1105 * @param {Roo.EventObject} e
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1129 getChildContainer : function() {
1135 if (this.panel.length) {
1136 return this.el.select('.panel-body',true).first();
1143 getAutoCreate : function(){
1146 tag : this.tag || 'div',
1150 if (this.jumbotron) {
1151 cfg.cls = 'jumbotron';
1156 // - this is applied by the parent..
1158 // cfg.cls = this.cls + '';
1161 if (this.sticky.length) {
1163 var bd = Roo.get(document.body);
1164 if (!bd.hasClass('bootstrap-sticky')) {
1165 bd.addClass('bootstrap-sticky');
1166 Roo.select('html',true).setStyle('height', '100%');
1169 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1173 if (this.well.length) {
1174 switch (this.well) {
1177 cfg.cls +=' well well-' +this.well;
1186 cfg.cls += ' hidden';
1190 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191 cfg.cls +=' alert alert-' + this.alert;
1196 if (this.panel.length) {
1197 cfg.cls += ' panel panel-' + this.panel;
1199 if (this.header.length) {
1203 if(this.expandable){
1205 cfg.cls = cfg.cls + ' expandable';
1209 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1217 cls : 'panel-title',
1218 html : (this.expandable ? ' ' : '') + this.header
1222 cls: 'panel-header-right',
1228 cls : 'panel-heading',
1229 style : this.expandable ? 'cursor: pointer' : '',
1237 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242 if (this.footer.length) {
1244 cls : 'panel-footer',
1253 body.html = this.html || cfg.html;
1254 // prefix with the icons..
1256 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1259 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265 cfg.cls = 'container';
1271 initEvents: function()
1273 if(this.expandable){
1274 var headerEl = this.headerEl();
1277 headerEl.on('click', this.onToggleClick, this);
1282 this.el.on('click', this.onClick, this);
1287 onToggleClick : function()
1289 var headerEl = this.headerEl();
1305 if(this.fireEvent('expand', this)) {
1307 this.expanded = true;
1309 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1311 this.el.select('.panel-body',true).first().removeClass('hide');
1313 var toggleEl = this.toggleEl();
1319 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324 collapse : function()
1326 if(this.fireEvent('collapse', this)) {
1328 this.expanded = false;
1330 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331 this.el.select('.panel-body',true).first().addClass('hide');
1333 var toggleEl = this.toggleEl();
1339 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1343 toggleEl : function()
1345 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1349 return this.el.select('.panel-heading .fa',true).first();
1352 headerEl : function()
1354 if(!this.el || !this.panel.length || !this.header.length){
1358 return this.el.select('.panel-heading',true).first()
1363 if(!this.el || !this.panel.length){
1367 return this.el.select('.panel-body',true).first()
1370 titleEl : function()
1372 if(!this.el || !this.panel.length || !this.header.length){
1376 return this.el.select('.panel-title',true).first();
1379 setTitle : function(v)
1381 var titleEl = this.titleEl();
1387 titleEl.dom.innerHTML = v;
1390 getTitle : function()
1393 var titleEl = this.titleEl();
1399 return titleEl.dom.innerHTML;
1402 setRightTitle : function(v)
1404 var t = this.el.select('.panel-header-right',true).first();
1410 t.dom.innerHTML = v;
1413 onClick : function(e)
1417 this.fireEvent('click', this, e);
1430 * @class Roo.bootstrap.Img
1431 * @extends Roo.bootstrap.Component
1432 * Bootstrap Img class
1433 * @cfg {Boolean} imgResponsive false | true
1434 * @cfg {String} border rounded | circle | thumbnail
1435 * @cfg {String} src image source
1436 * @cfg {String} alt image alternative text
1437 * @cfg {String} href a tag href
1438 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439 * @cfg {String} xsUrl xs image source
1440 * @cfg {String} smUrl sm image source
1441 * @cfg {String} mdUrl md image source
1442 * @cfg {String} lgUrl lg image source
1445 * Create a new Input
1446 * @param {Object} config The config object
1449 Roo.bootstrap.Img = function(config){
1450 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1456 * The img click event for the img.
1457 * @param {Roo.EventObject} e
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1465 imgResponsive: true,
1475 getAutoCreate : function()
1477 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478 return this.createSingleImg();
1483 cls: 'roo-image-responsive-group',
1488 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1490 if(!_this[size + 'Url']){
1496 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497 html: _this.html || cfg.html,
1498 src: _this[size + 'Url']
1501 img.cls += ' roo-image-responsive-' + size;
1503 var s = ['xs', 'sm', 'md', 'lg'];
1505 s.splice(s.indexOf(size), 1);
1507 Roo.each(s, function(ss){
1508 img.cls += ' hidden-' + ss;
1511 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512 cfg.cls += ' img-' + _this.border;
1516 cfg.alt = _this.alt;
1529 a.target = _this.target;
1533 cfg.cn.push((_this.href) ? a : img);
1540 createSingleImg : function()
1544 cls: (this.imgResponsive) ? 'img-responsive' : '',
1546 src : 'about:blank' // just incase src get's set to undefined?!?
1549 cfg.html = this.html || cfg.html;
1551 cfg.src = this.src || cfg.src;
1553 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554 cfg.cls += ' img-' + this.border;
1571 a.target = this.target;
1576 return (this.href) ? a : cfg;
1579 initEvents: function()
1582 this.el.on('click', this.onClick, this);
1587 onClick : function(e)
1589 Roo.log('img onclick');
1590 this.fireEvent('click', this, e);
1593 * Sets the url of the image - used to update it
1594 * @param {String} url the url of the image
1597 setSrc : function(url)
1601 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602 this.el.dom.src = url;
1606 this.el.select('img', true).first().dom.src = url;
1622 * @class Roo.bootstrap.Link
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap Link Class
1625 * @cfg {String} alt image alternative text
1626 * @cfg {String} href a tag href
1627 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628 * @cfg {String} html the content of the link.
1629 * @cfg {String} anchor name for the anchor link
1630 * @cfg {String} fa - favicon
1632 * @cfg {Boolean} preventDefault (true | false) default false
1636 * Create a new Input
1637 * @param {Object} config The config object
1640 Roo.bootstrap.Link = function(config){
1641 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1647 * The img click event for the img.
1648 * @param {Roo.EventObject} e
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1658 preventDefault: false,
1664 getAutoCreate : function()
1666 var html = this.html || '';
1668 if (this.fa !== false) {
1669 html = '<i class="fa fa-' + this.fa + '"></i>';
1674 // anchor's do not require html/href...
1675 if (this.anchor === false) {
1677 cfg.href = this.href || '#';
1679 cfg.name = this.anchor;
1680 if (this.html !== false || this.fa !== false) {
1683 if (this.href !== false) {
1684 cfg.href = this.href;
1688 if(this.alt !== false){
1693 if(this.target !== false) {
1694 cfg.target = this.target;
1700 initEvents: function() {
1702 if(!this.href || this.preventDefault){
1703 this.el.on('click', this.onClick, this);
1707 onClick : function(e)
1709 if(this.preventDefault){
1712 //Roo.log('img onclick');
1713 this.fireEvent('click', this, e);
1726 * @class Roo.bootstrap.Header
1727 * @extends Roo.bootstrap.Component
1728 * Bootstrap Header class
1729 * @cfg {String} html content of header
1730 * @cfg {Number} level (1|2|3|4|5|6) default 1
1733 * Create a new Header
1734 * @param {Object} config The config object
1738 Roo.bootstrap.Header = function(config){
1739 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1750 getAutoCreate : function(){
1755 tag: 'h' + (1 *this.level),
1756 html: this.html || ''
1768 * Ext JS Library 1.1.1
1769 * Copyright(c) 2006-2007, Ext JS, LLC.
1771 * Originally Released Under LGPL - original licence link has changed is not relivant.
1774 * <script type="text/javascript">
1778 * @class Roo.bootstrap.MenuMgr
1779 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1782 Roo.bootstrap.MenuMgr = function(){
1783 var menus, active, groups = {}, attached = false, lastShow = new Date();
1785 // private - called when first menu is created
1788 active = new Roo.util.MixedCollection();
1789 Roo.get(document).addKeyListener(27, function(){
1790 if(active.length > 0){
1798 if(active && active.length > 0){
1799 var c = active.clone();
1809 if(active.length < 1){
1810 Roo.get(document).un("mouseup", onMouseDown);
1818 var last = active.last();
1819 lastShow = new Date();
1822 Roo.get(document).on("mouseup", onMouseDown);
1827 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828 m.parentMenu.activeChild = m;
1829 }else if(last && last.isVisible()){
1830 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835 function onBeforeHide(m){
1837 m.activeChild.hide();
1839 if(m.autoHideTimer){
1840 clearTimeout(m.autoHideTimer);
1841 delete m.autoHideTimer;
1846 function onBeforeShow(m){
1847 var pm = m.parentMenu;
1848 if(!pm && !m.allowOtherMenus){
1850 }else if(pm && pm.activeChild && active != m){
1851 pm.activeChild.hide();
1855 // private this should really trigger on mouseup..
1856 function onMouseDown(e){
1857 Roo.log("on Mouse Up");
1859 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860 Roo.log("MenuManager hideAll");
1869 function onBeforeCheck(mi, state){
1871 var g = groups[mi.group];
1872 for(var i = 0, l = g.length; i < l; i++){
1874 g[i].setChecked(false);
1883 * Hides all menus that are currently visible
1885 hideAll : function(){
1890 register : function(menu){
1894 menus[menu.id] = menu;
1895 menu.on("beforehide", onBeforeHide);
1896 menu.on("hide", onHide);
1897 menu.on("beforeshow", onBeforeShow);
1898 menu.on("show", onShow);
1900 if(g && menu.events["checkchange"]){
1904 groups[g].push(menu);
1905 menu.on("checkchange", onCheck);
1910 * Returns a {@link Roo.menu.Menu} object
1911 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912 * be used to generate and return a new Menu instance.
1914 get : function(menu){
1915 if(typeof menu == "string"){ // menu id
1917 }else if(menu.events){ // menu instance
1920 /*else if(typeof menu.length == 'number'){ // array of menu items?
1921 return new Roo.bootstrap.Menu({items:menu});
1922 }else{ // otherwise, must be a config
1923 return new Roo.bootstrap.Menu(menu);
1930 unregister : function(menu){
1931 delete menus[menu.id];
1932 menu.un("beforehide", onBeforeHide);
1933 menu.un("hide", onHide);
1934 menu.un("beforeshow", onBeforeShow);
1935 menu.un("show", onShow);
1937 if(g && menu.events["checkchange"]){
1938 groups[g].remove(menu);
1939 menu.un("checkchange", onCheck);
1944 registerCheckable : function(menuItem){
1945 var g = menuItem.group;
1950 groups[g].push(menuItem);
1951 menuItem.on("beforecheckchange", onBeforeCheck);
1956 unregisterCheckable : function(menuItem){
1957 var g = menuItem.group;
1959 groups[g].remove(menuItem);
1960 menuItem.un("beforecheckchange", onBeforeCheck);
1972 * @class Roo.bootstrap.Menu
1973 * @extends Roo.bootstrap.Component
1974 * Bootstrap Menu class - container for MenuItems
1975 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976 * @cfg {bool} hidden if the menu should be hidden when rendered.
1977 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1978 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1982 * @param {Object} config The config object
1986 Roo.bootstrap.Menu = function(config){
1987 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988 if (this.registerMenu && this.type != 'treeview') {
1989 Roo.bootstrap.MenuMgr.register(this);
1994 * Fires before this menu is displayed
1995 * @param {Roo.menu.Menu} this
2000 * Fires before this menu is hidden
2001 * @param {Roo.menu.Menu} this
2006 * Fires after this menu is displayed
2007 * @param {Roo.menu.Menu} this
2012 * Fires after this menu is hidden
2013 * @param {Roo.menu.Menu} this
2018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019 * @param {Roo.menu.Menu} this
2020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021 * @param {Roo.EventObject} e
2026 * Fires when the mouse is hovering over this menu
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.EventObject} e
2029 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034 * Fires when the mouse exits this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when a menu item contained in this menu is clicked
2043 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044 * @param {Roo.EventObject} e
2048 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2055 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2058 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2060 registerMenu : true,
2062 menuItems :false, // stores the menu items..
2072 getChildContainer : function() {
2076 getAutoCreate : function(){
2078 //if (['right'].indexOf(this.align)!==-1) {
2079 // cfg.cn[1].cls += ' pull-right'
2085 cls : 'dropdown-menu' ,
2086 style : 'z-index:1000'
2090 if (this.type === 'submenu') {
2091 cfg.cls = 'submenu active';
2093 if (this.type === 'treeview') {
2094 cfg.cls = 'treeview-menu';
2099 initEvents : function() {
2101 // Roo.log("ADD event");
2102 // Roo.log(this.triggerEl.dom);
2104 this.triggerEl.on('click', this.onTriggerClick, this);
2106 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2108 this.triggerEl.addClass('dropdown-toggle');
2111 this.el.on('touchstart' , this.onTouch, this);
2113 this.el.on('click' , this.onClick, this);
2115 this.el.on("mouseover", this.onMouseOver, this);
2116 this.el.on("mouseout", this.onMouseOut, this);
2120 findTargetItem : function(e)
2122 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2126 //Roo.log(t); Roo.log(t.id);
2128 //Roo.log(this.menuitems);
2129 return this.menuitems.get(t.id);
2131 //return this.items.get(t.menuItemId);
2137 onTouch : function(e)
2139 Roo.log("menu.onTouch");
2140 //e.stopEvent(); this make the user popdown broken
2144 onClick : function(e)
2146 Roo.log("menu.onClick");
2148 var t = this.findTargetItem(e);
2149 if(!t || t.isContainer){
2154 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2155 if(t == this.activeItem && t.shouldDeactivate(e)){
2156 this.activeItem.deactivate();
2157 delete this.activeItem;
2161 this.setActiveItem(t, true);
2169 Roo.log('pass click event');
2173 this.fireEvent("click", this, t, e);
2177 if(!t.href.length || t.href == '#'){
2178 (function() { _this.hide(); }).defer(100);
2183 onMouseOver : function(e){
2184 var t = this.findTargetItem(e);
2187 // if(t.canActivate && !t.disabled){
2188 // this.setActiveItem(t, true);
2192 this.fireEvent("mouseover", this, e, t);
2194 isVisible : function(){
2195 return !this.hidden;
2197 onMouseOut : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t == this.activeItem && t.shouldDeactivate(e)){
2202 // this.activeItem.deactivate();
2203 // delete this.activeItem;
2206 this.fireEvent("mouseout", this, e, t);
2211 * Displays this menu relative to another element
2212 * @param {String/HTMLElement/Roo.Element} element The element to align to
2213 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214 * the element (defaults to this.defaultAlign)
2215 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2217 show : function(el, pos, parentMenu){
2218 this.parentMenu = parentMenu;
2222 this.fireEvent("beforeshow", this);
2223 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2226 * Displays this menu at a specific xy position
2227 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230 showAt : function(xy, parentMenu, /* private: */_e){
2231 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 //xy = this.el.adjustForConstraints(xy);
2241 this.hideMenuItems();
2242 this.hidden = false;
2243 this.triggerEl.addClass('open');
2245 // reassign x when hitting right
2246 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2250 // reassign y when hitting bottom
2251 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2255 // but the list may align on trigger left or trigger top... should it be a properity?
2257 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262 this.fireEvent("show", this);
2268 this.doFocus.defer(50, this);
2272 doFocus : function(){
2274 this.focusEl.focus();
2279 * Hides this menu and optionally all parent menus
2280 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282 hide : function(deep)
2285 this.hideMenuItems();
2286 if(this.el && this.isVisible()){
2287 this.fireEvent("beforehide", this);
2288 if(this.activeItem){
2289 this.activeItem.deactivate();
2290 this.activeItem = null;
2292 this.triggerEl.removeClass('open');;
2294 this.fireEvent("hide", this);
2296 if(deep === true && this.parentMenu){
2297 this.parentMenu.hide(true);
2301 onTriggerClick : function(e)
2303 Roo.log('trigger click');
2305 var target = e.getTarget();
2307 Roo.log(target.nodeName.toLowerCase());
2309 if(target.nodeName.toLowerCase() === 'i'){
2315 onTriggerPress : function(e)
2317 Roo.log('trigger press');
2318 //Roo.log(e.getTarget());
2319 // Roo.log(this.triggerEl.dom);
2321 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322 var pel = Roo.get(e.getTarget());
2323 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324 Roo.log('is treeview or dropdown?');
2328 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332 if (this.isVisible()) {
2337 this.show(this.triggerEl, false, false);
2340 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347 hideMenuItems : function()
2349 Roo.log("hide Menu Items");
2353 //$(backdrop).remove()
2354 this.el.select('.open',true).each(function(aa) {
2356 aa.removeClass('open');
2357 //var parent = getParent($(this))
2358 //var relatedTarget = { relatedTarget: this }
2360 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361 //if (e.isDefaultPrevented()) return
2362 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365 addxtypeChild : function (tree, cntr) {
2366 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368 this.menuitems.add(comp);
2380 this.getEl().dom.innerHTML = '';
2381 this.menuitems.clear();
2395 * @class Roo.bootstrap.MenuItem
2396 * @extends Roo.bootstrap.Component
2397 * Bootstrap MenuItem class
2398 * @cfg {String} html the menu label
2399 * @cfg {String} href the link
2400 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402 * @cfg {Boolean} active used on sidebars to highlight active itesm
2403 * @cfg {String} fa favicon to show on left of menu item.
2404 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408 * Create a new MenuItem
2409 * @param {Object} config The config object
2413 Roo.bootstrap.MenuItem = function(config){
2414 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419 * The raw click event for the entire grid.
2420 * @param {Roo.bootstrap.MenuItem} this
2421 * @param {Roo.EventObject} e
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2431 preventDefault: false,
2432 isContainer : false,
2436 getAutoCreate : function(){
2438 if(this.isContainer){
2441 cls: 'dropdown-menu-item'
2455 if (this.fa !== false) {
2458 cls : 'fa fa-' + this.fa
2467 cls: 'dropdown-menu-item',
2470 if (this.parent().type == 'treeview') {
2471 cfg.cls = 'treeview-menu';
2474 cfg.cls += ' active';
2479 anc.href = this.href || cfg.cn[0].href ;
2480 ctag.html = this.html || cfg.cn[0].html ;
2484 initEvents: function()
2486 if (this.parent().type == 'treeview') {
2487 this.el.select('a').on('click', this.onClick, this);
2491 this.menu.parentType = this.xtype;
2492 this.menu.triggerEl = this.el;
2493 this.menu = this.addxtype(Roo.apply({}, this.menu));
2497 onClick : function(e)
2499 Roo.log('item on click ');
2501 if(this.preventDefault){
2504 //this.parent().hideMenuItems();
2506 this.fireEvent('click', this, e);
2525 * @class Roo.bootstrap.MenuSeparator
2526 * @extends Roo.bootstrap.Component
2527 * Bootstrap MenuSeparator class
2530 * Create a new MenuItem
2531 * @param {Object} config The config object
2535 Roo.bootstrap.MenuSeparator = function(config){
2536 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2541 getAutoCreate : function(){
2560 * @class Roo.bootstrap.Modal
2561 * @extends Roo.bootstrap.Component
2562 * Bootstrap Modal class
2563 * @cfg {String} title Title of dialog
2564 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2566 * @cfg {Boolean} specificTitle default false
2567 * @cfg {Array} buttons Array of buttons or standard button set..
2568 * @cfg {String} buttonPosition (left|right|center) default right
2569 * @cfg {Boolean} animate default true
2570 * @cfg {Boolean} allow_close default true
2571 * @cfg {Boolean} fitwindow default false
2572 * @cfg {String} size (sm|lg) default empty
2573 * @cfg {Number} max_width set the max width of modal
2577 * Create a new Modal Dialog
2578 * @param {Object} config The config object
2581 Roo.bootstrap.Modal = function(config){
2582 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587 * The raw btnclick event for the button
2588 * @param {Roo.EventObject} e
2593 * Fire when dialog resize
2594 * @param {Roo.bootstrap.Modal} this
2595 * @param {Roo.EventObject} e
2599 this.buttons = this.buttons || [];
2602 this.tmpl = Roo.factory(this.tmpl);
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2609 title : 'test dialog',
2619 specificTitle: false,
2621 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function()
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(
2819 Roo.lib.Dom.getViewWidth(true),
2820 Roo.lib.Dom.getViewHeight(true)
2823 if (this.fitwindow) {
2825 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2831 if(this.max_width !== 0) {
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836 this.setSize(w, this.height);
2840 if(this.max_height) {
2841 this.setSize(w,Math.min(
2843 Roo.lib.Dom.getViewportHeight(true) - 60
2849 if(!this.fit_content) {
2850 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2854 this.setSize(w, Math.min(
2856 this.headerEl.getHeight() +
2857 this.footerEl.getHeight() +
2858 this.getChildHeight(this.bodyEl.dom.childNodes),
2859 Roo.lib.Dom.getViewportHeight(true) - 60)
2865 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h - this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3025 getChildHeight : function(child_nodes)
3029 child_nodes.length == 0
3034 var child_height = 0;
3036 for(var i = 0; i < child_nodes.length; i++) {
3039 * for modal with tabs...
3040 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042 var layout_childs = child_nodes[i].childNodes;
3044 for(var j = 0; j < layout_childs.length; j++) {
3046 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048 var layout_body_childs = layout_childs[j].childNodes;
3050 for(var k = 0; k < layout_body_childs.length; k++) {
3052 if(layout_body_childs[k].classList.contains('navbar')) {
3053 child_height += layout_body_childs[k].offsetHeight;
3057 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3059 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3061 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3063 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079 child_height += child_nodes[i].offsetHeight;
3080 // Roo.log(child_nodes[i].offsetHeight);
3083 return child_height;
3089 Roo.apply(Roo.bootstrap.Modal, {
3091 * Button config that displays a single OK button
3100 * Button config that displays Yes and No buttons
3116 * Button config that displays OK and Cancel buttons
3131 * Button config that displays Yes, No and Cancel buttons
3155 * messagebox - can be used as a replace
3159 * @class Roo.MessageBox
3160 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169 // process text value...
3173 // Show a dialog using config options:
3175 title:'Save Changes?',
3176 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177 buttons: Roo.Msg.YESNOCANCEL,
3184 Roo.bootstrap.MessageBox = function(){
3185 var dlg, opt, mask, waitTimer;
3186 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187 var buttons, activeTextEl, bwidth;
3191 var handleButton = function(button){
3193 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3197 var handleHide = function(){
3199 dlg.el.removeClass(opt.cls);
3202 // Roo.TaskMgr.stop(waitTimer);
3203 // waitTimer = null;
3208 var updateButtons = function(b){
3211 buttons["ok"].hide();
3212 buttons["cancel"].hide();
3213 buttons["yes"].hide();
3214 buttons["no"].hide();
3215 //dlg.footer.dom.style.display = 'none';
3218 dlg.footerEl.dom.style.display = '';
3219 for(var k in buttons){
3220 if(typeof buttons[k] != "function"){
3223 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224 width += buttons[k].el.getWidth()+15;
3234 var handleEsc = function(d, k, e){
3235 if(opt && opt.closable !== false){
3245 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246 * @return {Roo.BasicDialog} The BasicDialog element
3248 getDialog : function(){
3250 dlg = new Roo.bootstrap.Modal( {
3253 //constraintoviewport:false,
3255 //collapsible : false,
3260 //buttonAlign:"center",
3261 closeClick : function(){
3262 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3265 handleButton("cancel");
3270 dlg.on("hide", handleHide);
3272 //dlg.addKeyListener(27, handleEsc);
3274 this.buttons = buttons;
3275 var bt = this.buttonText;
3276 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281 bodyEl = dlg.bodyEl.createChild({
3283 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284 '<textarea class="roo-mb-textarea"></textarea>' +
3285 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3287 msgEl = bodyEl.dom.firstChild;
3288 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289 textboxEl.enableDisplayMode();
3290 textboxEl.addKeyListener([10,13], function(){
3291 if(dlg.isVisible() && opt && opt.buttons){
3294 }else if(opt.buttons.yes){
3295 handleButton("yes");
3299 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300 textareaEl.enableDisplayMode();
3301 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302 progressEl.enableDisplayMode();
3304 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305 var pf = progressEl.dom.firstChild;
3307 pp = Roo.get(pf.firstChild);
3308 pp.setHeight(pf.offsetHeight);
3316 * Updates the message box body text
3317 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318 * the XHTML-compliant non-breaking space character '&#160;')
3319 * @return {Roo.MessageBox} This message box
3321 updateText : function(text)
3323 if(!dlg.isVisible() && !opt.width){
3324 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327 msgEl.innerHTML = text || ' ';
3329 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332 Math.min(opt.width || cw , this.maxWidth),
3333 Math.max(opt.minWidth || this.minWidth, bwidth)
3336 activeTextEl.setWidth(w);
3338 if(dlg.isVisible()){
3339 dlg.fixedcenter = false;
3341 // to big, make it scroll. = But as usual stupid IE does not support
3344 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348 bodyEl.dom.style.height = '';
3349 bodyEl.dom.style.overflowY = '';
3352 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354 bodyEl.dom.style.overflowX = '';
3357 dlg.setContentSize(w, bodyEl.getHeight());
3358 if(dlg.isVisible()){
3359 dlg.fixedcenter = true;
3365 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3366 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369 * @return {Roo.MessageBox} This message box
3371 updateProgress : function(value, text){
3373 this.updateText(text);
3376 if (pp) { // weird bug on my firefox - for some reason this is not defined
3377 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3384 * Returns true if the message box is currently displayed
3385 * @return {Boolean} True if the message box is visible, else false
3387 isVisible : function(){
3388 return dlg && dlg.isVisible();
3392 * Hides the message box if it is displayed
3395 if(this.isVisible()){
3401 * Displays a new message box, or reinitializes an existing message box, based on the config options
3402 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403 * The following config object properties are supported:
3405 Property Type Description
3406 ---------- --------------- ------------------------------------------------------------------------------------
3407 animEl String/Element An id or Element from which the message box should animate as it opens and
3408 closes (defaults to undefined)
3409 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable Boolean False to hide the top-right close button (defaults to true). Note that
3412 progress and wait dialogs will ignore this property and always hide the
3413 close button as they can only be closed programmatically.
3414 cls String A custom CSS class to apply to the message box element
3415 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3416 displayed (defaults to 75)
3417 fn Function A callback function to execute after closing the dialog. The arguments to the
3418 function will be btn (the name of the button that was clicked, if applicable,
3419 e.g. "ok"), and text (the value of the active text field, if applicable).
3420 Progress and wait dialogs will ignore this option since they do not respond to
3421 user actions and can only be closed programmatically, so any required function
3422 should be called by the same code after it closes the dialog.
3423 icon String A CSS class that provides a background image to be used as an icon for
3424 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3426 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3427 modal Boolean False to allow user interaction with the page while the message box is
3428 displayed (defaults to true)
3429 msg String A string that will replace the existing message box body text (defaults
3430 to the XHTML-compliant non-breaking space character ' ')
3431 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3432 progress Boolean True to display a progress bar (defaults to false)
3433 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3436 title String The title text
3437 value String The string value to set into the active textbox element if displayed
3438 wait Boolean True to display a progress bar (defaults to false)
3439 width Number The width of the dialog in pixels
3446 msg: 'Please enter your address:',
3448 buttons: Roo.MessageBox.OKCANCEL,
3451 animEl: 'addAddressBtn'
3454 * @param {Object} config Configuration options
3455 * @return {Roo.MessageBox} This message box
3457 show : function(options)
3460 // this causes nightmares if you show one dialog after another
3461 // especially on callbacks..
3463 if(this.isVisible()){
3466 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3468 Roo.log("New Dialog Message:" + options.msg )
3469 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3473 var d = this.getDialog();
3475 d.setTitle(opt.title || " ");
3476 d.closeEl.setDisplayed(opt.closable !== false);
3477 activeTextEl = textboxEl;
3478 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3483 textareaEl.setHeight(typeof opt.multiline == "number" ?
3484 opt.multiline : this.defaultTextHeight);
3485 activeTextEl = textareaEl;
3494 progressEl.setDisplayed(opt.progress === true);
3495 this.updateProgress(0);
3496 activeTextEl.dom.value = opt.value || "";
3498 dlg.setDefaultButton(activeTextEl);
3500 var bs = opt.buttons;
3504 }else if(bs && bs.yes){
3505 db = buttons["yes"];
3507 dlg.setDefaultButton(db);
3509 bwidth = updateButtons(opt.buttons);
3510 this.updateText(opt.msg);
3512 d.el.addClass(opt.cls);
3514 d.proxyDrag = opt.proxyDrag === true;
3515 d.modal = opt.modal !== false;
3516 d.mask = opt.modal !== false ? mask : false;
3518 // force it to the end of the z-index stack so it gets a cursor in FF
3519 document.body.appendChild(dlg.el.dom);
3520 d.animateTarget = null;
3521 d.show(options.animEl);
3527 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3528 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529 * and closing the message box when the process is complete.
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @return {Roo.MessageBox} This message box
3534 progress : function(title, msg){
3541 minWidth: this.minProgressWidth,
3548 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549 * If a callback function is passed it will be called after the user clicks the button, and the
3550 * id of the button that was clicked will be passed as the only parameter to the callback
3551 * (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 alert : function(title, msg, fn, scope)
3573 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3574 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575 * You are responsible for closing the message box when the process is complete.
3576 * @param {String} msg The message box body text
3577 * @param {String} title (optional) The title bar text
3578 * @return {Roo.MessageBox} This message box
3580 wait : function(msg, title){
3591 waitTimer = Roo.TaskMgr.start({
3593 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3601 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604 * @param {String} title The title bar text
3605 * @param {String} msg The message box body text
3606 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607 * @param {Object} scope (optional) The scope of the callback function
3608 * @return {Roo.MessageBox} This message box
3610 confirm : function(title, msg, fn, scope){
3614 buttons: this.YESNO,
3623 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3625 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626 * (could also be the top-right close button) and the text that was entered will be passed as the two
3627 * parameters to the callback.
3628 * @param {String} title The title bar text
3629 * @param {String} msg The message box body text
3630 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631 * @param {Object} scope (optional) The scope of the callback function
3632 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634 * @return {Roo.MessageBox} This message box
3636 prompt : function(title, msg, fn, scope, multiline){
3640 buttons: this.OKCANCEL,
3645 multiline: multiline,
3652 * Button config that displays a single OK button
3657 * Button config that displays Yes and No buttons
3660 YESNO : {yes:true, no:true},
3662 * Button config that displays OK and Cancel buttons
3665 OKCANCEL : {ok:true, cancel:true},
3667 * Button config that displays Yes, No and Cancel buttons
3670 YESNOCANCEL : {yes:true, no:true, cancel:true},
3673 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3676 defaultTextHeight : 75,
3678 * The maximum width in pixels of the message box (defaults to 600)
3683 * The minimum width in pixels of the message box (defaults to 100)
3688 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3689 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3692 minProgressWidth : 250,
3694 * An object containing the default button text strings that can be overriden for localized language support.
3695 * Supported properties are: ok, cancel, yes and no.
3696 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3709 * Shorthand for {@link Roo.MessageBox}
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 * @class Roo.bootstrap.Navbar
3722 * @extends Roo.bootstrap.Component
3723 * Bootstrap Navbar class
3726 * Create a new Navbar
3727 * @param {Object} config The config object
3731 Roo.bootstrap.Navbar = function(config){
3732 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3736 * @event beforetoggle
3737 * Fire before toggle the menu
3738 * @param {Roo.EventObject} e
3740 "beforetoggle" : true
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3753 getAutoCreate : function(){
3756 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3760 initEvents :function ()
3762 //Roo.log(this.el.select('.navbar-toggle',true));
3763 this.el.select('.navbar-toggle',true).on('click', function() {
3764 if(this.fireEvent('beforetoggle', this) !== false){
3765 this.el.select('.navbar-collapse',true).toggleClass('in');
3775 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777 var size = this.el.getSize();
3778 this.maskEl.setSize(size.width, size.height);
3779 this.maskEl.enableDisplayMode("block");
3788 getChildContainer : function()
3790 if (this.el.select('.collapse').getCount()) {
3791 return this.el.select('.collapse',true).first();
3824 * @class Roo.bootstrap.NavSimplebar
3825 * @extends Roo.bootstrap.Navbar
3826 * Bootstrap Sidebar class
3828 * @cfg {Boolean} inverse is inverted color
3830 * @cfg {String} type (nav | pills | tabs)
3831 * @cfg {Boolean} arrangement stacked | justified
3832 * @cfg {String} align (left | right) alignment
3834 * @cfg {Boolean} main (true|false) main nav bar? default false
3835 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837 * @cfg {String} tag (header|footer|nav|div) default is nav
3843 * Create a new Sidebar
3844 * @param {Object} config The config object
3848 Roo.bootstrap.NavSimplebar = function(config){
3849 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3868 getAutoCreate : function(){
3872 tag : this.tag || 'div',
3885 this.type = this.type || 'nav';
3886 if (['tabs','pills'].indexOf(this.type)!==-1) {
3887 cfg.cn[0].cls += ' nav-' + this.type
3891 if (this.type!=='nav') {
3892 Roo.log('nav type must be nav/tabs/pills')
3894 cfg.cn[0].cls += ' navbar-nav'
3900 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.arrangement;
3905 if (this.align === 'right') {
3906 cfg.cn[0].cls += ' navbar-right';
3910 cfg.cls += ' navbar-inverse';
3937 * @class Roo.bootstrap.NavHeaderbar
3938 * @extends Roo.bootstrap.NavSimplebar
3939 * Bootstrap Sidebar class
3941 * @cfg {String} brand what is brand
3942 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943 * @cfg {String} brand_href href of the brand
3944 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3945 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3967 desktopCenter : false,
3970 getAutoCreate : function(){
3973 tag: this.nav || 'nav',
3980 if (this.desktopCenter) {
3981 cn.push({cls : 'container', cn : []});
3988 cls: 'navbar-header',
3993 cls: 'navbar-toggle',
3994 'data-toggle': 'collapse',
3999 html: 'Toggle navigation'
4021 cls: 'collapse navbar-collapse',
4025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028 cfg.cls += ' navbar-' + this.position;
4030 // tag can override this..
4032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4035 if (this.brand !== '') {
4038 href: this.brand_href ? this.brand_href : '#',
4039 cls: 'navbar-brand',
4047 cfg.cls += ' main-nav';
4055 getHeaderChildContainer : function()
4057 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058 return this.el.select('.navbar-header',true).first();
4061 return this.getChildContainer();
4065 initEvents : function()
4067 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069 if (this.autohide) {
4074 Roo.get(document).on('scroll',function(e) {
4075 var ns = Roo.get(document).getScroll().top;
4076 var os = prevScroll;
4080 ft.removeClass('slideDown');
4081 ft.addClass('slideUp');
4084 ft.removeClass('slideUp');
4085 ft.addClass('slideDown');
4106 * @class Roo.bootstrap.NavSidebar
4107 * @extends Roo.bootstrap.Navbar
4108 * Bootstrap Sidebar class
4111 * Create a new Sidebar
4112 * @param {Object} config The config object
4116 Roo.bootstrap.NavSidebar = function(config){
4117 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4122 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124 getAutoCreate : function(){
4129 cls: 'sidebar sidebar-nav'
4151 * @class Roo.bootstrap.NavGroup
4152 * @extends Roo.bootstrap.Component
4153 * Bootstrap NavGroup class
4154 * @cfg {String} align (left|right)
4155 * @cfg {Boolean} inverse
4156 * @cfg {String} type (nav|pills|tab) default nav
4157 * @cfg {String} navId - reference Id for navbar.
4161 * Create a new nav group
4162 * @param {Object} config The config object
4165 Roo.bootstrap.NavGroup = function(config){
4166 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4169 Roo.bootstrap.NavGroup.register(this);
4173 * Fires when the active item changes
4174 * @param {Roo.bootstrap.NavGroup} this
4175 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4194 getAutoCreate : function()
4196 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4203 if (['tabs','pills'].indexOf(this.type)!==-1) {
4204 cfg.cls += ' nav-' + this.type
4206 if (this.type!=='nav') {
4207 Roo.log('nav type must be nav/tabs/pills')
4209 cfg.cls += ' navbar-nav'
4212 if (this.parent() && this.parent().sidebar) {
4215 cls: 'dashboard-menu sidebar-menu'
4221 if (this.form === true) {
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4230 cfg.cls += ' navbar-left';
4234 if (this.align === 'right') {
4235 cfg.cls += ' navbar-right';
4239 cfg.cls += ' navbar-inverse';
4247 * sets the active Navigation item
4248 * @param {Roo.bootstrap.NavItem} the new current navitem
4250 setActiveItem : function(item)
4253 Roo.each(this.navItems, function(v){
4258 v.setActive(false, true);
4265 item.setActive(true, true);
4266 this.fireEvent('changed', this, item, prev);
4271 * gets the active Navigation item
4272 * @return {Roo.bootstrap.NavItem} the current navitem
4274 getActive : function()
4278 Roo.each(this.navItems, function(v){
4289 indexOfNav : function()
4293 Roo.each(this.navItems, function(v,i){
4304 * adds a Navigation item
4305 * @param {Roo.bootstrap.NavItem} the navitem to add
4307 addItem : function(cfg)
4309 var cn = new Roo.bootstrap.NavItem(cfg);
4311 cn.parentId = this.id;
4312 cn.onRender(this.el, null);
4316 * register a Navigation item
4317 * @param {Roo.bootstrap.NavItem} the navitem to add
4319 register : function(item)
4321 this.navItems.push( item);
4322 item.navId = this.navId;
4327 * clear all the Navigation item
4330 clearAll : function()
4333 this.el.dom.innerHTML = '';
4336 getNavItem: function(tabId)
4339 Roo.each(this.navItems, function(e) {
4340 if (e.tabId == tabId) {
4350 setActiveNext : function()
4352 var i = this.indexOfNav(this.getActive());
4353 if (i > this.navItems.length) {
4356 this.setActiveItem(this.navItems[i+1]);
4358 setActivePrev : function()
4360 var i = this.indexOfNav(this.getActive());
4364 this.setActiveItem(this.navItems[i-1]);
4366 clearWasActive : function(except) {
4367 Roo.each(this.navItems, function(e) {
4368 if (e.tabId != except.tabId && e.was_active) {
4369 e.was_active = false;
4376 getWasActive : function ()
4379 Roo.each(this.navItems, function(e) {
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4398 * register a Navigation Group
4399 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401 register : function(navgrp)
4403 this.groups[navgrp.navId] = navgrp;
4407 * fetch a Navigation Group based on the navigation ID
4408 * @param {string} the navgroup to add
4409 * @returns {Roo.bootstrap.NavGroup} the navgroup
4411 get: function(navId) {
4412 if (typeof(this.groups[navId]) == 'undefined') {
4414 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416 return this.groups[navId] ;
4431 * @class Roo.bootstrap.NavItem
4432 * @extends Roo.bootstrap.Component
4433 * Bootstrap Navbar.NavItem class
4434 * @cfg {String} href link to
4435 * @cfg {String} html content of button
4436 * @cfg {String} badge text inside badge
4437 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438 * @cfg {String} glyphicon name of glyphicon
4439 * @cfg {String} icon name of font awesome icon
4440 * @cfg {Boolean} active Is item active
4441 * @cfg {Boolean} disabled Is item disabled
4443 * @cfg {Boolean} preventDefault (true | false) default false
4444 * @cfg {String} tabId the tab that this item activates.
4445 * @cfg {String} tagtype (a|span) render as a href or span?
4446 * @cfg {Boolean} animateRef (true|false) link to element default false
4449 * Create a new Navbar Item
4450 * @param {Object} config The config object
4452 Roo.bootstrap.NavItem = function(config){
4453 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4458 * The raw click event for the entire grid.
4459 * @param {Roo.EventObject} e
4464 * Fires when the active item active state changes
4465 * @param {Roo.bootstrap.NavItem} this
4466 * @param {boolean} state the new state
4472 * Fires when scroll to element
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {Object} options
4475 * @param {Roo.EventObject} e
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4491 preventDefault : false,
4498 getAutoCreate : function(){
4507 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509 if (this.disabled) {
4510 cfg.cls += ' disabled';
4513 if (this.href || this.html || this.glyphicon || this.icon) {
4517 href : this.href || "#",
4518 html: this.html || ''
4523 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4526 if(this.glyphicon) {
4527 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4532 cfg.cn[0].html += " <span class='caret'></span>";
4536 if (this.badge !== '') {
4538 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4546 initEvents: function()
4548 if (typeof (this.menu) != 'undefined') {
4549 this.menu.parentType = this.xtype;
4550 this.menu.triggerEl = this.el;
4551 this.menu = this.addxtype(Roo.apply({}, this.menu));
4554 this.el.select('a',true).on('click', this.onClick, this);
4556 if(this.tagtype == 'span'){
4557 this.el.select('span',true).on('click', this.onClick, this);
4560 // at this point parent should be available..
4561 this.parent().register(this);
4564 onClick : function(e)
4566 if (e.getTarget('.dropdown-menu-item')) {
4567 // did you click on a menu itemm.... - then don't trigger onclick..
4572 this.preventDefault ||
4575 Roo.log("NavItem - prevent Default?");
4579 if (this.disabled) {
4583 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584 if (tg && tg.transition) {
4585 Roo.log("waiting for the transitionend");
4591 //Roo.log("fire event clicked");
4592 if(this.fireEvent('click', this, e) === false){
4596 if(this.tagtype == 'span'){
4600 //Roo.log(this.href);
4601 var ael = this.el.select('a',true).first();
4604 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607 return; // ignore... - it's a 'hash' to another page.
4609 Roo.log("NavItem - prevent Default?");
4611 this.scrollToElement(e);
4615 var p = this.parent();
4617 if (['tabs','pills'].indexOf(p.type)!==-1) {
4618 if (typeof(p.setActiveItem) !== 'undefined') {
4619 p.setActiveItem(this);
4623 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625 // remove the collapsed menu expand...
4626 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4630 isActive: function () {
4633 setActive : function(state, fire, is_was_active)
4635 if (this.active && !state && this.navId) {
4636 this.was_active = true;
4637 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639 nv.clearWasActive(this);
4643 this.active = state;
4646 this.el.removeClass('active');
4647 } else if (!this.el.hasClass('active')) {
4648 this.el.addClass('active');
4651 this.fireEvent('changed', this, state);
4654 // show a panel if it's registered and related..
4656 if (!this.navId || !this.tabId || !state || is_was_active) {
4660 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664 var pan = tg.getPanelByName(this.tabId);
4668 // if we can not flip to new panel - go back to old nav highlight..
4669 if (false == tg.showPanel(pan)) {
4670 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672 var onav = nv.getWasActive();
4674 onav.setActive(true, false, true);
4683 // this should not be here...
4684 setDisabled : function(state)
4686 this.disabled = state;
4688 this.el.removeClass('disabled');
4689 } else if (!this.el.hasClass('disabled')) {
4690 this.el.addClass('disabled');
4696 * Fetch the element to display the tooltip on.
4697 * @return {Roo.Element} defaults to this.el
4699 tooltipEl : function()
4701 return this.el.select('' + this.tagtype + '', true).first();
4704 scrollToElement : function(e)
4706 var c = document.body;
4709 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712 c = document.documentElement;
4715 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4721 var o = target.calcOffsetsTo(c);
4728 this.fireEvent('scrollto', this, options, e);
4730 Roo.get(c).scrollTo('top', options.value, true);
4743 * <span> icon </span>
4744 * <span> text </span>
4745 * <span>badge </span>
4749 * @class Roo.bootstrap.NavSidebarItem
4750 * @extends Roo.bootstrap.NavItem
4751 * Bootstrap Navbar.NavSidebarItem class
4752 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753 * {Boolean} open is the menu open
4754 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756 * {String} buttonSize (sm|md|lg)the extra classes for the button
4757 * {Boolean} showArrow show arrow next to the text (default true)
4759 * Create a new Navbar Button
4760 * @param {Object} config The config object
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4768 * The raw click event for the entire grid.
4769 * @param {Roo.EventObject} e
4774 * Fires when the active item active state changes
4775 * @param {Roo.bootstrap.NavSidebarItem} this
4776 * @param {boolean} state the new state
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4786 badgeWeight : 'default',
4792 buttonWeight : 'default',
4798 getAutoCreate : function(){
4803 href : this.href || '#',
4809 if(this.buttonView){
4812 href : this.href || '#',
4813 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4826 cfg.cls += ' active';
4829 if (this.disabled) {
4830 cfg.cls += ' disabled';
4833 cfg.cls += ' open x-open';
4836 if (this.glyphicon || this.icon) {
4837 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4838 a.cn.push({ tag : 'i', cls : c }) ;
4841 if(!this.buttonView){
4844 html : this.html || ''
4851 if (this.badge !== '') {
4852 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4858 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4861 a.cls += ' dropdown-toggle treeview' ;
4867 initEvents : function()
4869 if (typeof (this.menu) != 'undefined') {
4870 this.menu.parentType = this.xtype;
4871 this.menu.triggerEl = this.el;
4872 this.menu = this.addxtype(Roo.apply({}, this.menu));
4875 this.el.on('click', this.onClick, this);
4877 if(this.badge !== ''){
4878 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4883 onClick : function(e)
4890 if(this.preventDefault){
4894 this.fireEvent('click', this);
4897 disable : function()
4899 this.setDisabled(true);
4904 this.setDisabled(false);
4907 setDisabled : function(state)
4909 if(this.disabled == state){
4913 this.disabled = state;
4916 this.el.addClass('disabled');
4920 this.el.removeClass('disabled');
4925 setActive : function(state)
4927 if(this.active == state){
4931 this.active = state;
4934 this.el.addClass('active');
4938 this.el.removeClass('active');
4943 isActive: function ()
4948 setBadge : function(str)
4954 this.badgeEl.dom.innerHTML = str;
4971 * @class Roo.bootstrap.Row
4972 * @extends Roo.bootstrap.Component
4973 * Bootstrap Row class (contains columns...)
4977 * @param {Object} config The config object
4980 Roo.bootstrap.Row = function(config){
4981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4986 getAutoCreate : function(){
5005 * @class Roo.bootstrap.Element
5006 * @extends Roo.bootstrap.Component
5007 * Bootstrap Element class
5008 * @cfg {String} html contents of the element
5009 * @cfg {String} tag tag of the element
5010 * @cfg {String} cls class of the element
5011 * @cfg {Boolean} preventDefault (true|false) default false
5012 * @cfg {Boolean} clickable (true|false) default false
5015 * Create a new Element
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Element = function(config){
5020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5026 * When a element is chick
5027 * @param {Roo.bootstrap.Element} this
5028 * @param {Roo.EventObject} e
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5039 preventDefault: false,
5042 getAutoCreate : function(){
5046 // cls: this.cls, double assign in parent class Component.js :: onRender
5053 initEvents: function()
5055 Roo.bootstrap.Element.superclass.initEvents.call(this);
5058 this.el.on('click', this.onClick, this);
5063 onClick : function(e)
5065 if(this.preventDefault){
5069 this.fireEvent('click', this, e);
5072 getValue : function()
5074 return this.el.dom.innerHTML;
5077 setValue : function(value)
5079 this.el.dom.innerHTML = value;
5094 * @class Roo.bootstrap.Pagination
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Pagination class
5097 * @cfg {String} size xs | sm | md | lg
5098 * @cfg {Boolean} inverse false | true
5101 * Create a new Pagination
5102 * @param {Object} config The config object
5105 Roo.bootstrap.Pagination = function(config){
5106 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5121 cfg.cls += ' inverse';
5127 cfg.cls += " " + this.cls;
5145 * @class Roo.bootstrap.PaginationItem
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap PaginationItem class
5148 * @cfg {String} html text
5149 * @cfg {String} href the link
5150 * @cfg {Boolean} preventDefault (true | false) default true
5151 * @cfg {Boolean} active (true | false) default false
5152 * @cfg {Boolean} disabled default false
5156 * Create a new PaginationItem
5157 * @param {Object} config The config object
5161 Roo.bootstrap.PaginationItem = function(config){
5162 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5167 * The raw click event for the entire grid.
5168 * @param {Roo.EventObject} e
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5178 preventDefault: true,
5183 getAutoCreate : function(){
5189 href : this.href ? this.href : '#',
5190 html : this.html ? this.html : ''
5200 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5210 initEvents: function() {
5212 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 Roo.log('PaginationItem on click ');
5218 if(this.preventDefault){
5226 this.fireEvent('click', this, e);
5242 * @class Roo.bootstrap.Slider
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Slider class
5247 * Create a new Slider
5248 * @param {Object} config The config object
5251 Roo.bootstrap.Slider = function(config){
5252 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5261 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5277 * Ext JS Library 1.1.1
5278 * Copyright(c) 2006-2007, Ext JS, LLC.
5280 * Originally Released Under LGPL - original licence link has changed is not relivant.
5283 * <script type="text/javascript">
5288 * @class Roo.grid.ColumnModel
5289 * @extends Roo.util.Observable
5290 * This is the default implementation of a ColumnModel used by the Grid. It defines
5291 * the columns in the grid.
5294 var colModel = new Roo.grid.ColumnModel([
5295 {header: "Ticker", width: 60, sortable: true, locked: true},
5296 {header: "Company Name", width: 150, sortable: true},
5297 {header: "Market Cap.", width: 100, sortable: true},
5298 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299 {header: "Employees", width: 100, sortable: true, resizable: false}
5304 * The config options listed for this class are options which may appear in each
5305 * individual column definition.
5306 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308 * @param {Object} config An Array of column config objects. See this class's
5309 * config objects for details.
5311 Roo.grid.ColumnModel = function(config){
5313 * The config passed into the constructor
5315 this.config = config;
5318 // if no id, create one
5319 // if the column does not have a dataIndex mapping,
5320 // map it to the order it is in the config
5321 for(var i = 0, len = config.length; i < len; i++){
5323 if(typeof c.dataIndex == "undefined"){
5326 if(typeof c.renderer == "string"){
5327 c.renderer = Roo.util.Format[c.renderer];
5329 if(typeof c.id == "undefined"){
5332 if(c.editor && c.editor.xtype){
5333 c.editor = Roo.factory(c.editor, Roo.grid);
5335 if(c.editor && c.editor.isFormField){
5336 c.editor = new Roo.grid.GridEditor(c.editor);
5338 this.lookup[c.id] = c;
5342 * The width of columns which have no width specified (defaults to 100)
5345 this.defaultWidth = 100;
5348 * Default sortable of columns which have no sortable specified (defaults to false)
5351 this.defaultSortable = false;
5355 * @event widthchange
5356 * Fires when the width of a column changes.
5357 * @param {ColumnModel} this
5358 * @param {Number} columnIndex The column index
5359 * @param {Number} newWidth The new width
5361 "widthchange": true,
5363 * @event headerchange
5364 * Fires when the text of a header changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newText The new header text
5369 "headerchange": true,
5371 * @event hiddenchange
5372 * Fires when a column is hidden or "unhidden".
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Boolean} hidden true if hidden, false otherwise
5377 "hiddenchange": true,
5379 * @event columnmoved
5380 * Fires when a column is moved.
5381 * @param {ColumnModel} this
5382 * @param {Number} oldIndex
5383 * @param {Number} newIndex
5385 "columnmoved" : true,
5387 * @event columlockchange
5388 * Fires when a column's locked state is changed
5389 * @param {ColumnModel} this
5390 * @param {Number} colIndex
5391 * @param {Boolean} locked true if locked
5393 "columnlockchange" : true
5395 Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399 * @cfg {String} header The header text to display in the Grid view.
5402 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404 * specified, the column's index is used as an index into the Record's data Array.
5407 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5411 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412 * Defaults to the value of the {@link #defaultSortable} property.
5413 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5416 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5419 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5422 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5425 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5428 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5434 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5437 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5440 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5443 * @cfg {String} cursor (Optional)
5446 * @cfg {String} tooltip (Optional)
5449 * @cfg {Number} xs (Optional)
5452 * @cfg {Number} sm (Optional)
5455 * @cfg {Number} md (Optional)
5458 * @cfg {Number} lg (Optional)
5461 * Returns the id of the column at the specified index.
5462 * @param {Number} index The column index
5463 * @return {String} the id
5465 getColumnId : function(index){
5466 return this.config[index].id;
5470 * Returns the column for a specified id.
5471 * @param {String} id The column id
5472 * @return {Object} the column
5474 getColumnById : function(id){
5475 return this.lookup[id];
5480 * Returns the column for a specified dataIndex.
5481 * @param {String} dataIndex The column dataIndex
5482 * @return {Object|Boolean} the column or false if not found
5484 getColumnByDataIndex: function(dataIndex){
5485 var index = this.findColumnIndex(dataIndex);
5486 return index > -1 ? this.config[index] : false;
5490 * Returns the index for a specified column id.
5491 * @param {String} id The column id
5492 * @return {Number} the index, or -1 if not found
5494 getIndexById : function(id){
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(this.config[i].id == id){
5504 * Returns the index for a specified column dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Number} the index, or -1 if not found
5509 findColumnIndex : function(dataIndex){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(this.config[i].dataIndex == dataIndex){
5519 moveColumn : function(oldIndex, newIndex){
5520 var c = this.config[oldIndex];
5521 this.config.splice(oldIndex, 1);
5522 this.config.splice(newIndex, 0, c);
5523 this.dataMap = null;
5524 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5527 isLocked : function(colIndex){
5528 return this.config[colIndex].locked === true;
5531 setLocked : function(colIndex, value, suppressEvent){
5532 if(this.isLocked(colIndex) == value){
5535 this.config[colIndex].locked = value;
5537 this.fireEvent("columnlockchange", this, colIndex, value);
5541 getTotalLockedWidth : function(){
5543 for(var i = 0; i < this.config.length; i++){
5544 if(this.isLocked(i) && !this.isHidden(i)){
5545 this.totalWidth += this.getColumnWidth(i);
5551 getLockedCount : function(){
5552 for(var i = 0, len = this.config.length; i < len; i++){
5553 if(!this.isLocked(i)){
5558 return this.config.length;
5562 * Returns the number of columns.
5565 getColumnCount : function(visibleOnly){
5566 if(visibleOnly === true){
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(!this.isHidden(i)){
5575 return this.config.length;
5579 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580 * @param {Function} fn
5581 * @param {Object} scope (optional)
5582 * @return {Array} result
5584 getColumnsBy : function(fn, scope){
5586 for(var i = 0, len = this.config.length; i < len; i++){
5587 var c = this.config[i];
5588 if(fn.call(scope||this, c, i) === true){
5596 * Returns true if the specified column is sortable.
5597 * @param {Number} col The column index
5600 isSortable : function(col){
5601 if(typeof this.config[col].sortable == "undefined"){
5602 return this.defaultSortable;
5604 return this.config[col].sortable;
5608 * Returns the rendering (formatting) function defined for the column.
5609 * @param {Number} col The column index.
5610 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612 getRenderer : function(col){
5613 if(!this.config[col].renderer){
5614 return Roo.grid.ColumnModel.defaultRenderer;
5616 return this.config[col].renderer;
5620 * Sets the rendering (formatting) function for a column.
5621 * @param {Number} col The column index
5622 * @param {Function} fn The function to use to process the cell's raw data
5623 * to return HTML markup for the grid view. The render function is called with
5624 * the following parameters:<ul>
5625 * <li>Data value.</li>
5626 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627 * <li>css A CSS style string to apply to the table cell.</li>
5628 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630 * <li>Row index</li>
5631 * <li>Column index</li>
5632 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634 setRenderer : function(col, fn){
5635 this.config[col].renderer = fn;
5639 * Returns the width for the specified column.
5640 * @param {Number} col The column index
5643 getColumnWidth : function(col){
5644 return this.config[col].width * 1 || this.defaultWidth;
5648 * Sets the width for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} width The new width
5652 setColumnWidth : function(col, width, suppressEvent){
5653 this.config[col].width = width;
5654 this.totalWidth = null;
5656 this.fireEvent("widthchange", this, col, width);
5661 * Returns the total width of all columns.
5662 * @param {Boolean} includeHidden True to include hidden column widths
5665 getTotalWidth : function(includeHidden){
5666 if(!this.totalWidth){
5667 this.totalWidth = 0;
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(includeHidden || !this.isHidden(i)){
5670 this.totalWidth += this.getColumnWidth(i);
5674 return this.totalWidth;
5678 * Returns the header for the specified column.
5679 * @param {Number} col The column index
5682 getColumnHeader : function(col){
5683 return this.config[col].header;
5687 * Sets the header for a column.
5688 * @param {Number} col The column index
5689 * @param {String} header The new header
5691 setColumnHeader : function(col, header){
5692 this.config[col].header = header;
5693 this.fireEvent("headerchange", this, col, header);
5697 * Returns the tooltip for the specified column.
5698 * @param {Number} col The column index
5701 getColumnTooltip : function(col){
5702 return this.config[col].tooltip;
5705 * Sets the tooltip for a column.
5706 * @param {Number} col The column index
5707 * @param {String} tooltip The new tooltip
5709 setColumnTooltip : function(col, tooltip){
5710 this.config[col].tooltip = tooltip;
5714 * Returns the dataIndex for the specified column.
5715 * @param {Number} col The column index
5718 getDataIndex : function(col){
5719 return this.config[col].dataIndex;
5723 * Sets the dataIndex for a column.
5724 * @param {Number} col The column index
5725 * @param {Number} dataIndex The new dataIndex
5727 setDataIndex : function(col, dataIndex){
5728 this.config[col].dataIndex = dataIndex;
5734 * Returns true if the cell is editable.
5735 * @param {Number} colIndex The column index
5736 * @param {Number} rowIndex The row index - this is nto actually used..?
5739 isCellEditable : function(colIndex, rowIndex){
5740 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744 * Returns the editor defined for the cell/column.
5745 * return false or null to disable editing.
5746 * @param {Number} colIndex The column index
5747 * @param {Number} rowIndex The row index
5750 getCellEditor : function(colIndex, rowIndex){
5751 return this.config[colIndex].editor;
5755 * Sets if a column is editable.
5756 * @param {Number} col The column index
5757 * @param {Boolean} editable True if the column is editable
5759 setEditable : function(col, editable){
5760 this.config[col].editable = editable;
5765 * Returns true if the column is hidden.
5766 * @param {Number} colIndex The column index
5769 isHidden : function(colIndex){
5770 return this.config[colIndex].hidden;
5775 * Returns true if the column width cannot be changed
5777 isFixed : function(colIndex){
5778 return this.config[colIndex].fixed;
5782 * Returns true if the column can be resized
5785 isResizable : function(colIndex){
5786 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5789 * Sets if a column is hidden.
5790 * @param {Number} colIndex The column index
5791 * @param {Boolean} hidden True if the column is hidden
5793 setHidden : function(colIndex, hidden){
5794 this.config[colIndex].hidden = hidden;
5795 this.totalWidth = null;
5796 this.fireEvent("hiddenchange", this, colIndex, hidden);
5800 * Sets the editor for a column.
5801 * @param {Number} col The column index
5802 * @param {Object} editor The editor object
5804 setEditor : function(col, editor){
5805 this.config[col].editor = editor;
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 if(typeof value == "object") {
5814 if(typeof value == "string" && value.length < 1){
5818 return String.format("{0}", value);
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5825 * Ext JS Library 1.1.1
5826 * Copyright(c) 2006-2007, Ext JS, LLC.
5828 * Originally Released Under LGPL - original licence link has changed is not relivant.
5831 * <script type="text/javascript">
5835 * @class Roo.LoadMask
5836 * A simple utility class for generically masking elements while loading data. If the element being masked has
5837 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5839 * element's UpdateManager load indicator and will be destroyed after the initial load.
5841 * Create a new LoadMask
5842 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843 * @param {Object} config The config object
5845 Roo.LoadMask = function(el, config){
5846 this.el = Roo.get(el);
5847 Roo.apply(this, config);
5849 this.store.on('beforeload', this.onBeforeLoad, this);
5850 this.store.on('load', this.onLoad, this);
5851 this.store.on('loadexception', this.onLoadException, this);
5852 this.removeMask = false;
5854 var um = this.el.getUpdateManager();
5855 um.showLoadIndicator = false; // disable the default indicator
5856 um.on('beforeupdate', this.onBeforeLoad, this);
5857 um.on('update', this.onLoad, this);
5858 um.on('failure', this.onLoad, this);
5859 this.removeMask = true;
5863 Roo.LoadMask.prototype = {
5865 * @cfg {Boolean} removeMask
5866 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5871 * The text to display in a centered loading message box (defaults to 'Loading...')
5875 * @cfg {String} msgCls
5876 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878 msgCls : 'x-mask-loading',
5881 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5887 * Disables the mask to prevent it from being displayed
5889 disable : function(){
5890 this.disabled = true;
5894 * Enables the mask so that it can be displayed
5896 enable : function(){
5897 this.disabled = false;
5900 onLoadException : function()
5904 if (typeof(arguments[3]) != 'undefined') {
5905 Roo.MessageBox.alert("Error loading",arguments[3]);
5909 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5917 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5922 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926 onBeforeLoad : function(){
5928 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5933 destroy : function(){
5935 this.store.un('beforeload', this.onBeforeLoad, this);
5936 this.store.un('load', this.onLoad, this);
5937 this.store.un('loadexception', this.onLoadException, this);
5939 var um = this.el.getUpdateManager();
5940 um.un('beforeupdate', this.onBeforeLoad, this);
5941 um.un('update', this.onLoad, this);
5942 um.un('failure', this.onLoad, this);
5953 * @class Roo.bootstrap.Table
5954 * @extends Roo.bootstrap.Component
5955 * Bootstrap Table class
5956 * @cfg {String} cls table class
5957 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958 * @cfg {String} bgcolor Specifies the background color for a table
5959 * @cfg {Number} border Specifies whether the table cells should have borders or not
5960 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961 * @cfg {Number} cellspacing Specifies the space between cells
5962 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964 * @cfg {String} sortable Specifies that the table should be sortable
5965 * @cfg {String} summary Specifies a summary of the content of a table
5966 * @cfg {Number} width Specifies the width of a table
5967 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969 * @cfg {boolean} striped Should the rows be alternative striped
5970 * @cfg {boolean} bordered Add borders to the table
5971 * @cfg {boolean} hover Add hover highlighting
5972 * @cfg {boolean} condensed Format condensed
5973 * @cfg {boolean} responsive Format condensed
5974 * @cfg {Boolean} loadMask (true|false) default false
5975 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977 * @cfg {Boolean} rowSelection (true|false) default false
5978 * @cfg {Boolean} cellSelection (true|false) default false
5979 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5981 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5982 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5986 * Create a new Table
5987 * @param {Object} config The config object
5990 Roo.bootstrap.Table = function(config){
5991 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5996 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003 this.sm.grid = this;
6004 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005 this.sm = this.selModel;
6006 this.sm.xmodule = this.xmodule || false;
6009 if (this.cm && typeof(this.cm.config) == 'undefined') {
6010 this.colModel = new Roo.grid.ColumnModel(this.cm);
6011 this.cm = this.colModel;
6012 this.cm.xmodule = this.xmodule || false;
6015 this.store= Roo.factory(this.store, Roo.data);
6016 this.ds = this.store;
6017 this.ds.xmodule = this.xmodule || false;
6020 if (this.footer && this.store) {
6021 this.footer.dataSource = this.ds;
6022 this.footer = Roo.factory(this.footer);
6029 * Fires when a cell is clicked
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Roo.Element} el
6032 * @param {Number} rowIndex
6033 * @param {Number} columnIndex
6034 * @param {Roo.EventObject} e
6038 * @event celldblclick
6039 * Fires when a cell is double clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Roo.Element} el
6042 * @param {Number} rowIndex
6043 * @param {Number} columnIndex
6044 * @param {Roo.EventObject} e
6046 "celldblclick" : true,
6049 * Fires when a row is clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Roo.Element} el
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6057 * @event rowdblclick
6058 * Fires when a row is double clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Roo.Element} el
6061 * @param {Number} rowIndex
6062 * @param {Roo.EventObject} e
6064 "rowdblclick" : true,
6067 * Fires when a mouseover occur
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Roo.Element} el
6070 * @param {Number} rowIndex
6071 * @param {Number} columnIndex
6072 * @param {Roo.EventObject} e
6077 * Fires when a mouseout occur
6078 * @param {Roo.bootstrap.Table} this
6079 * @param {Roo.Element} el
6080 * @param {Number} rowIndex
6081 * @param {Number} columnIndex
6082 * @param {Roo.EventObject} e
6087 * Fires when a row is rendered, so you can change add a style to it.
6088 * @param {Roo.bootstrap.Table} this
6089 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6093 * @event rowsrendered
6094 * Fires when all the rows have been rendered
6095 * @param {Roo.bootstrap.Table} this
6097 'rowsrendered' : true,
6099 * @event contextmenu
6100 * The raw contextmenu event for the entire grid.
6101 * @param {Roo.EventObject} e
6103 "contextmenu" : true,
6105 * @event rowcontextmenu
6106 * Fires when a row is right clicked
6107 * @param {Roo.bootstrap.Table} this
6108 * @param {Number} rowIndex
6109 * @param {Roo.EventObject} e
6111 "rowcontextmenu" : true,
6113 * @event cellcontextmenu
6114 * Fires when a cell is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Number} cellIndex
6118 * @param {Roo.EventObject} e
6120 "cellcontextmenu" : true,
6122 * @event headercontextmenu
6123 * Fires when a header is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} columnIndex
6126 * @param {Roo.EventObject} e
6128 "headercontextmenu" : true
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6158 rowSelection : false,
6159 cellSelection : false,
6162 // Roo.Element - the tbody
6164 // Roo.Element - thead element
6167 container: false, // used by gridpanel...
6173 auto_hide_footer : false,
6175 getAutoCreate : function()
6177 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6184 if (this.scrollBody) {
6185 cfg.cls += ' table-body-fixed';
6188 cfg.cls += ' table-striped';
6192 cfg.cls += ' table-hover';
6194 if (this.bordered) {
6195 cfg.cls += ' table-bordered';
6197 if (this.condensed) {
6198 cfg.cls += ' table-condensed';
6200 if (this.responsive) {
6201 cfg.cls += ' table-responsive';
6205 cfg.cls+= ' ' +this.cls;
6208 // this lot should be simplifed...
6221 ].forEach(function(k) {
6229 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6232 if(this.store || this.cm){
6233 if(this.headerShow){
6234 cfg.cn.push(this.renderHeader());
6237 cfg.cn.push(this.renderBody());
6239 if(this.footerShow){
6240 cfg.cn.push(this.renderFooter());
6242 // where does this come from?
6243 //cfg.cls+= ' TableGrid';
6246 return { cn : [ cfg ] };
6249 initEvents : function()
6251 if(!this.store || !this.cm){
6254 if (this.selModel) {
6255 this.selModel.initEvents();
6259 //Roo.log('initEvents with ds!!!!');
6261 this.mainBody = this.el.select('tbody', true).first();
6262 this.mainHead = this.el.select('thead', true).first();
6263 this.mainFoot = this.el.select('tfoot', true).first();
6269 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270 e.on('click', _this.sort, _this);
6273 this.mainBody.on("click", this.onClick, this);
6274 this.mainBody.on("dblclick", this.onDblClick, this);
6276 // why is this done????? = it breaks dialogs??
6277 //this.parent().el.setStyle('position', 'relative');
6281 this.footer.parentId = this.id;
6282 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6285 this.el.select('tfoot tr td').first().addClass('hide');
6290 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6293 this.store.on('load', this.onLoad, this);
6294 this.store.on('beforeload', this.onBeforeLoad, this);
6295 this.store.on('update', this.onUpdate, this);
6296 this.store.on('add', this.onAdd, this);
6297 this.store.on("clear", this.clear, this);
6299 this.el.on("contextmenu", this.onContextMenu, this);
6301 this.mainBody.on('scroll', this.onBodyScroll, this);
6303 this.cm.on("headerchange", this.onHeaderChange, this);
6305 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309 onContextMenu : function(e, t)
6311 this.processEvent("contextmenu", e);
6314 processEvent : function(name, e)
6316 if (name != 'touchstart' ) {
6317 this.fireEvent(name, e);
6320 var t = e.getTarget();
6322 var cell = Roo.get(t);
6328 if(cell.findParent('tfoot', false, true)){
6332 if(cell.findParent('thead', false, true)){
6334 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335 cell = Roo.get(t).findParent('th', false, true);
6337 Roo.log("failed to find th in thead?");
6338 Roo.log(e.getTarget());
6343 var cellIndex = cell.dom.cellIndex;
6345 var ename = name == 'touchstart' ? 'click' : name;
6346 this.fireEvent("header" + ename, this, cellIndex, e);
6351 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352 cell = Roo.get(t).findParent('td', false, true);
6354 Roo.log("failed to find th in tbody?");
6355 Roo.log(e.getTarget());
6360 var row = cell.findParent('tr', false, true);
6361 var cellIndex = cell.dom.cellIndex;
6362 var rowIndex = row.dom.rowIndex - 1;
6366 this.fireEvent("row" + name, this, rowIndex, e);
6370 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6376 onMouseover : function(e, el)
6378 var cell = Roo.get(el);
6384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385 cell = cell.findParent('td', false, true);
6388 var row = cell.findParent('tr', false, true);
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = row.dom.rowIndex - 1; // start from 0
6392 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396 onMouseout : function(e, el)
6398 var cell = Roo.get(el);
6404 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405 cell = cell.findParent('td', false, true);
6408 var row = cell.findParent('tr', false, true);
6409 var cellIndex = cell.dom.cellIndex;
6410 var rowIndex = row.dom.rowIndex - 1; // start from 0
6412 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416 onClick : function(e, el)
6418 var cell = Roo.get(el);
6420 if(!cell || (!this.cellSelection && !this.rowSelection)){
6424 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425 cell = cell.findParent('td', false, true);
6428 if(!cell || typeof(cell) == 'undefined'){
6432 var row = cell.findParent('tr', false, true);
6434 if(!row || typeof(row) == 'undefined'){
6438 var cellIndex = cell.dom.cellIndex;
6439 var rowIndex = this.getRowIndex(row);
6441 // why??? - should these not be based on SelectionModel?
6442 if(this.cellSelection){
6443 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6446 if(this.rowSelection){
6447 this.fireEvent('rowclick', this, row, rowIndex, e);
6453 onDblClick : function(e,el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 if(this.cellSelection){
6479 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6482 if(this.rowSelection){
6483 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487 sort : function(e,el)
6489 var col = Roo.get(el);
6491 if(!col.hasClass('sortable')){
6495 var sort = col.attr('sort');
6498 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502 this.store.sortInfo = {field : sort, direction : dir};
6505 Roo.log("calling footer first");
6506 this.footer.onClick('first');
6509 this.store.load({ params : { start : 0 } });
6513 renderHeader : function()
6521 this.totalWidth = 0;
6523 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525 var config = cm.config[i];
6529 cls : 'x-hcol-' + i,
6531 html: cm.getColumnHeader(i)
6536 if(typeof(config.sortable) != 'undefined' && config.sortable){
6538 c.html = '<i class="glyphicon"></i>' + c.html;
6541 if(typeof(config.lgHeader) != 'undefined'){
6542 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6545 if(typeof(config.mdHeader) != 'undefined'){
6546 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6549 if(typeof(config.smHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6553 if(typeof(config.xsHeader) != 'undefined'){
6554 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6561 if(typeof(config.tooltip) != 'undefined'){
6562 c.tooltip = config.tooltip;
6565 if(typeof(config.colspan) != 'undefined'){
6566 c.colspan = config.colspan;
6569 if(typeof(config.hidden) != 'undefined' && config.hidden){
6570 c.style += ' display:none;';
6573 if(typeof(config.dataIndex) != 'undefined'){
6574 c.sort = config.dataIndex;
6579 if(typeof(config.align) != 'undefined' && config.align.length){
6580 c.style += ' text-align:' + config.align + ';';
6583 if(typeof(config.width) != 'undefined'){
6584 c.style += ' width:' + config.width + 'px;';
6585 this.totalWidth += config.width;
6587 this.totalWidth += 100; // assume minimum of 100 per column?
6590 if(typeof(config.cls) != 'undefined'){
6591 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 c.cls += ' hidden-' + size;
6605 c.cls += ' col-' + size + '-' + config[size];
6615 renderBody : function()
6625 colspan : this.cm.getColumnCount()
6635 renderFooter : function()
6645 colspan : this.cm.getColumnCount()
6659 // Roo.log('ds onload');
6664 var ds = this.store;
6666 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668 if (_this.store.sortInfo) {
6670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671 e.select('i', true).addClass(['glyphicon-arrow-up']);
6674 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675 e.select('i', true).addClass(['glyphicon-arrow-down']);
6680 var tbody = this.mainBody;
6682 if(ds.getCount() > 0){
6683 ds.data.each(function(d,rowIndex){
6684 var row = this.renderRow(cm, ds, rowIndex);
6686 tbody.createChild(row);
6690 if(row.cellObjects.length){
6691 Roo.each(row.cellObjects, function(r){
6692 _this.renderCellObject(r);
6699 var tfoot = this.el.select('tfoot', true).first();
6701 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705 var total = this.ds.getTotalCount();
6707 if(this.footer.pageSize < total){
6708 this.mainFoot.show();
6712 Roo.each(this.el.select('tbody td', true).elements, function(e){
6713 e.on('mouseover', _this.onMouseover, _this);
6716 Roo.each(this.el.select('tbody td', true).elements, function(e){
6717 e.on('mouseout', _this.onMouseout, _this);
6719 this.fireEvent('rowsrendered', this);
6725 onUpdate : function(ds,record)
6727 this.refreshRow(record);
6731 onRemove : function(ds, record, index, isUpdate){
6732 if(isUpdate !== true){
6733 this.fireEvent("beforerowremoved", this, index, record);
6735 var bt = this.mainBody.dom;
6737 var rows = this.el.select('tbody > tr', true).elements;
6739 if(typeof(rows[index]) != 'undefined'){
6740 bt.removeChild(rows[index].dom);
6743 // if(bt.rows[index]){
6744 // bt.removeChild(bt.rows[index]);
6747 if(isUpdate !== true){
6748 //this.stripeRows(index);
6749 //this.syncRowHeights(index, index);
6751 this.fireEvent("rowremoved", this, index, record);
6755 onAdd : function(ds, records, rowIndex)
6757 //Roo.log('on Add called');
6758 // - note this does not handle multiple adding very well..
6759 var bt = this.mainBody.dom;
6760 for (var i =0 ; i < records.length;i++) {
6761 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762 //Roo.log(records[i]);
6763 //Roo.log(this.store.getAt(rowIndex+i));
6764 this.insertRow(this.store, rowIndex + i, false);
6771 refreshRow : function(record){
6772 var ds = this.store, index;
6773 if(typeof record == 'number'){
6775 record = ds.getAt(index);
6777 index = ds.indexOf(record);
6779 this.insertRow(ds, index, true);
6781 this.onRemove(ds, record, index+1, true);
6783 //this.syncRowHeights(index, index);
6785 this.fireEvent("rowupdated", this, index, record);
6788 insertRow : function(dm, rowIndex, isUpdate){
6791 this.fireEvent("beforerowsinserted", this, rowIndex);
6793 //var s = this.getScrollState();
6794 var row = this.renderRow(this.cm, this.store, rowIndex);
6795 // insert before rowIndex..
6796 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800 if(row.cellObjects.length){
6801 Roo.each(row.cellObjects, function(r){
6802 _this.renderCellObject(r);
6807 this.fireEvent("rowsinserted", this, rowIndex);
6808 //this.syncRowHeights(firstRow, lastRow);
6809 //this.stripeRows(firstRow);
6816 getRowDom : function(rowIndex)
6818 var rows = this.el.select('tbody > tr', true).elements;
6820 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6823 // returns the object tree for a tr..
6826 renderRow : function(cm, ds, rowIndex)
6828 var d = ds.getAt(rowIndex);
6832 cls : 'x-row-' + rowIndex,
6836 var cellObjects = [];
6838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839 var config = cm.config[i];
6841 var renderer = cm.getRenderer(i);
6845 if(typeof(renderer) !== 'undefined'){
6846 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849 // and are rendered into the cells after the row is rendered - using the id for the element.
6851 if(typeof(value) === 'object'){
6861 rowIndex : rowIndex,
6866 this.fireEvent('rowclass', this, rowcfg);
6870 cls : rowcfg.rowClass + ' x-col-' + i,
6872 html: (typeof(value) === 'object') ? '' : value
6879 if(typeof(config.colspan) != 'undefined'){
6880 td.colspan = config.colspan;
6883 if(typeof(config.hidden) != 'undefined' && config.hidden){
6884 td.style += ' display:none;';
6887 if(typeof(config.align) != 'undefined' && config.align.length){
6888 td.style += ' text-align:' + config.align + ';';
6890 if(typeof(config.valign) != 'undefined' && config.valign.length){
6891 td.style += ' vertical-align:' + config.valign + ';';
6894 if(typeof(config.width) != 'undefined'){
6895 td.style += ' width:' + config.width + 'px;';
6898 if(typeof(config.cursor) != 'undefined'){
6899 td.style += ' cursor:' + config.cursor + ';';
6902 if(typeof(config.cls) != 'undefined'){
6903 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6906 ['xs','sm','md','lg'].map(function(size){
6908 if(typeof(config[size]) == 'undefined'){
6912 if (!config[size]) { // 0 = hidden
6913 td.cls += ' hidden-' + size;
6917 td.cls += ' col-' + size + '-' + config[size];
6925 row.cellObjects = cellObjects;
6933 onBeforeLoad : function()
6942 this.el.select('tbody', true).first().dom.innerHTML = '';
6945 * Show or hide a row.
6946 * @param {Number} rowIndex to show or hide
6947 * @param {Boolean} state hide
6949 setRowVisibility : function(rowIndex, state)
6951 var bt = this.mainBody.dom;
6953 var rows = this.el.select('tbody > tr', true).elements;
6955 if(typeof(rows[rowIndex]) == 'undefined'){
6958 rows[rowIndex].dom.style.display = state ? '' : 'none';
6962 getSelectionModel : function(){
6964 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966 return this.selModel;
6969 * Render the Roo.bootstrap object from renderder
6971 renderCellObject : function(r)
6975 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977 var t = r.cfg.render(r.container);
6980 Roo.each(r.cfg.cn, function(c){
6982 container: t.getChildContainer(),
6985 _this.renderCellObject(child);
6990 getRowIndex : function(row)
6994 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7005 * Returns the grid's underlying element = used by panel.Grid
7006 * @return {Element} The element
7008 getGridEl : function(){
7012 * Forces a resize - used by panel.Grid
7013 * @return {Element} The element
7015 autoSize : function()
7017 //var ctr = Roo.get(this.container.dom.parentElement);
7018 var ctr = Roo.get(this.el.dom);
7020 var thd = this.getGridEl().select('thead',true).first();
7021 var tbd = this.getGridEl().select('tbody', true).first();
7022 var tfd = this.getGridEl().select('tfoot', true).first();
7024 var cw = ctr.getWidth();
7028 tbd.setSize(ctr.getWidth(),
7029 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7034 cw = Math.max(cw, this.totalWidth);
7035 this.getGridEl().select('tr',true).setWidth(cw);
7036 // resize 'expandable coloumn?
7038 return; // we doe not have a view in this design..
7041 onBodyScroll: function()
7043 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045 this.mainHead.setStyle({
7046 'position' : 'relative',
7047 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7053 var scrollHeight = this.mainBody.dom.scrollHeight;
7055 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057 var height = this.mainBody.getHeight();
7059 if(scrollHeight - height == scrollTop) {
7061 var total = this.ds.getTotalCount();
7063 if(this.footer.cursor + this.footer.pageSize < total){
7065 this.footer.ds.load({
7067 start : this.footer.cursor + this.footer.pageSize,
7068 limit : this.footer.pageSize
7078 onHeaderChange : function()
7080 var header = this.renderHeader();
7081 var table = this.el.select('table', true).first();
7083 this.mainHead.remove();
7084 this.mainHead = table.createChild(header, this.mainBody, false);
7087 onHiddenChange : function(colModel, colIndex, hidden)
7089 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092 this.CSS.updateRule(thSelector, "display", "");
7093 this.CSS.updateRule(tdSelector, "display", "");
7096 this.CSS.updateRule(thSelector, "display", "none");
7097 this.CSS.updateRule(tdSelector, "display", "none");
7100 this.onHeaderChange();
7104 setColumnWidth: function(col_index, width)
7106 // width = "md-2 xs-2..."
7107 if(!this.colModel.config[col_index]) {
7111 var w = width.split(" ");
7113 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7115 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7118 for(var j = 0; j < w.length; j++) {
7124 var size_cls = w[j].split("-");
7126 if(!Number.isInteger(size_cls[1] * 1)) {
7130 if(!this.colModel.config[col_index][size_cls[0]]) {
7134 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7138 h_row[0].classList.replace(
7139 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7140 "col-"+size_cls[0]+"-"+size_cls[1]
7143 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7146 for(var i = 0; i < rows.length; i++) {
7148 for(var j = 0; j < w.length; j++) {
7150 var size_cls = w[j].split("-");
7152 if(!Number.isInteger(size_cls[1] * 1)) {
7156 if(!this.colModel.config[col_index][size_cls[0]]) {
7160 Roo.log('returning..');
7162 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7166 rows[i].classList.replace(
7167 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7168 "col-"+size_cls[0]+"-"+size_cls[1]
7185 * @class Roo.bootstrap.TableCell
7186 * @extends Roo.bootstrap.Component
7187 * Bootstrap TableCell class
7188 * @cfg {String} html cell contain text
7189 * @cfg {String} cls cell class
7190 * @cfg {String} tag cell tag (td|th) default td
7191 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7192 * @cfg {String} align Aligns the content in a cell
7193 * @cfg {String} axis Categorizes cells
7194 * @cfg {String} bgcolor Specifies the background color of a cell
7195 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7196 * @cfg {Number} colspan Specifies the number of columns a cell should span
7197 * @cfg {String} headers Specifies one or more header cells a cell is related to
7198 * @cfg {Number} height Sets the height of a cell
7199 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7200 * @cfg {Number} rowspan Sets the number of rows a cell should span
7201 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7202 * @cfg {String} valign Vertical aligns the content in a cell
7203 * @cfg {Number} width Specifies the width of a cell
7206 * Create a new TableCell
7207 * @param {Object} config The config object
7210 Roo.bootstrap.TableCell = function(config){
7211 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7214 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7234 getAutoCreate : function(){
7235 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7255 cfg.align=this.align
7261 cfg.bgcolor=this.bgcolor
7264 cfg.charoff=this.charoff
7267 cfg.colspan=this.colspan
7270 cfg.headers=this.headers
7273 cfg.height=this.height
7276 cfg.nowrap=this.nowrap
7279 cfg.rowspan=this.rowspan
7282 cfg.scope=this.scope
7285 cfg.valign=this.valign
7288 cfg.width=this.width
7307 * @class Roo.bootstrap.TableRow
7308 * @extends Roo.bootstrap.Component
7309 * Bootstrap TableRow class
7310 * @cfg {String} cls row class
7311 * @cfg {String} align Aligns the content in a table row
7312 * @cfg {String} bgcolor Specifies a background color for a table row
7313 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7314 * @cfg {String} valign Vertical aligns the content in a table row
7317 * Create a new TableRow
7318 * @param {Object} config The config object
7321 Roo.bootstrap.TableRow = function(config){
7322 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7325 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7333 getAutoCreate : function(){
7334 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7344 cfg.align = this.align;
7347 cfg.bgcolor = this.bgcolor;
7350 cfg.charoff = this.charoff;
7353 cfg.valign = this.valign;
7371 * @class Roo.bootstrap.TableBody
7372 * @extends Roo.bootstrap.Component
7373 * Bootstrap TableBody class
7374 * @cfg {String} cls element class
7375 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7376 * @cfg {String} align Aligns the content inside the element
7377 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7378 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7381 * Create a new TableBody
7382 * @param {Object} config The config object
7385 Roo.bootstrap.TableBody = function(config){
7386 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7389 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7397 getAutoCreate : function(){
7398 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7412 cfg.align = this.align;
7415 cfg.charoff = this.charoff;
7418 cfg.valign = this.valign;
7425 // initEvents : function()
7432 // this.store = Roo.factory(this.store, Roo.data);
7433 // this.store.on('load', this.onLoad, this);
7435 // this.store.load();
7439 // onLoad: function ()
7441 // this.fireEvent('load', this);
7451 * Ext JS Library 1.1.1
7452 * Copyright(c) 2006-2007, Ext JS, LLC.
7454 * Originally Released Under LGPL - original licence link has changed is not relivant.
7457 * <script type="text/javascript">
7460 // as we use this in bootstrap.
7461 Roo.namespace('Roo.form');
7463 * @class Roo.form.Action
7464 * Internal Class used to handle form actions
7466 * @param {Roo.form.BasicForm} el The form element or its id
7467 * @param {Object} config Configuration options
7472 // define the action interface
7473 Roo.form.Action = function(form, options){
7475 this.options = options || {};
7478 * Client Validation Failed
7481 Roo.form.Action.CLIENT_INVALID = 'client';
7483 * Server Validation Failed
7486 Roo.form.Action.SERVER_INVALID = 'server';
7488 * Connect to Server Failed
7491 Roo.form.Action.CONNECT_FAILURE = 'connect';
7493 * Reading Data from Server Failed
7496 Roo.form.Action.LOAD_FAILURE = 'load';
7498 Roo.form.Action.prototype = {
7500 failureType : undefined,
7501 response : undefined,
7505 run : function(options){
7510 success : function(response){
7515 handleResponse : function(response){
7519 // default connection failure
7520 failure : function(response){
7522 this.response = response;
7523 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7524 this.form.afterAction(this, false);
7527 processResponse : function(response){
7528 this.response = response;
7529 if(!response.responseText){
7532 this.result = this.handleResponse(response);
7536 // utility functions used internally
7537 getUrl : function(appendParams){
7538 var url = this.options.url || this.form.url || this.form.el.dom.action;
7540 var p = this.getParams();
7542 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7548 getMethod : function(){
7549 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7552 getParams : function(){
7553 var bp = this.form.baseParams;
7554 var p = this.options.params;
7556 if(typeof p == "object"){
7557 p = Roo.urlEncode(Roo.applyIf(p, bp));
7558 }else if(typeof p == 'string' && bp){
7559 p += '&' + Roo.urlEncode(bp);
7562 p = Roo.urlEncode(bp);
7567 createCallback : function(){
7569 success: this.success,
7570 failure: this.failure,
7572 timeout: (this.form.timeout*1000),
7573 upload: this.form.fileUpload ? this.success : undefined
7578 Roo.form.Action.Submit = function(form, options){
7579 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7582 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7585 haveProgress : false,
7586 uploadComplete : false,
7588 // uploadProgress indicator.
7589 uploadProgress : function()
7591 if (!this.form.progressUrl) {
7595 if (!this.haveProgress) {
7596 Roo.MessageBox.progress("Uploading", "Uploading");
7598 if (this.uploadComplete) {
7599 Roo.MessageBox.hide();
7603 this.haveProgress = true;
7605 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7607 var c = new Roo.data.Connection();
7609 url : this.form.progressUrl,
7614 success : function(req){
7615 //console.log(data);
7619 rdata = Roo.decode(req.responseText)
7621 Roo.log("Invalid data from server..");
7625 if (!rdata || !rdata.success) {
7627 Roo.MessageBox.alert(Roo.encode(rdata));
7630 var data = rdata.data;
7632 if (this.uploadComplete) {
7633 Roo.MessageBox.hide();
7638 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7639 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7642 this.uploadProgress.defer(2000,this);
7645 failure: function(data) {
7646 Roo.log('progress url failed ');
7657 // run get Values on the form, so it syncs any secondary forms.
7658 this.form.getValues();
7660 var o = this.options;
7661 var method = this.getMethod();
7662 var isPost = method == 'POST';
7663 if(o.clientValidation === false || this.form.isValid()){
7665 if (this.form.progressUrl) {
7666 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7667 (new Date() * 1) + '' + Math.random());
7672 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7673 form:this.form.el.dom,
7674 url:this.getUrl(!isPost),
7676 params:isPost ? this.getParams() : null,
7677 isUpload: this.form.fileUpload
7680 this.uploadProgress();
7682 }else if (o.clientValidation !== false){ // client validation failed
7683 this.failureType = Roo.form.Action.CLIENT_INVALID;
7684 this.form.afterAction(this, false);
7688 success : function(response)
7690 this.uploadComplete= true;
7691 if (this.haveProgress) {
7692 Roo.MessageBox.hide();
7696 var result = this.processResponse(response);
7697 if(result === true || result.success){
7698 this.form.afterAction(this, true);
7702 this.form.markInvalid(result.errors);
7703 this.failureType = Roo.form.Action.SERVER_INVALID;
7705 this.form.afterAction(this, false);
7707 failure : function(response)
7709 this.uploadComplete= true;
7710 if (this.haveProgress) {
7711 Roo.MessageBox.hide();
7714 this.response = response;
7715 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7716 this.form.afterAction(this, false);
7719 handleResponse : function(response){
7720 if(this.form.errorReader){
7721 var rs = this.form.errorReader.read(response);
7724 for(var i = 0, len = rs.records.length; i < len; i++) {
7725 var r = rs.records[i];
7729 if(errors.length < 1){
7733 success : rs.success,
7739 ret = Roo.decode(response.responseText);
7743 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7753 Roo.form.Action.Load = function(form, options){
7754 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7755 this.reader = this.form.reader;
7758 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7763 Roo.Ajax.request(Roo.apply(
7764 this.createCallback(), {
7765 method:this.getMethod(),
7766 url:this.getUrl(false),
7767 params:this.getParams()
7771 success : function(response){
7773 var result = this.processResponse(response);
7774 if(result === true || !result.success || !result.data){
7775 this.failureType = Roo.form.Action.LOAD_FAILURE;
7776 this.form.afterAction(this, false);
7779 this.form.clearInvalid();
7780 this.form.setValues(result.data);
7781 this.form.afterAction(this, true);
7784 handleResponse : function(response){
7785 if(this.form.reader){
7786 var rs = this.form.reader.read(response);
7787 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7789 success : rs.success,
7793 return Roo.decode(response.responseText);
7797 Roo.form.Action.ACTION_TYPES = {
7798 'load' : Roo.form.Action.Load,
7799 'submit' : Roo.form.Action.Submit
7808 * @class Roo.bootstrap.Form
7809 * @extends Roo.bootstrap.Component
7810 * Bootstrap Form class
7811 * @cfg {String} method GET | POST (default POST)
7812 * @cfg {String} labelAlign top | left (default top)
7813 * @cfg {String} align left | right - for navbars
7814 * @cfg {Boolean} loadMask load mask when submit (default true)
7819 * @param {Object} config The config object
7823 Roo.bootstrap.Form = function(config){
7825 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7827 Roo.bootstrap.Form.popover.apply();
7831 * @event clientvalidation
7832 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7833 * @param {Form} this
7834 * @param {Boolean} valid true if the form has passed client-side validation
7836 clientvalidation: true,
7838 * @event beforeaction
7839 * Fires before any action is performed. Return false to cancel the action.
7840 * @param {Form} this
7841 * @param {Action} action The action to be performed
7845 * @event actionfailed
7846 * Fires when an action fails.
7847 * @param {Form} this
7848 * @param {Action} action The action that failed
7850 actionfailed : true,
7852 * @event actioncomplete
7853 * Fires when an action is completed.
7854 * @param {Form} this
7855 * @param {Action} action The action that completed
7857 actioncomplete : true
7861 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7864 * @cfg {String} method
7865 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7870 * The URL to use for form actions if one isn't supplied in the action options.
7873 * @cfg {Boolean} fileUpload
7874 * Set to true if this form is a file upload.
7878 * @cfg {Object} baseParams
7879 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7883 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7887 * @cfg {Sting} align (left|right) for navbar forms
7892 activeAction : null,
7895 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7896 * element by passing it or its id or mask the form itself by passing in true.
7899 waitMsgTarget : false,
7904 * @cfg {Boolean} errorMask (true|false) default false
7909 * @cfg {Number} maskOffset Default 100
7914 * @cfg {Boolean} maskBody
7918 getAutoCreate : function(){
7922 method : this.method || 'POST',
7923 id : this.id || Roo.id(),
7926 if (this.parent().xtype.match(/^Nav/)) {
7927 cfg.cls = 'navbar-form navbar-' + this.align;
7931 if (this.labelAlign == 'left' ) {
7932 cfg.cls += ' form-horizontal';
7938 initEvents : function()
7940 this.el.on('submit', this.onSubmit, this);
7941 // this was added as random key presses on the form where triggering form submit.
7942 this.el.on('keypress', function(e) {
7943 if (e.getCharCode() != 13) {
7946 // we might need to allow it for textareas.. and some other items.
7947 // check e.getTarget().
7949 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7953 Roo.log("keypress blocked");
7961 onSubmit : function(e){
7966 * Returns true if client-side validation on the form is successful.
7969 isValid : function(){
7970 var items = this.getItems();
7974 items.each(function(f){
7980 Roo.log('invalid field: ' + f.name);
7984 if(!target && f.el.isVisible(true)){
7990 if(this.errorMask && !valid){
7991 Roo.bootstrap.Form.popover.mask(this, target);
7998 * Returns true if any fields in this form have changed since their original load.
8001 isDirty : function(){
8003 var items = this.getItems();
8004 items.each(function(f){
8014 * Performs a predefined action (submit or load) or custom actions you define on this form.
8015 * @param {String} actionName The name of the action type
8016 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8017 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8018 * accept other config options):
8020 Property Type Description
8021 ---------------- --------------- ----------------------------------------------------------------------------------
8022 url String The url for the action (defaults to the form's url)
8023 method String The form method to use (defaults to the form's method, or POST if not defined)
8024 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8025 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8026 validate the form on the client (defaults to false)
8028 * @return {BasicForm} this
8030 doAction : function(action, options){
8031 if(typeof action == 'string'){
8032 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8034 if(this.fireEvent('beforeaction', this, action) !== false){
8035 this.beforeAction(action);
8036 action.run.defer(100, action);
8042 beforeAction : function(action){
8043 var o = action.options;
8048 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8050 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8053 // not really supported yet.. ??
8055 //if(this.waitMsgTarget === true){
8056 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8057 //}else if(this.waitMsgTarget){
8058 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8059 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8061 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8067 afterAction : function(action, success){
8068 this.activeAction = null;
8069 var o = action.options;
8074 Roo.get(document.body).unmask();
8080 //if(this.waitMsgTarget === true){
8081 // this.el.unmask();
8082 //}else if(this.waitMsgTarget){
8083 // this.waitMsgTarget.unmask();
8085 // Roo.MessageBox.updateProgress(1);
8086 // Roo.MessageBox.hide();
8093 Roo.callback(o.success, o.scope, [this, action]);
8094 this.fireEvent('actioncomplete', this, action);
8098 // failure condition..
8099 // we have a scenario where updates need confirming.
8100 // eg. if a locking scenario exists..
8101 // we look for { errors : { needs_confirm : true }} in the response.
8103 (typeof(action.result) != 'undefined') &&
8104 (typeof(action.result.errors) != 'undefined') &&
8105 (typeof(action.result.errors.needs_confirm) != 'undefined')
8108 Roo.log("not supported yet");
8111 Roo.MessageBox.confirm(
8112 "Change requires confirmation",
8113 action.result.errorMsg,
8118 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8128 Roo.callback(o.failure, o.scope, [this, action]);
8129 // show an error message if no failed handler is set..
8130 if (!this.hasListener('actionfailed')) {
8131 Roo.log("need to add dialog support");
8133 Roo.MessageBox.alert("Error",
8134 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8135 action.result.errorMsg :
8136 "Saving Failed, please check your entries or try again"
8141 this.fireEvent('actionfailed', this, action);
8146 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8147 * @param {String} id The value to search for
8150 findField : function(id){
8151 var items = this.getItems();
8152 var field = items.get(id);
8154 items.each(function(f){
8155 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8162 return field || null;
8165 * Mark fields in this form invalid in bulk.
8166 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8167 * @return {BasicForm} this
8169 markInvalid : function(errors){
8170 if(errors instanceof Array){
8171 for(var i = 0, len = errors.length; i < len; i++){
8172 var fieldError = errors[i];
8173 var f = this.findField(fieldError.id);
8175 f.markInvalid(fieldError.msg);
8181 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8182 field.markInvalid(errors[id]);
8186 //Roo.each(this.childForms || [], function (f) {
8187 // f.markInvalid(errors);
8194 * Set values for fields in this form in bulk.
8195 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8196 * @return {BasicForm} this
8198 setValues : function(values){
8199 if(values instanceof Array){ // array of objects
8200 for(var i = 0, len = values.length; i < len; i++){
8202 var f = this.findField(v.id);
8204 f.setValue(v.value);
8205 if(this.trackResetOnLoad){
8206 f.originalValue = f.getValue();
8210 }else{ // object hash
8213 if(typeof values[id] != 'function' && (field = this.findField(id))){
8215 if (field.setFromData &&
8217 field.displayField &&
8218 // combos' with local stores can
8219 // be queried via setValue()
8220 // to set their value..
8221 (field.store && !field.store.isLocal)
8225 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8226 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8227 field.setFromData(sd);
8229 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8231 field.setFromData(values);
8234 field.setValue(values[id]);
8238 if(this.trackResetOnLoad){
8239 field.originalValue = field.getValue();
8245 //Roo.each(this.childForms || [], function (f) {
8246 // f.setValues(values);
8253 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8254 * they are returned as an array.
8255 * @param {Boolean} asString
8258 getValues : function(asString){
8259 //if (this.childForms) {
8260 // copy values from the child forms
8261 // Roo.each(this.childForms, function (f) {
8262 // this.setValues(f.getValues());
8268 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8269 if(asString === true){
8272 return Roo.urlDecode(fs);
8276 * Returns the fields in this form as an object with key/value pairs.
8277 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8280 getFieldValues : function(with_hidden)
8282 var items = this.getItems();
8284 items.each(function(f){
8290 var v = f.getValue();
8292 if (f.inputType =='radio') {
8293 if (typeof(ret[f.getName()]) == 'undefined') {
8294 ret[f.getName()] = ''; // empty..
8297 if (!f.el.dom.checked) {
8305 if(f.xtype == 'MoneyField'){
8306 ret[f.currencyName] = f.getCurrency();
8309 // not sure if this supported any more..
8310 if ((typeof(v) == 'object') && f.getRawValue) {
8311 v = f.getRawValue() ; // dates..
8313 // combo boxes where name != hiddenName...
8314 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8315 ret[f.name] = f.getRawValue();
8317 ret[f.getName()] = v;
8324 * Clears all invalid messages in this form.
8325 * @return {BasicForm} this
8327 clearInvalid : function(){
8328 var items = this.getItems();
8330 items.each(function(f){
8339 * @return {BasicForm} this
8342 var items = this.getItems();
8343 items.each(function(f){
8347 Roo.each(this.childForms || [], function (f) {
8355 getItems : function()
8357 var r=new Roo.util.MixedCollection(false, function(o){
8358 return o.id || (o.id = Roo.id());
8360 var iter = function(el) {
8367 Roo.each(el.items,function(e) {
8376 hideFields : function(items)
8378 Roo.each(items, function(i){
8380 var f = this.findField(i);
8391 showFields : function(items)
8393 Roo.each(items, function(i){
8395 var f = this.findField(i);
8408 Roo.apply(Roo.bootstrap.Form, {
8435 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8436 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8437 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8438 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8441 this.maskEl.top.enableDisplayMode("block");
8442 this.maskEl.left.enableDisplayMode("block");
8443 this.maskEl.bottom.enableDisplayMode("block");
8444 this.maskEl.right.enableDisplayMode("block");
8446 this.toolTip = new Roo.bootstrap.Tooltip({
8447 cls : 'roo-form-error-popover',
8449 'left' : ['r-l', [-2,0], 'right'],
8450 'right' : ['l-r', [2,0], 'left'],
8451 'bottom' : ['tl-bl', [0,2], 'top'],
8452 'top' : [ 'bl-tl', [0,-2], 'bottom']
8456 this.toolTip.render(Roo.get(document.body));
8458 this.toolTip.el.enableDisplayMode("block");
8460 Roo.get(document.body).on('click', function(){
8464 Roo.get(document.body).on('touchstart', function(){
8468 this.isApplied = true
8471 mask : function(form, target)
8475 this.target = target;
8477 if(!this.form.errorMask || !target.el){
8481 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8483 Roo.log(scrollable);
8485 var ot = this.target.el.calcOffsetsTo(scrollable);
8487 var scrollTo = ot[1] - this.form.maskOffset;
8489 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8491 scrollable.scrollTo('top', scrollTo);
8493 var box = this.target.el.getBox();
8495 var zIndex = Roo.bootstrap.Modal.zIndex++;
8498 this.maskEl.top.setStyle('position', 'absolute');
8499 this.maskEl.top.setStyle('z-index', zIndex);
8500 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8501 this.maskEl.top.setLeft(0);
8502 this.maskEl.top.setTop(0);
8503 this.maskEl.top.show();
8505 this.maskEl.left.setStyle('position', 'absolute');
8506 this.maskEl.left.setStyle('z-index', zIndex);
8507 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8508 this.maskEl.left.setLeft(0);
8509 this.maskEl.left.setTop(box.y - this.padding);
8510 this.maskEl.left.show();
8512 this.maskEl.bottom.setStyle('position', 'absolute');
8513 this.maskEl.bottom.setStyle('z-index', zIndex);
8514 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8515 this.maskEl.bottom.setLeft(0);
8516 this.maskEl.bottom.setTop(box.bottom + this.padding);
8517 this.maskEl.bottom.show();
8519 this.maskEl.right.setStyle('position', 'absolute');
8520 this.maskEl.right.setStyle('z-index', zIndex);
8521 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8522 this.maskEl.right.setLeft(box.right + this.padding);
8523 this.maskEl.right.setTop(box.y - this.padding);
8524 this.maskEl.right.show();
8526 this.toolTip.bindEl = this.target.el;
8528 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8530 var tip = this.target.blankText;
8532 if(this.target.getValue() !== '' ) {
8534 if (this.target.invalidText.length) {
8535 tip = this.target.invalidText;
8536 } else if (this.target.regexText.length){
8537 tip = this.target.regexText;
8541 this.toolTip.show(tip);
8543 this.intervalID = window.setInterval(function() {
8544 Roo.bootstrap.Form.popover.unmask();
8547 window.onwheel = function(){ return false;};
8549 (function(){ this.isMasked = true; }).defer(500, this);
8555 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8559 this.maskEl.top.setStyle('position', 'absolute');
8560 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8561 this.maskEl.top.hide();
8563 this.maskEl.left.setStyle('position', 'absolute');
8564 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8565 this.maskEl.left.hide();
8567 this.maskEl.bottom.setStyle('position', 'absolute');
8568 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8569 this.maskEl.bottom.hide();
8571 this.maskEl.right.setStyle('position', 'absolute');
8572 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8573 this.maskEl.right.hide();
8575 this.toolTip.hide();
8577 this.toolTip.el.hide();
8579 window.onwheel = function(){ return true;};
8581 if(this.intervalID){
8582 window.clearInterval(this.intervalID);
8583 this.intervalID = false;
8586 this.isMasked = false;
8596 * Ext JS Library 1.1.1
8597 * Copyright(c) 2006-2007, Ext JS, LLC.
8599 * Originally Released Under LGPL - original licence link has changed is not relivant.
8602 * <script type="text/javascript">
8605 * @class Roo.form.VTypes
8606 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8609 Roo.form.VTypes = function(){
8610 // closure these in so they are only created once.
8611 var alpha = /^[a-zA-Z_]+$/;
8612 var alphanum = /^[a-zA-Z0-9_]+$/;
8613 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8614 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8616 // All these messages and functions are configurable
8619 * The function used to validate email addresses
8620 * @param {String} value The email address
8622 'email' : function(v){
8623 return email.test(v);
8626 * The error text to display when the email validation function returns false
8629 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8631 * The keystroke filter mask to be applied on email input
8634 'emailMask' : /[a-z0-9_\.\-@]/i,
8637 * The function used to validate URLs
8638 * @param {String} value The URL
8640 'url' : function(v){
8644 * The error text to display when the url validation function returns false
8647 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8650 * The function used to validate alpha values
8651 * @param {String} value The value
8653 'alpha' : function(v){
8654 return alpha.test(v);
8657 * The error text to display when the alpha validation function returns false
8660 'alphaText' : 'This field should only contain letters and _',
8662 * The keystroke filter mask to be applied on alpha input
8665 'alphaMask' : /[a-z_]/i,
8668 * The function used to validate alphanumeric values
8669 * @param {String} value The value
8671 'alphanum' : function(v){
8672 return alphanum.test(v);
8675 * The error text to display when the alphanumeric validation function returns false
8678 'alphanumText' : 'This field should only contain letters, numbers and _',
8680 * The keystroke filter mask to be applied on alphanumeric input
8683 'alphanumMask' : /[a-z0-9_]/i
8693 * @class Roo.bootstrap.Input
8694 * @extends Roo.bootstrap.Component
8695 * Bootstrap Input class
8696 * @cfg {Boolean} disabled is it disabled
8697 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8698 * @cfg {String} name name of the input
8699 * @cfg {string} fieldLabel - the label associated
8700 * @cfg {string} placeholder - placeholder to put in text.
8701 * @cfg {string} before - input group add on before
8702 * @cfg {string} after - input group add on after
8703 * @cfg {string} size - (lg|sm) or leave empty..
8704 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8705 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8706 * @cfg {Number} md colspan out of 12 for computer-sized screens
8707 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8708 * @cfg {string} value default value of the input
8709 * @cfg {Number} labelWidth set the width of label
8710 * @cfg {Number} labellg set the width of label (1-12)
8711 * @cfg {Number} labelmd set the width of label (1-12)
8712 * @cfg {Number} labelsm set the width of label (1-12)
8713 * @cfg {Number} labelxs set the width of label (1-12)
8714 * @cfg {String} labelAlign (top|left)
8715 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8716 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8717 * @cfg {String} indicatorpos (left|right) default left
8718 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8719 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8721 * @cfg {String} align (left|center|right) Default left
8722 * @cfg {Boolean} forceFeedback (true|false) Default false
8725 * Create a new Input
8726 * @param {Object} config The config object
8729 Roo.bootstrap.Input = function(config){
8731 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8736 * Fires when this field receives input focus.
8737 * @param {Roo.form.Field} this
8742 * Fires when this field loses input focus.
8743 * @param {Roo.form.Field} this
8748 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8749 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8750 * @param {Roo.form.Field} this
8751 * @param {Roo.EventObject} e The event object
8756 * Fires just before the field blurs if the field value has changed.
8757 * @param {Roo.form.Field} this
8758 * @param {Mixed} newValue The new value
8759 * @param {Mixed} oldValue The original value
8764 * Fires after the field has been marked as invalid.
8765 * @param {Roo.form.Field} this
8766 * @param {String} msg The validation message
8771 * Fires after the field has been validated with no errors.
8772 * @param {Roo.form.Field} this
8777 * Fires after the key up
8778 * @param {Roo.form.Field} this
8779 * @param {Roo.EventObject} e The event Object
8785 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8787 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8788 automatic validation (defaults to "keyup").
8790 validationEvent : "keyup",
8792 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8794 validateOnBlur : true,
8796 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8798 validationDelay : 250,
8800 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8802 focusClass : "x-form-focus", // not needed???
8806 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8808 invalidClass : "has-warning",
8811 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8813 validClass : "has-success",
8816 * @cfg {Boolean} hasFeedback (true|false) default true
8821 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8823 invalidFeedbackClass : "glyphicon-warning-sign",
8826 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8828 validFeedbackClass : "glyphicon-ok",
8831 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8833 selectOnFocus : false,
8836 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8840 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8845 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8847 disableKeyFilter : false,
8850 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8854 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8858 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8860 blankText : "Please complete this mandatory field",
8863 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8867 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8869 maxLength : Number.MAX_VALUE,
8871 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8873 minLengthText : "The minimum length for this field is {0}",
8875 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8877 maxLengthText : "The maximum length for this field is {0}",
8881 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8882 * If available, this function will be called only after the basic validators all return true, and will be passed the
8883 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8887 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8888 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8889 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8893 * @cfg {String} regexText -- Depricated - use Invalid Text
8898 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8904 autocomplete: false,
8923 formatedValue : false,
8924 forceFeedback : false,
8926 indicatorpos : 'left',
8936 parentLabelAlign : function()
8939 while (parent.parent()) {
8940 parent = parent.parent();
8941 if (typeof(parent.labelAlign) !='undefined') {
8942 return parent.labelAlign;
8949 getAutoCreate : function()
8951 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8957 if(this.inputType != 'hidden'){
8958 cfg.cls = 'form-group' //input-group
8964 type : this.inputType,
8966 cls : 'form-control',
8967 placeholder : this.placeholder || '',
8968 autocomplete : this.autocomplete || 'new-password'
8971 if(this.capture.length){
8972 input.capture = this.capture;
8975 if(this.accept.length){
8976 input.accept = this.accept + "/*";
8980 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8983 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8984 input.maxLength = this.maxLength;
8987 if (this.disabled) {
8988 input.disabled=true;
8991 if (this.readOnly) {
8992 input.readonly=true;
8996 input.name = this.name;
9000 input.cls += ' input-' + this.size;
9004 ['xs','sm','md','lg'].map(function(size){
9005 if (settings[size]) {
9006 cfg.cls += ' col-' + size + '-' + settings[size];
9010 var inputblock = input;
9014 cls: 'glyphicon form-control-feedback'
9017 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9020 cls : 'has-feedback',
9028 if (this.before || this.after) {
9031 cls : 'input-group',
9035 if (this.before && typeof(this.before) == 'string') {
9037 inputblock.cn.push({
9039 cls : 'roo-input-before input-group-addon',
9043 if (this.before && typeof(this.before) == 'object') {
9044 this.before = Roo.factory(this.before);
9046 inputblock.cn.push({
9048 cls : 'roo-input-before input-group-' +
9049 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9053 inputblock.cn.push(input);
9055 if (this.after && typeof(this.after) == 'string') {
9056 inputblock.cn.push({
9058 cls : 'roo-input-after input-group-addon',
9062 if (this.after && typeof(this.after) == 'object') {
9063 this.after = Roo.factory(this.after);
9065 inputblock.cn.push({
9067 cls : 'roo-input-after input-group-' +
9068 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9072 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9073 inputblock.cls += ' has-feedback';
9074 inputblock.cn.push(feedback);
9078 if (align ==='left' && this.fieldLabel.length) {
9080 cfg.cls += ' roo-form-group-label-left';
9085 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9086 tooltip : 'This field is required'
9091 cls : 'control-label',
9092 html : this.fieldLabel
9103 var labelCfg = cfg.cn[1];
9104 var contentCfg = cfg.cn[2];
9106 if(this.indicatorpos == 'right'){
9111 cls : 'control-label',
9115 html : this.fieldLabel
9119 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9120 tooltip : 'This field is required'
9133 labelCfg = cfg.cn[0];
9134 contentCfg = cfg.cn[1];
9138 if(this.labelWidth > 12){
9139 labelCfg.style = "width: " + this.labelWidth + 'px';
9142 if(this.labelWidth < 13 && this.labelmd == 0){
9143 this.labelmd = this.labelWidth;
9146 if(this.labellg > 0){
9147 labelCfg.cls += ' col-lg-' + this.labellg;
9148 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9151 if(this.labelmd > 0){
9152 labelCfg.cls += ' col-md-' + this.labelmd;
9153 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9156 if(this.labelsm > 0){
9157 labelCfg.cls += ' col-sm-' + this.labelsm;
9158 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9161 if(this.labelxs > 0){
9162 labelCfg.cls += ' col-xs-' + this.labelxs;
9163 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9167 } else if ( this.fieldLabel.length) {
9172 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9173 tooltip : 'This field is required'
9177 //cls : 'input-group-addon',
9178 html : this.fieldLabel
9186 if(this.indicatorpos == 'right'){
9191 //cls : 'input-group-addon',
9192 html : this.fieldLabel
9197 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9198 tooltip : 'This field is required'
9218 if (this.parentType === 'Navbar' && this.parent().bar) {
9219 cfg.cls += ' navbar-form';
9222 if (this.parentType === 'NavGroup') {
9223 cfg.cls += ' navbar-form';
9231 * return the real input element.
9233 inputEl: function ()
9235 return this.el.select('input.form-control',true).first();
9238 tooltipEl : function()
9240 return this.inputEl();
9243 indicatorEl : function()
9245 var indicator = this.el.select('i.roo-required-indicator',true).first();
9255 setDisabled : function(v)
9257 var i = this.inputEl().dom;
9259 i.removeAttribute('disabled');
9263 i.setAttribute('disabled','true');
9265 initEvents : function()
9268 this.inputEl().on("keydown" , this.fireKey, this);
9269 this.inputEl().on("focus", this.onFocus, this);
9270 this.inputEl().on("blur", this.onBlur, this);
9272 this.inputEl().relayEvent('keyup', this);
9274 this.indicator = this.indicatorEl();
9277 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9280 // reference to original value for reset
9281 this.originalValue = this.getValue();
9282 //Roo.form.TextField.superclass.initEvents.call(this);
9283 if(this.validationEvent == 'keyup'){
9284 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9285 this.inputEl().on('keyup', this.filterValidation, this);
9287 else if(this.validationEvent !== false){
9288 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9291 if(this.selectOnFocus){
9292 this.on("focus", this.preFocus, this);
9295 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9296 this.inputEl().on("keypress", this.filterKeys, this);
9298 this.inputEl().relayEvent('keypress', this);
9301 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9302 this.el.on("click", this.autoSize, this);
9305 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9306 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9309 if (typeof(this.before) == 'object') {
9310 this.before.render(this.el.select('.roo-input-before',true).first());
9312 if (typeof(this.after) == 'object') {
9313 this.after.render(this.el.select('.roo-input-after',true).first());
9316 this.inputEl().on('change', this.onChange, this);
9319 filterValidation : function(e){
9320 if(!e.isNavKeyPress()){
9321 this.validationTask.delay(this.validationDelay);
9325 * Validates the field value
9326 * @return {Boolean} True if the value is valid, else false
9328 validate : function(){
9329 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9330 if(this.disabled || this.validateValue(this.getRawValue())){
9341 * Validates a value according to the field's validation rules and marks the field as invalid
9342 * if the validation fails
9343 * @param {Mixed} value The value to validate
9344 * @return {Boolean} True if the value is valid, else false
9346 validateValue : function(value)
9348 if(this.getVisibilityEl().hasClass('hidden')){
9352 if(value.length < 1) { // if it's blank
9353 if(this.allowBlank){
9359 if(value.length < this.minLength){
9362 if(value.length > this.maxLength){
9366 var vt = Roo.form.VTypes;
9367 if(!vt[this.vtype](value, this)){
9371 if(typeof this.validator == "function"){
9372 var msg = this.validator(value);
9376 if (typeof(msg) == 'string') {
9377 this.invalidText = msg;
9381 if(this.regex && !this.regex.test(value)){
9389 fireKey : function(e){
9390 //Roo.log('field ' + e.getKey());
9391 if(e.isNavKeyPress()){
9392 this.fireEvent("specialkey", this, e);
9395 focus : function (selectText){
9397 this.inputEl().focus();
9398 if(selectText === true){
9399 this.inputEl().dom.select();
9405 onFocus : function(){
9406 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9407 // this.el.addClass(this.focusClass);
9410 this.hasFocus = true;
9411 this.startValue = this.getValue();
9412 this.fireEvent("focus", this);
9416 beforeBlur : Roo.emptyFn,
9420 onBlur : function(){
9422 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9423 //this.el.removeClass(this.focusClass);
9425 this.hasFocus = false;
9426 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9429 var v = this.getValue();
9430 if(String(v) !== String(this.startValue)){
9431 this.fireEvent('change', this, v, this.startValue);
9433 this.fireEvent("blur", this);
9436 onChange : function(e)
9438 var v = this.getValue();
9439 if(String(v) !== String(this.startValue)){
9440 this.fireEvent('change', this, v, this.startValue);
9446 * Resets the current field value to the originally loaded value and clears any validation messages
9449 this.setValue(this.originalValue);
9453 * Returns the name of the field
9454 * @return {Mixed} name The name field
9456 getName: function(){
9460 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9461 * @return {Mixed} value The field value
9463 getValue : function(){
9465 var v = this.inputEl().getValue();
9470 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9471 * @return {Mixed} value The field value
9473 getRawValue : function(){
9474 var v = this.inputEl().getValue();
9480 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9481 * @param {Mixed} value The value to set
9483 setRawValue : function(v){
9484 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9487 selectText : function(start, end){
9488 var v = this.getRawValue();
9490 start = start === undefined ? 0 : start;
9491 end = end === undefined ? v.length : end;
9492 var d = this.inputEl().dom;
9493 if(d.setSelectionRange){
9494 d.setSelectionRange(start, end);
9495 }else if(d.createTextRange){
9496 var range = d.createTextRange();
9497 range.moveStart("character", start);
9498 range.moveEnd("character", v.length-end);
9505 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9506 * @param {Mixed} value The value to set
9508 setValue : function(v){
9511 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9517 processValue : function(value){
9518 if(this.stripCharsRe){
9519 var newValue = value.replace(this.stripCharsRe, '');
9520 if(newValue !== value){
9521 this.setRawValue(newValue);
9528 preFocus : function(){
9530 if(this.selectOnFocus){
9531 this.inputEl().dom.select();
9534 filterKeys : function(e){
9536 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9539 var c = e.getCharCode(), cc = String.fromCharCode(c);
9540 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9543 if(!this.maskRe.test(cc)){
9548 * Clear any invalid styles/messages for this field
9550 clearInvalid : function(){
9552 if(!this.el || this.preventMark){ // not rendered
9557 this.el.removeClass(this.invalidClass);
9559 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9561 var feedback = this.el.select('.form-control-feedback', true).first();
9564 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9570 this.indicator.removeClass('visible');
9571 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9574 this.fireEvent('valid', this);
9578 * Mark this field as valid
9580 markValid : function()
9582 if(!this.el || this.preventMark){ // not rendered...
9586 this.el.removeClass([this.invalidClass, this.validClass]);
9588 var feedback = this.el.select('.form-control-feedback', true).first();
9591 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9595 this.indicator.removeClass('visible');
9596 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9603 if(this.allowBlank && !this.getRawValue().length){
9607 this.el.addClass(this.validClass);
9609 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9611 var feedback = this.el.select('.form-control-feedback', true).first();
9614 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9615 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9620 this.fireEvent('valid', this);
9624 * Mark this field as invalid
9625 * @param {String} msg The validation message
9627 markInvalid : function(msg)
9629 if(!this.el || this.preventMark){ // not rendered
9633 this.el.removeClass([this.invalidClass, this.validClass]);
9635 var feedback = this.el.select('.form-control-feedback', true).first();
9638 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9645 if(this.allowBlank && !this.getRawValue().length){
9650 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9651 this.indicator.addClass('visible');
9654 this.el.addClass(this.invalidClass);
9656 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9658 var feedback = this.el.select('.form-control-feedback', true).first();
9661 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9663 if(this.getValue().length || this.forceFeedback){
9664 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9671 this.fireEvent('invalid', this, msg);
9674 SafariOnKeyDown : function(event)
9676 // this is a workaround for a password hang bug on chrome/ webkit.
9677 if (this.inputEl().dom.type != 'password') {
9681 var isSelectAll = false;
9683 if(this.inputEl().dom.selectionEnd > 0){
9684 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9686 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9687 event.preventDefault();
9692 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9694 event.preventDefault();
9695 // this is very hacky as keydown always get's upper case.
9697 var cc = String.fromCharCode(event.getCharCode());
9698 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9702 adjustWidth : function(tag, w){
9703 tag = tag.toLowerCase();
9704 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9705 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9709 if(tag == 'textarea'){
9712 }else if(Roo.isOpera){
9716 if(tag == 'textarea'){
9724 setFieldLabel : function(v)
9731 var ar = this.el.select('label > span',true);
9733 if (ar.elements.length) {
9734 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9735 this.fieldLabel = v;
9739 var br = this.el.select('label',true);
9741 if(br.elements.length) {
9742 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9743 this.fieldLabel = v;
9747 Roo.log('Cannot Found any of label > span || label in input');
9751 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9752 this.fieldLabel = v;
9767 * @class Roo.bootstrap.TextArea
9768 * @extends Roo.bootstrap.Input
9769 * Bootstrap TextArea class
9770 * @cfg {Number} cols Specifies the visible width of a text area
9771 * @cfg {Number} rows Specifies the visible number of lines in a text area
9772 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9773 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9774 * @cfg {string} html text
9777 * Create a new TextArea
9778 * @param {Object} config The config object
9781 Roo.bootstrap.TextArea = function(config){
9782 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9786 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9796 getAutoCreate : function(){
9798 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9804 if(this.inputType != 'hidden'){
9805 cfg.cls = 'form-group' //input-group
9813 value : this.value || '',
9814 html: this.html || '',
9815 cls : 'form-control',
9816 placeholder : this.placeholder || ''
9820 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9821 input.maxLength = this.maxLength;
9825 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9829 input.cols = this.cols;
9832 if (this.readOnly) {
9833 input.readonly = true;
9837 input.name = this.name;
9841 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9845 ['xs','sm','md','lg'].map(function(size){
9846 if (settings[size]) {
9847 cfg.cls += ' col-' + size + '-' + settings[size];
9851 var inputblock = input;
9853 if(this.hasFeedback && !this.allowBlank){
9857 cls: 'glyphicon form-control-feedback'
9861 cls : 'has-feedback',
9870 if (this.before || this.after) {
9873 cls : 'input-group',
9877 inputblock.cn.push({
9879 cls : 'input-group-addon',
9884 inputblock.cn.push(input);
9886 if(this.hasFeedback && !this.allowBlank){
9887 inputblock.cls += ' has-feedback';
9888 inputblock.cn.push(feedback);
9892 inputblock.cn.push({
9894 cls : 'input-group-addon',
9901 if (align ==='left' && this.fieldLabel.length) {
9906 cls : 'control-label',
9907 html : this.fieldLabel
9918 if(this.labelWidth > 12){
9919 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9922 if(this.labelWidth < 13 && this.labelmd == 0){
9923 this.labelmd = this.labelWidth;
9926 if(this.labellg > 0){
9927 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9928 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9931 if(this.labelmd > 0){
9932 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9933 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9936 if(this.labelsm > 0){
9937 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9938 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9941 if(this.labelxs > 0){
9942 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9943 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9946 } else if ( this.fieldLabel.length) {
9951 //cls : 'input-group-addon',
9952 html : this.fieldLabel
9970 if (this.disabled) {
9971 input.disabled=true;
9978 * return the real textarea element.
9980 inputEl: function ()
9982 return this.el.select('textarea.form-control',true).first();
9986 * Clear any invalid styles/messages for this field
9988 clearInvalid : function()
9991 if(!this.el || this.preventMark){ // not rendered
9995 var label = this.el.select('label', true).first();
9996 var icon = this.el.select('i.fa-star', true).first();
10002 this.el.removeClass(this.invalidClass);
10004 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10006 var feedback = this.el.select('.form-control-feedback', true).first();
10009 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10014 this.fireEvent('valid', this);
10018 * Mark this field as valid
10020 markValid : function()
10022 if(!this.el || this.preventMark){ // not rendered
10026 this.el.removeClass([this.invalidClass, this.validClass]);
10028 var feedback = this.el.select('.form-control-feedback', true).first();
10031 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10034 if(this.disabled || this.allowBlank){
10038 var label = this.el.select('label', true).first();
10039 var icon = this.el.select('i.fa-star', true).first();
10045 this.el.addClass(this.validClass);
10047 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10049 var feedback = this.el.select('.form-control-feedback', true).first();
10052 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10053 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10058 this.fireEvent('valid', this);
10062 * Mark this field as invalid
10063 * @param {String} msg The validation message
10065 markInvalid : function(msg)
10067 if(!this.el || this.preventMark){ // not rendered
10071 this.el.removeClass([this.invalidClass, this.validClass]);
10073 var feedback = this.el.select('.form-control-feedback', true).first();
10076 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10079 if(this.disabled || this.allowBlank){
10083 var label = this.el.select('label', true).first();
10084 var icon = this.el.select('i.fa-star', true).first();
10086 if(!this.getValue().length && label && !icon){
10087 this.el.createChild({
10089 cls : 'text-danger fa fa-lg fa-star',
10090 tooltip : 'This field is required',
10091 style : 'margin-right:5px;'
10095 this.el.addClass(this.invalidClass);
10097 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10099 var feedback = this.el.select('.form-control-feedback', true).first();
10102 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10104 if(this.getValue().length || this.forceFeedback){
10105 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10112 this.fireEvent('invalid', this, msg);
10120 * trigger field - base class for combo..
10125 * @class Roo.bootstrap.TriggerField
10126 * @extends Roo.bootstrap.Input
10127 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10128 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10129 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10130 * for which you can provide a custom implementation. For example:
10132 var trigger = new Roo.bootstrap.TriggerField();
10133 trigger.onTriggerClick = myTriggerFn;
10134 trigger.applyTo('my-field');
10137 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10138 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10139 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10140 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10141 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10144 * Create a new TriggerField.
10145 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10146 * to the base TextField)
10148 Roo.bootstrap.TriggerField = function(config){
10149 this.mimicing = false;
10150 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10153 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10155 * @cfg {String} triggerClass A CSS class to apply to the trigger
10158 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10163 * @cfg {Boolean} removable (true|false) special filter default false
10167 /** @cfg {Boolean} grow @hide */
10168 /** @cfg {Number} growMin @hide */
10169 /** @cfg {Number} growMax @hide */
10175 autoSize: Roo.emptyFn,
10179 deferHeight : true,
10182 actionMode : 'wrap',
10187 getAutoCreate : function(){
10189 var align = this.labelAlign || this.parentLabelAlign();
10194 cls: 'form-group' //input-group
10201 type : this.inputType,
10202 cls : 'form-control',
10203 autocomplete: 'new-password',
10204 placeholder : this.placeholder || ''
10208 input.name = this.name;
10211 input.cls += ' input-' + this.size;
10214 if (this.disabled) {
10215 input.disabled=true;
10218 var inputblock = input;
10220 if(this.hasFeedback && !this.allowBlank){
10224 cls: 'glyphicon form-control-feedback'
10227 if(this.removable && !this.editable && !this.tickable){
10229 cls : 'has-feedback',
10235 cls : 'roo-combo-removable-btn close'
10242 cls : 'has-feedback',
10251 if(this.removable && !this.editable && !this.tickable){
10253 cls : 'roo-removable',
10259 cls : 'roo-combo-removable-btn close'
10266 if (this.before || this.after) {
10269 cls : 'input-group',
10273 inputblock.cn.push({
10275 cls : 'input-group-addon',
10280 inputblock.cn.push(input);
10282 if(this.hasFeedback && !this.allowBlank){
10283 inputblock.cls += ' has-feedback';
10284 inputblock.cn.push(feedback);
10288 inputblock.cn.push({
10290 cls : 'input-group-addon',
10303 cls: 'form-hidden-field'
10317 cls: 'form-hidden-field'
10321 cls: 'roo-select2-choices',
10325 cls: 'roo-select2-search-field',
10338 cls: 'roo-select2-container input-group',
10343 // cls: 'typeahead typeahead-long dropdown-menu',
10344 // style: 'display:none'
10349 if(!this.multiple && this.showToggleBtn){
10355 if (this.caret != false) {
10358 cls: 'fa fa-' + this.caret
10365 cls : 'input-group-addon btn dropdown-toggle',
10370 cls: 'combobox-clear',
10384 combobox.cls += ' roo-select2-container-multi';
10387 if (align ==='left' && this.fieldLabel.length) {
10389 cfg.cls += ' roo-form-group-label-left';
10394 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10395 tooltip : 'This field is required'
10400 cls : 'control-label',
10401 html : this.fieldLabel
10413 var labelCfg = cfg.cn[1];
10414 var contentCfg = cfg.cn[2];
10416 if(this.indicatorpos == 'right'){
10421 cls : 'control-label',
10425 html : this.fieldLabel
10429 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10430 tooltip : 'This field is required'
10443 labelCfg = cfg.cn[0];
10444 contentCfg = cfg.cn[1];
10447 if(this.labelWidth > 12){
10448 labelCfg.style = "width: " + this.labelWidth + 'px';
10451 if(this.labelWidth < 13 && this.labelmd == 0){
10452 this.labelmd = this.labelWidth;
10455 if(this.labellg > 0){
10456 labelCfg.cls += ' col-lg-' + this.labellg;
10457 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10460 if(this.labelmd > 0){
10461 labelCfg.cls += ' col-md-' + this.labelmd;
10462 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10465 if(this.labelsm > 0){
10466 labelCfg.cls += ' col-sm-' + this.labelsm;
10467 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10470 if(this.labelxs > 0){
10471 labelCfg.cls += ' col-xs-' + this.labelxs;
10472 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10475 } else if ( this.fieldLabel.length) {
10476 // Roo.log(" label");
10480 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10481 tooltip : 'This field is required'
10485 //cls : 'input-group-addon',
10486 html : this.fieldLabel
10494 if(this.indicatorpos == 'right'){
10502 html : this.fieldLabel
10506 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10507 tooltip : 'This field is required'
10520 // Roo.log(" no label && no align");
10527 ['xs','sm','md','lg'].map(function(size){
10528 if (settings[size]) {
10529 cfg.cls += ' col-' + size + '-' + settings[size];
10540 onResize : function(w, h){
10541 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10542 // if(typeof w == 'number'){
10543 // var x = w - this.trigger.getWidth();
10544 // this.inputEl().setWidth(this.adjustWidth('input', x));
10545 // this.trigger.setStyle('left', x+'px');
10550 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10553 getResizeEl : function(){
10554 return this.inputEl();
10558 getPositionEl : function(){
10559 return this.inputEl();
10563 alignErrorIcon : function(){
10564 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10568 initEvents : function(){
10572 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10573 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10574 if(!this.multiple && this.showToggleBtn){
10575 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10576 if(this.hideTrigger){
10577 this.trigger.setDisplayed(false);
10579 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10583 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10586 if(this.removable && !this.editable && !this.tickable){
10587 var close = this.closeTriggerEl();
10590 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10591 close.on('click', this.removeBtnClick, this, close);
10595 //this.trigger.addClassOnOver('x-form-trigger-over');
10596 //this.trigger.addClassOnClick('x-form-trigger-click');
10599 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10603 closeTriggerEl : function()
10605 var close = this.el.select('.roo-combo-removable-btn', true).first();
10606 return close ? close : false;
10609 removeBtnClick : function(e, h, el)
10611 e.preventDefault();
10613 if(this.fireEvent("remove", this) !== false){
10615 this.fireEvent("afterremove", this)
10619 createList : function()
10621 this.list = Roo.get(document.body).createChild({
10623 cls: 'typeahead typeahead-long dropdown-menu',
10624 style: 'display:none'
10627 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10632 initTrigger : function(){
10637 onDestroy : function(){
10639 this.trigger.removeAllListeners();
10640 // this.trigger.remove();
10643 // this.wrap.remove();
10645 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10649 onFocus : function(){
10650 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10652 if(!this.mimicing){
10653 this.wrap.addClass('x-trigger-wrap-focus');
10654 this.mimicing = true;
10655 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10656 if(this.monitorTab){
10657 this.el.on("keydown", this.checkTab, this);
10664 checkTab : function(e){
10665 if(e.getKey() == e.TAB){
10666 this.triggerBlur();
10671 onBlur : function(){
10676 mimicBlur : function(e, t){
10678 if(!this.wrap.contains(t) && this.validateBlur()){
10679 this.triggerBlur();
10685 triggerBlur : function(){
10686 this.mimicing = false;
10687 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10688 if(this.monitorTab){
10689 this.el.un("keydown", this.checkTab, this);
10691 //this.wrap.removeClass('x-trigger-wrap-focus');
10692 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10696 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10697 validateBlur : function(e, t){
10702 onDisable : function(){
10703 this.inputEl().dom.disabled = true;
10704 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10706 // this.wrap.addClass('x-item-disabled');
10711 onEnable : function(){
10712 this.inputEl().dom.disabled = false;
10713 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10715 // this.el.removeClass('x-item-disabled');
10720 onShow : function(){
10721 var ae = this.getActionEl();
10724 ae.dom.style.display = '';
10725 ae.dom.style.visibility = 'visible';
10731 onHide : function(){
10732 var ae = this.getActionEl();
10733 ae.dom.style.display = 'none';
10737 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10738 * by an implementing function.
10740 * @param {EventObject} e
10742 onTriggerClick : Roo.emptyFn
10746 * Ext JS Library 1.1.1
10747 * Copyright(c) 2006-2007, Ext JS, LLC.
10749 * Originally Released Under LGPL - original licence link has changed is not relivant.
10752 * <script type="text/javascript">
10757 * @class Roo.data.SortTypes
10759 * Defines the default sorting (casting?) comparison functions used when sorting data.
10761 Roo.data.SortTypes = {
10763 * Default sort that does nothing
10764 * @param {Mixed} s The value being converted
10765 * @return {Mixed} The comparison value
10767 none : function(s){
10772 * The regular expression used to strip tags
10776 stripTagsRE : /<\/?[^>]+>/gi,
10779 * Strips all HTML tags to sort on text only
10780 * @param {Mixed} s The value being converted
10781 * @return {String} The comparison value
10783 asText : function(s){
10784 return String(s).replace(this.stripTagsRE, "");
10788 * Strips all HTML tags to sort on text only - Case insensitive
10789 * @param {Mixed} s The value being converted
10790 * @return {String} The comparison value
10792 asUCText : function(s){
10793 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10797 * Case insensitive string
10798 * @param {Mixed} s The value being converted
10799 * @return {String} The comparison value
10801 asUCString : function(s) {
10802 return String(s).toUpperCase();
10807 * @param {Mixed} s The value being converted
10808 * @return {Number} The comparison value
10810 asDate : function(s) {
10814 if(s instanceof Date){
10815 return s.getTime();
10817 return Date.parse(String(s));
10822 * @param {Mixed} s The value being converted
10823 * @return {Float} The comparison value
10825 asFloat : function(s) {
10826 var val = parseFloat(String(s).replace(/,/g, ""));
10835 * @param {Mixed} s The value being converted
10836 * @return {Number} The comparison value
10838 asInt : function(s) {
10839 var val = parseInt(String(s).replace(/,/g, ""));
10847 * Ext JS Library 1.1.1
10848 * Copyright(c) 2006-2007, Ext JS, LLC.
10850 * Originally Released Under LGPL - original licence link has changed is not relivant.
10853 * <script type="text/javascript">
10857 * @class Roo.data.Record
10858 * Instances of this class encapsulate both record <em>definition</em> information, and record
10859 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10860 * to access Records cached in an {@link Roo.data.Store} object.<br>
10862 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10863 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10866 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10868 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10869 * {@link #create}. The parameters are the same.
10870 * @param {Array} data An associative Array of data values keyed by the field name.
10871 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10872 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10873 * not specified an integer id is generated.
10875 Roo.data.Record = function(data, id){
10876 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10881 * Generate a constructor for a specific record layout.
10882 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10883 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10884 * Each field definition object may contain the following properties: <ul>
10885 * <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,
10886 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10887 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10888 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10889 * is being used, then this is a string containing the javascript expression to reference the data relative to
10890 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10891 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10892 * this may be omitted.</p></li>
10893 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10894 * <ul><li>auto (Default, implies no conversion)</li>
10899 * <li>date</li></ul></p></li>
10900 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10901 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10902 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10903 * by the Reader into an object that will be stored in the Record. It is passed the
10904 * following parameters:<ul>
10905 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10907 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10909 * <br>usage:<br><pre><code>
10910 var TopicRecord = Roo.data.Record.create(
10911 {name: 'title', mapping: 'topic_title'},
10912 {name: 'author', mapping: 'username'},
10913 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10914 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10915 {name: 'lastPoster', mapping: 'user2'},
10916 {name: 'excerpt', mapping: 'post_text'}
10919 var myNewRecord = new TopicRecord({
10920 title: 'Do my job please',
10923 lastPost: new Date(),
10924 lastPoster: 'Animal',
10925 excerpt: 'No way dude!'
10927 myStore.add(myNewRecord);
10932 Roo.data.Record.create = function(o){
10933 var f = function(){
10934 f.superclass.constructor.apply(this, arguments);
10936 Roo.extend(f, Roo.data.Record);
10937 var p = f.prototype;
10938 p.fields = new Roo.util.MixedCollection(false, function(field){
10941 for(var i = 0, len = o.length; i < len; i++){
10942 p.fields.add(new Roo.data.Field(o[i]));
10944 f.getField = function(name){
10945 return p.fields.get(name);
10950 Roo.data.Record.AUTO_ID = 1000;
10951 Roo.data.Record.EDIT = 'edit';
10952 Roo.data.Record.REJECT = 'reject';
10953 Roo.data.Record.COMMIT = 'commit';
10955 Roo.data.Record.prototype = {
10957 * Readonly flag - true if this record has been modified.
10966 join : function(store){
10967 this.store = store;
10971 * Set the named field to the specified value.
10972 * @param {String} name The name of the field to set.
10973 * @param {Object} value The value to set the field to.
10975 set : function(name, value){
10976 if(this.data[name] == value){
10980 if(!this.modified){
10981 this.modified = {};
10983 if(typeof this.modified[name] == 'undefined'){
10984 this.modified[name] = this.data[name];
10986 this.data[name] = value;
10987 if(!this.editing && this.store){
10988 this.store.afterEdit(this);
10993 * Get the value of the named field.
10994 * @param {String} name The name of the field to get the value of.
10995 * @return {Object} The value of the field.
10997 get : function(name){
10998 return this.data[name];
11002 beginEdit : function(){
11003 this.editing = true;
11004 this.modified = {};
11008 cancelEdit : function(){
11009 this.editing = false;
11010 delete this.modified;
11014 endEdit : function(){
11015 this.editing = false;
11016 if(this.dirty && this.store){
11017 this.store.afterEdit(this);
11022 * Usually called by the {@link Roo.data.Store} which owns the Record.
11023 * Rejects all changes made to the Record since either creation, or the last commit operation.
11024 * Modified fields are reverted to their original values.
11026 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11027 * of reject operations.
11029 reject : function(){
11030 var m = this.modified;
11032 if(typeof m[n] != "function"){
11033 this.data[n] = m[n];
11036 this.dirty = false;
11037 delete this.modified;
11038 this.editing = false;
11040 this.store.afterReject(this);
11045 * Usually called by the {@link Roo.data.Store} which owns the Record.
11046 * Commits all changes made to the Record since either creation, or the last commit operation.
11048 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11049 * of commit operations.
11051 commit : function(){
11052 this.dirty = false;
11053 delete this.modified;
11054 this.editing = false;
11056 this.store.afterCommit(this);
11061 hasError : function(){
11062 return this.error != null;
11066 clearError : function(){
11071 * Creates a copy of this record.
11072 * @param {String} id (optional) A new record id if you don't want to use this record's id
11075 copy : function(newId) {
11076 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11080 * Ext JS Library 1.1.1
11081 * Copyright(c) 2006-2007, Ext JS, LLC.
11083 * Originally Released Under LGPL - original licence link has changed is not relivant.
11086 * <script type="text/javascript">
11092 * @class Roo.data.Store
11093 * @extends Roo.util.Observable
11094 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11095 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11097 * 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
11098 * has no knowledge of the format of the data returned by the Proxy.<br>
11100 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11101 * instances from the data object. These records are cached and made available through accessor functions.
11103 * Creates a new Store.
11104 * @param {Object} config A config object containing the objects needed for the Store to access data,
11105 * and read the data into Records.
11107 Roo.data.Store = function(config){
11108 this.data = new Roo.util.MixedCollection(false);
11109 this.data.getKey = function(o){
11112 this.baseParams = {};
11114 this.paramNames = {
11119 "multisort" : "_multisort"
11122 if(config && config.data){
11123 this.inlineData = config.data;
11124 delete config.data;
11127 Roo.apply(this, config);
11129 if(this.reader){ // reader passed
11130 this.reader = Roo.factory(this.reader, Roo.data);
11131 this.reader.xmodule = this.xmodule || false;
11132 if(!this.recordType){
11133 this.recordType = this.reader.recordType;
11135 if(this.reader.onMetaChange){
11136 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11140 if(this.recordType){
11141 this.fields = this.recordType.prototype.fields;
11143 this.modified = [];
11147 * @event datachanged
11148 * Fires when the data cache has changed, and a widget which is using this Store
11149 * as a Record cache should refresh its view.
11150 * @param {Store} this
11152 datachanged : true,
11154 * @event metachange
11155 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11156 * @param {Store} this
11157 * @param {Object} meta The JSON metadata
11162 * Fires when Records have been added to the Store
11163 * @param {Store} this
11164 * @param {Roo.data.Record[]} records The array of Records added
11165 * @param {Number} index The index at which the record(s) were added
11170 * Fires when a Record has been removed from the Store
11171 * @param {Store} this
11172 * @param {Roo.data.Record} record The Record that was removed
11173 * @param {Number} index The index at which the record was removed
11178 * Fires when a Record has been updated
11179 * @param {Store} this
11180 * @param {Roo.data.Record} record The Record that was updated
11181 * @param {String} operation The update operation being performed. Value may be one of:
11183 Roo.data.Record.EDIT
11184 Roo.data.Record.REJECT
11185 Roo.data.Record.COMMIT
11191 * Fires when the data cache has been cleared.
11192 * @param {Store} this
11196 * @event beforeload
11197 * Fires before a request is made for a new data object. If the beforeload handler returns false
11198 * the load action will be canceled.
11199 * @param {Store} this
11200 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11204 * @event beforeloadadd
11205 * Fires after a new set of Records has been loaded.
11206 * @param {Store} this
11207 * @param {Roo.data.Record[]} records The Records that were loaded
11208 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11210 beforeloadadd : true,
11213 * Fires after a new set of Records has been loaded, before they are added to the store.
11214 * @param {Store} this
11215 * @param {Roo.data.Record[]} records The Records that were loaded
11216 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11217 * @params {Object} return from reader
11221 * @event loadexception
11222 * Fires if an exception occurs in the Proxy during loading.
11223 * Called with the signature of the Proxy's "loadexception" event.
11224 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11227 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11228 * @param {Object} load options
11229 * @param {Object} jsonData from your request (normally this contains the Exception)
11231 loadexception : true
11235 this.proxy = Roo.factory(this.proxy, Roo.data);
11236 this.proxy.xmodule = this.xmodule || false;
11237 this.relayEvents(this.proxy, ["loadexception"]);
11239 this.sortToggle = {};
11240 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11242 Roo.data.Store.superclass.constructor.call(this);
11244 if(this.inlineData){
11245 this.loadData(this.inlineData);
11246 delete this.inlineData;
11250 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11252 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11253 * without a remote query - used by combo/forms at present.
11257 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11260 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11263 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11264 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11267 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11268 * on any HTTP request
11271 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11274 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11278 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11279 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11281 remoteSort : false,
11284 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11285 * loaded or when a record is removed. (defaults to false).
11287 pruneModifiedRecords : false,
11290 lastOptions : null,
11293 * Add Records to the Store and fires the add event.
11294 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11296 add : function(records){
11297 records = [].concat(records);
11298 for(var i = 0, len = records.length; i < len; i++){
11299 records[i].join(this);
11301 var index = this.data.length;
11302 this.data.addAll(records);
11303 this.fireEvent("add", this, records, index);
11307 * Remove a Record from the Store and fires the remove event.
11308 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11310 remove : function(record){
11311 var index = this.data.indexOf(record);
11312 this.data.removeAt(index);
11314 if(this.pruneModifiedRecords){
11315 this.modified.remove(record);
11317 this.fireEvent("remove", this, record, index);
11321 * Remove all Records from the Store and fires the clear event.
11323 removeAll : function(){
11325 if(this.pruneModifiedRecords){
11326 this.modified = [];
11328 this.fireEvent("clear", this);
11332 * Inserts Records to the Store at the given index and fires the add event.
11333 * @param {Number} index The start index at which to insert the passed Records.
11334 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11336 insert : function(index, records){
11337 records = [].concat(records);
11338 for(var i = 0, len = records.length; i < len; i++){
11339 this.data.insert(index, records[i]);
11340 records[i].join(this);
11342 this.fireEvent("add", this, records, index);
11346 * Get the index within the cache of the passed Record.
11347 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11348 * @return {Number} The index of the passed Record. Returns -1 if not found.
11350 indexOf : function(record){
11351 return this.data.indexOf(record);
11355 * Get the index within the cache of the Record with the passed id.
11356 * @param {String} id The id of the Record to find.
11357 * @return {Number} The index of the Record. Returns -1 if not found.
11359 indexOfId : function(id){
11360 return this.data.indexOfKey(id);
11364 * Get the Record with the specified id.
11365 * @param {String} id The id of the Record to find.
11366 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11368 getById : function(id){
11369 return this.data.key(id);
11373 * Get the Record at the specified index.
11374 * @param {Number} index The index of the Record to find.
11375 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11377 getAt : function(index){
11378 return this.data.itemAt(index);
11382 * Returns a range of Records between specified indices.
11383 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11384 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11385 * @return {Roo.data.Record[]} An array of Records
11387 getRange : function(start, end){
11388 return this.data.getRange(start, end);
11392 storeOptions : function(o){
11393 o = Roo.apply({}, o);
11396 this.lastOptions = o;
11400 * Loads the Record cache from the configured Proxy using the configured Reader.
11402 * If using remote paging, then the first load call must specify the <em>start</em>
11403 * and <em>limit</em> properties in the options.params property to establish the initial
11404 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11406 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11407 * and this call will return before the new data has been loaded. Perform any post-processing
11408 * in a callback function, or in a "load" event handler.</strong>
11410 * @param {Object} options An object containing properties which control loading options:<ul>
11411 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11412 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11413 * passed the following arguments:<ul>
11414 * <li>r : Roo.data.Record[]</li>
11415 * <li>options: Options object from the load call</li>
11416 * <li>success: Boolean success indicator</li></ul></li>
11417 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11418 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11421 load : function(options){
11422 options = options || {};
11423 if(this.fireEvent("beforeload", this, options) !== false){
11424 this.storeOptions(options);
11425 var p = Roo.apply(options.params || {}, this.baseParams);
11426 // if meta was not loaded from remote source.. try requesting it.
11427 if (!this.reader.metaFromRemote) {
11428 p._requestMeta = 1;
11430 if(this.sortInfo && this.remoteSort){
11431 var pn = this.paramNames;
11432 p[pn["sort"]] = this.sortInfo.field;
11433 p[pn["dir"]] = this.sortInfo.direction;
11435 if (this.multiSort) {
11436 var pn = this.paramNames;
11437 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11440 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11445 * Reloads the Record cache from the configured Proxy using the configured Reader and
11446 * the options from the last load operation performed.
11447 * @param {Object} options (optional) An object containing properties which may override the options
11448 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11449 * the most recently used options are reused).
11451 reload : function(options){
11452 this.load(Roo.applyIf(options||{}, this.lastOptions));
11456 // Called as a callback by the Reader during a load operation.
11457 loadRecords : function(o, options, success){
11458 if(!o || success === false){
11459 if(success !== false){
11460 this.fireEvent("load", this, [], options, o);
11462 if(options.callback){
11463 options.callback.call(options.scope || this, [], options, false);
11467 // if data returned failure - throw an exception.
11468 if (o.success === false) {
11469 // show a message if no listener is registered.
11470 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11471 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11473 // loadmask wil be hooked into this..
11474 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11477 var r = o.records, t = o.totalRecords || r.length;
11479 this.fireEvent("beforeloadadd", this, r, options, o);
11481 if(!options || options.add !== true){
11482 if(this.pruneModifiedRecords){
11483 this.modified = [];
11485 for(var i = 0, len = r.length; i < len; i++){
11489 this.data = this.snapshot;
11490 delete this.snapshot;
11493 this.data.addAll(r);
11494 this.totalLength = t;
11496 this.fireEvent("datachanged", this);
11498 this.totalLength = Math.max(t, this.data.length+r.length);
11502 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11504 var e = new Roo.data.Record({});
11506 e.set(this.parent.displayField, this.parent.emptyTitle);
11507 e.set(this.parent.valueField, '');
11512 this.fireEvent("load", this, r, options, o);
11513 if(options.callback){
11514 options.callback.call(options.scope || this, r, options, true);
11520 * Loads data from a passed data block. A Reader which understands the format of the data
11521 * must have been configured in the constructor.
11522 * @param {Object} data The data block from which to read the Records. The format of the data expected
11523 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11524 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11526 loadData : function(o, append){
11527 var r = this.reader.readRecords(o);
11528 this.loadRecords(r, {add: append}, true);
11532 * Gets the number of cached records.
11534 * <em>If using paging, this may not be the total size of the dataset. If the data object
11535 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11536 * the data set size</em>
11538 getCount : function(){
11539 return this.data.length || 0;
11543 * Gets the total number of records in the dataset as returned by the server.
11545 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11546 * the dataset size</em>
11548 getTotalCount : function(){
11549 return this.totalLength || 0;
11553 * Returns the sort state of the Store as an object with two properties:
11555 field {String} The name of the field by which the Records are sorted
11556 direction {String} The sort order, "ASC" or "DESC"
11559 getSortState : function(){
11560 return this.sortInfo;
11564 applySort : function(){
11565 if(this.sortInfo && !this.remoteSort){
11566 var s = this.sortInfo, f = s.field;
11567 var st = this.fields.get(f).sortType;
11568 var fn = function(r1, r2){
11569 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11570 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11572 this.data.sort(s.direction, fn);
11573 if(this.snapshot && this.snapshot != this.data){
11574 this.snapshot.sort(s.direction, fn);
11580 * Sets the default sort column and order to be used by the next load operation.
11581 * @param {String} fieldName The name of the field to sort by.
11582 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11584 setDefaultSort : function(field, dir){
11585 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11589 * Sort the Records.
11590 * If remote sorting is used, the sort is performed on the server, and the cache is
11591 * reloaded. If local sorting is used, the cache is sorted internally.
11592 * @param {String} fieldName The name of the field to sort by.
11593 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11595 sort : function(fieldName, dir){
11596 var f = this.fields.get(fieldName);
11598 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11600 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11601 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11606 this.sortToggle[f.name] = dir;
11607 this.sortInfo = {field: f.name, direction: dir};
11608 if(!this.remoteSort){
11610 this.fireEvent("datachanged", this);
11612 this.load(this.lastOptions);
11617 * Calls the specified function for each of the Records in the cache.
11618 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11619 * Returning <em>false</em> aborts and exits the iteration.
11620 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11622 each : function(fn, scope){
11623 this.data.each(fn, scope);
11627 * Gets all records modified since the last commit. Modified records are persisted across load operations
11628 * (e.g., during paging).
11629 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11631 getModifiedRecords : function(){
11632 return this.modified;
11636 createFilterFn : function(property, value, anyMatch){
11637 if(!value.exec){ // not a regex
11638 value = String(value);
11639 if(value.length == 0){
11642 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11644 return function(r){
11645 return value.test(r.data[property]);
11650 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11651 * @param {String} property A field on your records
11652 * @param {Number} start The record index to start at (defaults to 0)
11653 * @param {Number} end The last record index to include (defaults to length - 1)
11654 * @return {Number} The sum
11656 sum : function(property, start, end){
11657 var rs = this.data.items, v = 0;
11658 start = start || 0;
11659 end = (end || end === 0) ? end : rs.length-1;
11661 for(var i = start; i <= end; i++){
11662 v += (rs[i].data[property] || 0);
11668 * Filter the records by a specified property.
11669 * @param {String} field A field on your records
11670 * @param {String/RegExp} value Either a string that the field
11671 * should start with or a RegExp to test against the field
11672 * @param {Boolean} anyMatch True to match any part not just the beginning
11674 filter : function(property, value, anyMatch){
11675 var fn = this.createFilterFn(property, value, anyMatch);
11676 return fn ? this.filterBy(fn) : this.clearFilter();
11680 * Filter by a function. The specified function will be called with each
11681 * record in this data source. If the function returns true the record is included,
11682 * otherwise it is filtered.
11683 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11684 * @param {Object} scope (optional) The scope of the function (defaults to this)
11686 filterBy : function(fn, scope){
11687 this.snapshot = this.snapshot || this.data;
11688 this.data = this.queryBy(fn, scope||this);
11689 this.fireEvent("datachanged", this);
11693 * Query the records by a specified property.
11694 * @param {String} field A field on your records
11695 * @param {String/RegExp} value Either a string that the field
11696 * should start with or a RegExp to test against the field
11697 * @param {Boolean} anyMatch True to match any part not just the beginning
11698 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11700 query : function(property, value, anyMatch){
11701 var fn = this.createFilterFn(property, value, anyMatch);
11702 return fn ? this.queryBy(fn) : this.data.clone();
11706 * Query by a function. The specified function will be called with each
11707 * record in this data source. If the function returns true the record is included
11709 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11710 * @param {Object} scope (optional) The scope of the function (defaults to this)
11711 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11713 queryBy : function(fn, scope){
11714 var data = this.snapshot || this.data;
11715 return data.filterBy(fn, scope||this);
11719 * Collects unique values for a particular dataIndex from this store.
11720 * @param {String} dataIndex The property to collect
11721 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11722 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11723 * @return {Array} An array of the unique values
11725 collect : function(dataIndex, allowNull, bypassFilter){
11726 var d = (bypassFilter === true && this.snapshot) ?
11727 this.snapshot.items : this.data.items;
11728 var v, sv, r = [], l = {};
11729 for(var i = 0, len = d.length; i < len; i++){
11730 v = d[i].data[dataIndex];
11732 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11741 * Revert to a view of the Record cache with no filtering applied.
11742 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11744 clearFilter : function(suppressEvent){
11745 if(this.snapshot && this.snapshot != this.data){
11746 this.data = this.snapshot;
11747 delete this.snapshot;
11748 if(suppressEvent !== true){
11749 this.fireEvent("datachanged", this);
11755 afterEdit : function(record){
11756 if(this.modified.indexOf(record) == -1){
11757 this.modified.push(record);
11759 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11763 afterReject : function(record){
11764 this.modified.remove(record);
11765 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11769 afterCommit : function(record){
11770 this.modified.remove(record);
11771 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11775 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11776 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11778 commitChanges : function(){
11779 var m = this.modified.slice(0);
11780 this.modified = [];
11781 for(var i = 0, len = m.length; i < len; i++){
11787 * Cancel outstanding changes on all changed records.
11789 rejectChanges : function(){
11790 var m = this.modified.slice(0);
11791 this.modified = [];
11792 for(var i = 0, len = m.length; i < len; i++){
11797 onMetaChange : function(meta, rtype, o){
11798 this.recordType = rtype;
11799 this.fields = rtype.prototype.fields;
11800 delete this.snapshot;
11801 this.sortInfo = meta.sortInfo || this.sortInfo;
11802 this.modified = [];
11803 this.fireEvent('metachange', this, this.reader.meta);
11806 moveIndex : function(data, type)
11808 var index = this.indexOf(data);
11810 var newIndex = index + type;
11814 this.insert(newIndex, data);
11819 * Ext JS Library 1.1.1
11820 * Copyright(c) 2006-2007, Ext JS, LLC.
11822 * Originally Released Under LGPL - original licence link has changed is not relivant.
11825 * <script type="text/javascript">
11829 * @class Roo.data.SimpleStore
11830 * @extends Roo.data.Store
11831 * Small helper class to make creating Stores from Array data easier.
11832 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11833 * @cfg {Array} fields An array of field definition objects, or field name strings.
11834 * @cfg {Array} data The multi-dimensional array of data
11836 * @param {Object} config
11838 Roo.data.SimpleStore = function(config){
11839 Roo.data.SimpleStore.superclass.constructor.call(this, {
11841 reader: new Roo.data.ArrayReader({
11844 Roo.data.Record.create(config.fields)
11846 proxy : new Roo.data.MemoryProxy(config.data)
11850 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11852 * Ext JS Library 1.1.1
11853 * Copyright(c) 2006-2007, Ext JS, LLC.
11855 * Originally Released Under LGPL - original licence link has changed is not relivant.
11858 * <script type="text/javascript">
11863 * @extends Roo.data.Store
11864 * @class Roo.data.JsonStore
11865 * Small helper class to make creating Stores for JSON data easier. <br/>
11867 var store = new Roo.data.JsonStore({
11868 url: 'get-images.php',
11870 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11873 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11874 * JsonReader and HttpProxy (unless inline data is provided).</b>
11875 * @cfg {Array} fields An array of field definition objects, or field name strings.
11877 * @param {Object} config
11879 Roo.data.JsonStore = function(c){
11880 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11881 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11882 reader: new Roo.data.JsonReader(c, c.fields)
11885 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11887 * Ext JS Library 1.1.1
11888 * Copyright(c) 2006-2007, Ext JS, LLC.
11890 * Originally Released Under LGPL - original licence link has changed is not relivant.
11893 * <script type="text/javascript">
11897 Roo.data.Field = function(config){
11898 if(typeof config == "string"){
11899 config = {name: config};
11901 Roo.apply(this, config);
11904 this.type = "auto";
11907 var st = Roo.data.SortTypes;
11908 // named sortTypes are supported, here we look them up
11909 if(typeof this.sortType == "string"){
11910 this.sortType = st[this.sortType];
11913 // set default sortType for strings and dates
11914 if(!this.sortType){
11917 this.sortType = st.asUCString;
11920 this.sortType = st.asDate;
11923 this.sortType = st.none;
11928 var stripRe = /[\$,%]/g;
11930 // prebuilt conversion function for this field, instead of
11931 // switching every time we're reading a value
11933 var cv, dateFormat = this.dateFormat;
11938 cv = function(v){ return v; };
11941 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11945 return v !== undefined && v !== null && v !== '' ?
11946 parseInt(String(v).replace(stripRe, ""), 10) : '';
11951 return v !== undefined && v !== null && v !== '' ?
11952 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11957 cv = function(v){ return v === true || v === "true" || v == 1; };
11964 if(v instanceof Date){
11968 if(dateFormat == "timestamp"){
11969 return new Date(v*1000);
11971 return Date.parseDate(v, dateFormat);
11973 var parsed = Date.parse(v);
11974 return parsed ? new Date(parsed) : null;
11983 Roo.data.Field.prototype = {
11991 * Ext JS Library 1.1.1
11992 * Copyright(c) 2006-2007, Ext JS, LLC.
11994 * Originally Released Under LGPL - original licence link has changed is not relivant.
11997 * <script type="text/javascript">
12000 // Base class for reading structured data from a data source. This class is intended to be
12001 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12004 * @class Roo.data.DataReader
12005 * Base class for reading structured data from a data source. This class is intended to be
12006 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12009 Roo.data.DataReader = function(meta, recordType){
12013 this.recordType = recordType instanceof Array ?
12014 Roo.data.Record.create(recordType) : recordType;
12017 Roo.data.DataReader.prototype = {
12019 * Create an empty record
12020 * @param {Object} data (optional) - overlay some values
12021 * @return {Roo.data.Record} record created.
12023 newRow : function(d) {
12025 this.recordType.prototype.fields.each(function(c) {
12027 case 'int' : da[c.name] = 0; break;
12028 case 'date' : da[c.name] = new Date(); break;
12029 case 'float' : da[c.name] = 0.0; break;
12030 case 'boolean' : da[c.name] = false; break;
12031 default : da[c.name] = ""; break;
12035 return new this.recordType(Roo.apply(da, d));
12040 * Ext JS Library 1.1.1
12041 * Copyright(c) 2006-2007, Ext JS, LLC.
12043 * Originally Released Under LGPL - original licence link has changed is not relivant.
12046 * <script type="text/javascript">
12050 * @class Roo.data.DataProxy
12051 * @extends Roo.data.Observable
12052 * This class is an abstract base class for implementations which provide retrieval of
12053 * unformatted data objects.<br>
12055 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12056 * (of the appropriate type which knows how to parse the data object) to provide a block of
12057 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12059 * Custom implementations must implement the load method as described in
12060 * {@link Roo.data.HttpProxy#load}.
12062 Roo.data.DataProxy = function(){
12065 * @event beforeload
12066 * Fires before a network request is made to retrieve a data object.
12067 * @param {Object} This DataProxy object.
12068 * @param {Object} params The params parameter to the load function.
12073 * Fires before the load method's callback is called.
12074 * @param {Object} This DataProxy object.
12075 * @param {Object} o The data object.
12076 * @param {Object} arg The callback argument object passed to the load function.
12080 * @event loadexception
12081 * Fires if an Exception occurs during data retrieval.
12082 * @param {Object} This DataProxy object.
12083 * @param {Object} o The data object.
12084 * @param {Object} arg The callback argument object passed to the load function.
12085 * @param {Object} e The Exception.
12087 loadexception : true
12089 Roo.data.DataProxy.superclass.constructor.call(this);
12092 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12095 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12099 * Ext JS Library 1.1.1
12100 * Copyright(c) 2006-2007, Ext JS, LLC.
12102 * Originally Released Under LGPL - original licence link has changed is not relivant.
12105 * <script type="text/javascript">
12108 * @class Roo.data.MemoryProxy
12109 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12110 * to the Reader when its load method is called.
12112 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12114 Roo.data.MemoryProxy = function(data){
12118 Roo.data.MemoryProxy.superclass.constructor.call(this);
12122 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12125 * Load data from the requested source (in this case an in-memory
12126 * data object passed to the constructor), read the data object into
12127 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12128 * process that block using the passed callback.
12129 * @param {Object} params This parameter is not used by the MemoryProxy class.
12130 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12131 * object into a block of Roo.data.Records.
12132 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12133 * The function must be passed <ul>
12134 * <li>The Record block object</li>
12135 * <li>The "arg" argument from the load function</li>
12136 * <li>A boolean success indicator</li>
12138 * @param {Object} scope The scope in which to call the callback
12139 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12141 load : function(params, reader, callback, scope, arg){
12142 params = params || {};
12145 result = reader.readRecords(this.data);
12147 this.fireEvent("loadexception", this, arg, null, e);
12148 callback.call(scope, null, arg, false);
12151 callback.call(scope, result, arg, true);
12155 update : function(params, records){
12160 * Ext JS Library 1.1.1
12161 * Copyright(c) 2006-2007, Ext JS, LLC.
12163 * Originally Released Under LGPL - original licence link has changed is not relivant.
12166 * <script type="text/javascript">
12169 * @class Roo.data.HttpProxy
12170 * @extends Roo.data.DataProxy
12171 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12172 * configured to reference a certain URL.<br><br>
12174 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12175 * from which the running page was served.<br><br>
12177 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12179 * Be aware that to enable the browser to parse an XML document, the server must set
12180 * the Content-Type header in the HTTP response to "text/xml".
12182 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12183 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12184 * will be used to make the request.
12186 Roo.data.HttpProxy = function(conn){
12187 Roo.data.HttpProxy.superclass.constructor.call(this);
12188 // is conn a conn config or a real conn?
12190 this.useAjax = !conn || !conn.events;
12194 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12195 // thse are take from connection...
12198 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12201 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12202 * extra parameters to each request made by this object. (defaults to undefined)
12205 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12206 * to each request made by this object. (defaults to undefined)
12209 * @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)
12212 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12215 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12221 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12225 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12226 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12227 * a finer-grained basis than the DataProxy events.
12229 getConnection : function(){
12230 return this.useAjax ? Roo.Ajax : this.conn;
12234 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12235 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12236 * process that block using the passed callback.
12237 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12238 * for the request to the remote server.
12239 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12240 * object into a block of Roo.data.Records.
12241 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12242 * The function must be passed <ul>
12243 * <li>The Record block object</li>
12244 * <li>The "arg" argument from the load function</li>
12245 * <li>A boolean success indicator</li>
12247 * @param {Object} scope The scope in which to call the callback
12248 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12250 load : function(params, reader, callback, scope, arg){
12251 if(this.fireEvent("beforeload", this, params) !== false){
12253 params : params || {},
12255 callback : callback,
12260 callback : this.loadResponse,
12264 Roo.applyIf(o, this.conn);
12265 if(this.activeRequest){
12266 Roo.Ajax.abort(this.activeRequest);
12268 this.activeRequest = Roo.Ajax.request(o);
12270 this.conn.request(o);
12273 callback.call(scope||this, null, arg, false);
12278 loadResponse : function(o, success, response){
12279 delete this.activeRequest;
12281 this.fireEvent("loadexception", this, o, response);
12282 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12287 result = o.reader.read(response);
12289 this.fireEvent("loadexception", this, o, response, e);
12290 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12294 this.fireEvent("load", this, o, o.request.arg);
12295 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12299 update : function(dataSet){
12304 updateResponse : function(dataSet){
12309 * Ext JS Library 1.1.1
12310 * Copyright(c) 2006-2007, Ext JS, LLC.
12312 * Originally Released Under LGPL - original licence link has changed is not relivant.
12315 * <script type="text/javascript">
12319 * @class Roo.data.ScriptTagProxy
12320 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12321 * other than the originating domain of the running page.<br><br>
12323 * <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
12324 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12326 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12327 * source code that is used as the source inside a <script> tag.<br><br>
12329 * In order for the browser to process the returned data, the server must wrap the data object
12330 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12331 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12332 * depending on whether the callback name was passed:
12335 boolean scriptTag = false;
12336 String cb = request.getParameter("callback");
12339 response.setContentType("text/javascript");
12341 response.setContentType("application/x-json");
12343 Writer out = response.getWriter();
12345 out.write(cb + "(");
12347 out.print(dataBlock.toJsonString());
12354 * @param {Object} config A configuration object.
12356 Roo.data.ScriptTagProxy = function(config){
12357 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12358 Roo.apply(this, config);
12359 this.head = document.getElementsByTagName("head")[0];
12362 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12364 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12366 * @cfg {String} url The URL from which to request the data object.
12369 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12373 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12374 * the server the name of the callback function set up by the load call to process the returned data object.
12375 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12376 * javascript output which calls this named function passing the data object as its only parameter.
12378 callbackParam : "callback",
12380 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12381 * name to the request.
12386 * Load data from the configured URL, read the data object into
12387 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12388 * process that block using the passed callback.
12389 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12390 * for the request to the remote server.
12391 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12392 * object into a block of Roo.data.Records.
12393 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12394 * The function must be passed <ul>
12395 * <li>The Record block object</li>
12396 * <li>The "arg" argument from the load function</li>
12397 * <li>A boolean success indicator</li>
12399 * @param {Object} scope The scope in which to call the callback
12400 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12402 load : function(params, reader, callback, scope, arg){
12403 if(this.fireEvent("beforeload", this, params) !== false){
12405 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12407 var url = this.url;
12408 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12410 url += "&_dc=" + (new Date().getTime());
12412 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12415 cb : "stcCallback"+transId,
12416 scriptId : "stcScript"+transId,
12420 callback : callback,
12426 window[trans.cb] = function(o){
12427 conn.handleResponse(o, trans);
12430 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12432 if(this.autoAbort !== false){
12436 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12438 var script = document.createElement("script");
12439 script.setAttribute("src", url);
12440 script.setAttribute("type", "text/javascript");
12441 script.setAttribute("id", trans.scriptId);
12442 this.head.appendChild(script);
12444 this.trans = trans;
12446 callback.call(scope||this, null, arg, false);
12451 isLoading : function(){
12452 return this.trans ? true : false;
12456 * Abort the current server request.
12458 abort : function(){
12459 if(this.isLoading()){
12460 this.destroyTrans(this.trans);
12465 destroyTrans : function(trans, isLoaded){
12466 this.head.removeChild(document.getElementById(trans.scriptId));
12467 clearTimeout(trans.timeoutId);
12469 window[trans.cb] = undefined;
12471 delete window[trans.cb];
12474 // if hasn't been loaded, wait for load to remove it to prevent script error
12475 window[trans.cb] = function(){
12476 window[trans.cb] = undefined;
12478 delete window[trans.cb];
12485 handleResponse : function(o, trans){
12486 this.trans = false;
12487 this.destroyTrans(trans, true);
12490 result = trans.reader.readRecords(o);
12492 this.fireEvent("loadexception", this, o, trans.arg, e);
12493 trans.callback.call(trans.scope||window, null, trans.arg, false);
12496 this.fireEvent("load", this, o, trans.arg);
12497 trans.callback.call(trans.scope||window, result, trans.arg, true);
12501 handleFailure : function(trans){
12502 this.trans = false;
12503 this.destroyTrans(trans, false);
12504 this.fireEvent("loadexception", this, null, trans.arg);
12505 trans.callback.call(trans.scope||window, null, trans.arg, false);
12509 * Ext JS Library 1.1.1
12510 * Copyright(c) 2006-2007, Ext JS, LLC.
12512 * Originally Released Under LGPL - original licence link has changed is not relivant.
12515 * <script type="text/javascript">
12519 * @class Roo.data.JsonReader
12520 * @extends Roo.data.DataReader
12521 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12522 * based on mappings in a provided Roo.data.Record constructor.
12524 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12525 * in the reply previously.
12530 var RecordDef = Roo.data.Record.create([
12531 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12532 {name: 'occupation'} // This field will use "occupation" as the mapping.
12534 var myReader = new Roo.data.JsonReader({
12535 totalProperty: "results", // The property which contains the total dataset size (optional)
12536 root: "rows", // The property which contains an Array of row objects
12537 id: "id" // The property within each row object that provides an ID for the record (optional)
12541 * This would consume a JSON file like this:
12543 { 'results': 2, 'rows': [
12544 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12545 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12548 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12549 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12550 * paged from the remote server.
12551 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12552 * @cfg {String} root name of the property which contains the Array of row objects.
12553 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12554 * @cfg {Array} fields Array of field definition objects
12556 * Create a new JsonReader
12557 * @param {Object} meta Metadata configuration options
12558 * @param {Object} recordType Either an Array of field definition objects,
12559 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12561 Roo.data.JsonReader = function(meta, recordType){
12564 // set some defaults:
12565 Roo.applyIf(meta, {
12566 totalProperty: 'total',
12567 successProperty : 'success',
12572 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12574 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12577 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12578 * Used by Store query builder to append _requestMeta to params.
12581 metaFromRemote : false,
12583 * This method is only used by a DataProxy which has retrieved data from a remote server.
12584 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12585 * @return {Object} data A data block which is used by an Roo.data.Store object as
12586 * a cache of Roo.data.Records.
12588 read : function(response){
12589 var json = response.responseText;
12591 var o = /* eval:var:o */ eval("("+json+")");
12593 throw {message: "JsonReader.read: Json object not found"};
12599 this.metaFromRemote = true;
12600 this.meta = o.metaData;
12601 this.recordType = Roo.data.Record.create(o.metaData.fields);
12602 this.onMetaChange(this.meta, this.recordType, o);
12604 return this.readRecords(o);
12607 // private function a store will implement
12608 onMetaChange : function(meta, recordType, o){
12615 simpleAccess: function(obj, subsc) {
12622 getJsonAccessor: function(){
12624 return function(expr) {
12626 return(re.test(expr))
12627 ? new Function("obj", "return obj." + expr)
12632 return Roo.emptyFn;
12637 * Create a data block containing Roo.data.Records from an XML document.
12638 * @param {Object} o An object which contains an Array of row objects in the property specified
12639 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12640 * which contains the total size of the dataset.
12641 * @return {Object} data A data block which is used by an Roo.data.Store object as
12642 * a cache of Roo.data.Records.
12644 readRecords : function(o){
12646 * After any data loads, the raw JSON data is available for further custom processing.
12650 var s = this.meta, Record = this.recordType,
12651 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12653 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12655 if(s.totalProperty) {
12656 this.getTotal = this.getJsonAccessor(s.totalProperty);
12658 if(s.successProperty) {
12659 this.getSuccess = this.getJsonAccessor(s.successProperty);
12661 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12663 var g = this.getJsonAccessor(s.id);
12664 this.getId = function(rec) {
12666 return (r === undefined || r === "") ? null : r;
12669 this.getId = function(){return null;};
12672 for(var jj = 0; jj < fl; jj++){
12674 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12675 this.ef[jj] = this.getJsonAccessor(map);
12679 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12680 if(s.totalProperty){
12681 var vt = parseInt(this.getTotal(o), 10);
12686 if(s.successProperty){
12687 var vs = this.getSuccess(o);
12688 if(vs === false || vs === 'false'){
12693 for(var i = 0; i < c; i++){
12696 var id = this.getId(n);
12697 for(var j = 0; j < fl; j++){
12699 var v = this.ef[j](n);
12701 Roo.log('missing convert for ' + f.name);
12705 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12707 var record = new Record(values, id);
12709 records[i] = record;
12715 totalRecords : totalRecords
12720 * Ext JS Library 1.1.1
12721 * Copyright(c) 2006-2007, Ext JS, LLC.
12723 * Originally Released Under LGPL - original licence link has changed is not relivant.
12726 * <script type="text/javascript">
12730 * @class Roo.data.ArrayReader
12731 * @extends Roo.data.DataReader
12732 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12733 * Each element of that Array represents a row of data fields. The
12734 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12735 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12739 var RecordDef = Roo.data.Record.create([
12740 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12741 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12743 var myReader = new Roo.data.ArrayReader({
12744 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12748 * This would consume an Array like this:
12750 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12752 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12754 * Create a new JsonReader
12755 * @param {Object} meta Metadata configuration options.
12756 * @param {Object} recordType Either an Array of field definition objects
12757 * as specified to {@link Roo.data.Record#create},
12758 * or an {@link Roo.data.Record} object
12759 * created using {@link Roo.data.Record#create}.
12761 Roo.data.ArrayReader = function(meta, recordType){
12762 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12765 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12767 * Create a data block containing Roo.data.Records from an XML document.
12768 * @param {Object} o An Array of row objects which represents the dataset.
12769 * @return {Object} data A data block which is used by an Roo.data.Store object as
12770 * a cache of Roo.data.Records.
12772 readRecords : function(o){
12773 var sid = this.meta ? this.meta.id : null;
12774 var recordType = this.recordType, fields = recordType.prototype.fields;
12777 for(var i = 0; i < root.length; i++){
12780 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12781 for(var j = 0, jlen = fields.length; j < jlen; j++){
12782 var f = fields.items[j];
12783 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12784 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12786 values[f.name] = v;
12788 var record = new recordType(values, id);
12790 records[records.length] = record;
12794 totalRecords : records.length
12803 * @class Roo.bootstrap.ComboBox
12804 * @extends Roo.bootstrap.TriggerField
12805 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12806 * @cfg {Boolean} append (true|false) default false
12807 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12808 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12809 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12810 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12811 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12812 * @cfg {Boolean} animate default true
12813 * @cfg {Boolean} emptyResultText only for touch device
12814 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12815 * @cfg {String} emptyTitle default ''
12817 * Create a new ComboBox.
12818 * @param {Object} config Configuration options
12820 Roo.bootstrap.ComboBox = function(config){
12821 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12825 * Fires when the dropdown list is expanded
12826 * @param {Roo.bootstrap.ComboBox} combo This combo box
12831 * Fires when the dropdown list is collapsed
12832 * @param {Roo.bootstrap.ComboBox} combo This combo box
12836 * @event beforeselect
12837 * Fires before a list item is selected. Return false to cancel the selection.
12838 * @param {Roo.bootstrap.ComboBox} combo This combo box
12839 * @param {Roo.data.Record} record The data record returned from the underlying store
12840 * @param {Number} index The index of the selected item in the dropdown list
12842 'beforeselect' : true,
12845 * Fires when a list item is selected
12846 * @param {Roo.bootstrap.ComboBox} combo This combo box
12847 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12848 * @param {Number} index The index of the selected item in the dropdown list
12852 * @event beforequery
12853 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12854 * The event object passed has these properties:
12855 * @param {Roo.bootstrap.ComboBox} combo This combo box
12856 * @param {String} query The query
12857 * @param {Boolean} forceAll true to force "all" query
12858 * @param {Boolean} cancel true to cancel the query
12859 * @param {Object} e The query event object
12861 'beforequery': true,
12864 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12865 * @param {Roo.bootstrap.ComboBox} combo This combo box
12870 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12871 * @param {Roo.bootstrap.ComboBox} combo This combo box
12872 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12877 * Fires when the remove value from the combobox array
12878 * @param {Roo.bootstrap.ComboBox} combo This combo box
12882 * @event afterremove
12883 * Fires when the remove value from the combobox array
12884 * @param {Roo.bootstrap.ComboBox} combo This combo box
12886 'afterremove' : true,
12888 * @event specialfilter
12889 * Fires when specialfilter
12890 * @param {Roo.bootstrap.ComboBox} combo This combo box
12892 'specialfilter' : true,
12895 * Fires when tick the element
12896 * @param {Roo.bootstrap.ComboBox} combo This combo box
12900 * @event touchviewdisplay
12901 * Fires when touch view require special display (default is using displayField)
12902 * @param {Roo.bootstrap.ComboBox} combo This combo box
12903 * @param {Object} cfg set html .
12905 'touchviewdisplay' : true
12910 this.tickItems = [];
12912 this.selectedIndex = -1;
12913 if(this.mode == 'local'){
12914 if(config.queryDelay === undefined){
12915 this.queryDelay = 10;
12917 if(config.minChars === undefined){
12923 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12926 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12927 * rendering into an Roo.Editor, defaults to false)
12930 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12931 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12934 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12937 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12938 * the dropdown list (defaults to undefined, with no header element)
12942 * @cfg {String/Roo.Template} tpl The template to use to render the output
12946 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12948 listWidth: undefined,
12950 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12951 * mode = 'remote' or 'text' if mode = 'local')
12953 displayField: undefined,
12956 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12957 * mode = 'remote' or 'value' if mode = 'local').
12958 * Note: use of a valueField requires the user make a selection
12959 * in order for a value to be mapped.
12961 valueField: undefined,
12963 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12968 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12969 * field's data value (defaults to the underlying DOM element's name)
12971 hiddenName: undefined,
12973 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12977 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12979 selectedClass: 'active',
12982 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12986 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12987 * anchor positions (defaults to 'tl-bl')
12989 listAlign: 'tl-bl?',
12991 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12995 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12996 * query specified by the allQuery config option (defaults to 'query')
12998 triggerAction: 'query',
13000 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13001 * (defaults to 4, does not apply if editable = false)
13005 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13006 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13010 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13011 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13015 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13016 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13020 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13021 * when editable = true (defaults to false)
13023 selectOnFocus:false,
13025 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13027 queryParam: 'query',
13029 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13030 * when mode = 'remote' (defaults to 'Loading...')
13032 loadingText: 'Loading...',
13034 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13038 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13042 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13043 * traditional select (defaults to true)
13047 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13051 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13055 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13056 * listWidth has a higher value)
13060 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13061 * allow the user to set arbitrary text into the field (defaults to false)
13063 forceSelection:false,
13065 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13066 * if typeAhead = true (defaults to 250)
13068 typeAheadDelay : 250,
13070 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13071 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13073 valueNotFoundText : undefined,
13075 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13077 blockFocus : false,
13080 * @cfg {Boolean} disableClear Disable showing of clear button.
13082 disableClear : false,
13084 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13086 alwaysQuery : false,
13089 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13094 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13096 invalidClass : "has-warning",
13099 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13101 validClass : "has-success",
13104 * @cfg {Boolean} specialFilter (true|false) special filter default false
13106 specialFilter : false,
13109 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13111 mobileTouchView : true,
13114 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13116 useNativeIOS : false,
13119 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13121 mobile_restrict_height : false,
13123 ios_options : false,
13135 btnPosition : 'right',
13136 triggerList : true,
13137 showToggleBtn : true,
13139 emptyResultText: 'Empty',
13140 triggerText : 'Select',
13143 // element that contains real text value.. (when hidden is used..)
13145 getAutoCreate : function()
13150 * Render classic select for iso
13153 if(Roo.isIOS && this.useNativeIOS){
13154 cfg = this.getAutoCreateNativeIOS();
13162 if(Roo.isTouch && this.mobileTouchView){
13163 cfg = this.getAutoCreateTouchView();
13170 if(!this.tickable){
13171 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13176 * ComboBox with tickable selections
13179 var align = this.labelAlign || this.parentLabelAlign();
13182 cls : 'form-group roo-combobox-tickable' //input-group
13185 var btn_text_select = '';
13186 var btn_text_done = '';
13187 var btn_text_cancel = '';
13189 if (this.btn_text_show) {
13190 btn_text_select = 'Select';
13191 btn_text_done = 'Done';
13192 btn_text_cancel = 'Cancel';
13197 cls : 'tickable-buttons',
13202 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13203 //html : this.triggerText
13204 html: btn_text_select
13210 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13212 html: btn_text_done
13218 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13220 html: btn_text_cancel
13226 buttons.cn.unshift({
13228 cls: 'roo-select2-search-field-input'
13234 Roo.each(buttons.cn, function(c){
13236 c.cls += ' btn-' + _this.size;
13239 if (_this.disabled) {
13250 cls: 'form-hidden-field'
13254 cls: 'roo-select2-choices',
13258 cls: 'roo-select2-search-field',
13269 cls: 'roo-select2-container input-group roo-select2-container-multi',
13274 // cls: 'typeahead typeahead-long dropdown-menu',
13275 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13280 if(this.hasFeedback && !this.allowBlank){
13284 cls: 'glyphicon form-control-feedback'
13287 combobox.cn.push(feedback);
13291 if (align ==='left' && this.fieldLabel.length) {
13293 cfg.cls += ' roo-form-group-label-left';
13298 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13299 tooltip : 'This field is required'
13304 cls : 'control-label',
13305 html : this.fieldLabel
13317 var labelCfg = cfg.cn[1];
13318 var contentCfg = cfg.cn[2];
13321 if(this.indicatorpos == 'right'){
13327 cls : 'control-label',
13331 html : this.fieldLabel
13335 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13336 tooltip : 'This field is required'
13351 labelCfg = cfg.cn[0];
13352 contentCfg = cfg.cn[1];
13356 if(this.labelWidth > 12){
13357 labelCfg.style = "width: " + this.labelWidth + 'px';
13360 if(this.labelWidth < 13 && this.labelmd == 0){
13361 this.labelmd = this.labelWidth;
13364 if(this.labellg > 0){
13365 labelCfg.cls += ' col-lg-' + this.labellg;
13366 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13369 if(this.labelmd > 0){
13370 labelCfg.cls += ' col-md-' + this.labelmd;
13371 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13374 if(this.labelsm > 0){
13375 labelCfg.cls += ' col-sm-' + this.labelsm;
13376 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13379 if(this.labelxs > 0){
13380 labelCfg.cls += ' col-xs-' + this.labelxs;
13381 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13385 } else if ( this.fieldLabel.length) {
13386 // Roo.log(" label");
13390 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13391 tooltip : 'This field is required'
13395 //cls : 'input-group-addon',
13396 html : this.fieldLabel
13401 if(this.indicatorpos == 'right'){
13405 //cls : 'input-group-addon',
13406 html : this.fieldLabel
13410 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13411 tooltip : 'This field is required'
13420 // Roo.log(" no label && no align");
13427 ['xs','sm','md','lg'].map(function(size){
13428 if (settings[size]) {
13429 cfg.cls += ' col-' + size + '-' + settings[size];
13437 _initEventsCalled : false,
13440 initEvents: function()
13442 if (this._initEventsCalled) { // as we call render... prevent looping...
13445 this._initEventsCalled = true;
13448 throw "can not find store for combo";
13451 this.indicator = this.indicatorEl();
13453 this.store = Roo.factory(this.store, Roo.data);
13454 this.store.parent = this;
13456 // if we are building from html. then this element is so complex, that we can not really
13457 // use the rendered HTML.
13458 // so we have to trash and replace the previous code.
13459 if (Roo.XComponent.build_from_html) {
13460 // remove this element....
13461 var e = this.el.dom, k=0;
13462 while (e ) { e = e.previousSibling; ++k;}
13467 this.rendered = false;
13469 this.render(this.parent().getChildContainer(true), k);
13472 if(Roo.isIOS && this.useNativeIOS){
13473 this.initIOSView();
13481 if(Roo.isTouch && this.mobileTouchView){
13482 this.initTouchView();
13487 this.initTickableEvents();
13491 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13493 if(this.hiddenName){
13495 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13497 this.hiddenField.dom.value =
13498 this.hiddenValue !== undefined ? this.hiddenValue :
13499 this.value !== undefined ? this.value : '';
13501 // prevent input submission
13502 this.el.dom.removeAttribute('name');
13503 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13508 // this.el.dom.setAttribute('autocomplete', 'off');
13511 var cls = 'x-combo-list';
13513 //this.list = new Roo.Layer({
13514 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13520 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13521 _this.list.setWidth(lw);
13524 this.list.on('mouseover', this.onViewOver, this);
13525 this.list.on('mousemove', this.onViewMove, this);
13526 this.list.on('scroll', this.onViewScroll, this);
13529 this.list.swallowEvent('mousewheel');
13530 this.assetHeight = 0;
13533 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13534 this.assetHeight += this.header.getHeight();
13537 this.innerList = this.list.createChild({cls:cls+'-inner'});
13538 this.innerList.on('mouseover', this.onViewOver, this);
13539 this.innerList.on('mousemove', this.onViewMove, this);
13540 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13542 if(this.allowBlank && !this.pageSize && !this.disableClear){
13543 this.footer = this.list.createChild({cls:cls+'-ft'});
13544 this.pageTb = new Roo.Toolbar(this.footer);
13548 this.footer = this.list.createChild({cls:cls+'-ft'});
13549 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13550 {pageSize: this.pageSize});
13554 if (this.pageTb && this.allowBlank && !this.disableClear) {
13556 this.pageTb.add(new Roo.Toolbar.Fill(), {
13557 cls: 'x-btn-icon x-btn-clear',
13559 handler: function()
13562 _this.clearValue();
13563 _this.onSelect(false, -1);
13568 this.assetHeight += this.footer.getHeight();
13573 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13576 this.view = new Roo.View(this.list, this.tpl, {
13577 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13579 //this.view.wrapEl.setDisplayed(false);
13580 this.view.on('click', this.onViewClick, this);
13583 this.store.on('beforeload', this.onBeforeLoad, this);
13584 this.store.on('load', this.onLoad, this);
13585 this.store.on('loadexception', this.onLoadException, this);
13587 if(this.resizable){
13588 this.resizer = new Roo.Resizable(this.list, {
13589 pinned:true, handles:'se'
13591 this.resizer.on('resize', function(r, w, h){
13592 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13593 this.listWidth = w;
13594 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13595 this.restrictHeight();
13597 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13600 if(!this.editable){
13601 this.editable = true;
13602 this.setEditable(false);
13607 if (typeof(this.events.add.listeners) != 'undefined') {
13609 this.addicon = this.wrap.createChild(
13610 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13612 this.addicon.on('click', function(e) {
13613 this.fireEvent('add', this);
13616 if (typeof(this.events.edit.listeners) != 'undefined') {
13618 this.editicon = this.wrap.createChild(
13619 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13620 if (this.addicon) {
13621 this.editicon.setStyle('margin-left', '40px');
13623 this.editicon.on('click', function(e) {
13625 // we fire even if inothing is selected..
13626 this.fireEvent('edit', this, this.lastData );
13632 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13633 "up" : function(e){
13634 this.inKeyMode = true;
13638 "down" : function(e){
13639 if(!this.isExpanded()){
13640 this.onTriggerClick();
13642 this.inKeyMode = true;
13647 "enter" : function(e){
13648 // this.onViewClick();
13652 if(this.fireEvent("specialkey", this, e)){
13653 this.onViewClick(false);
13659 "esc" : function(e){
13663 "tab" : function(e){
13666 if(this.fireEvent("specialkey", this, e)){
13667 this.onViewClick(false);
13675 doRelay : function(foo, bar, hname){
13676 if(hname == 'down' || this.scope.isExpanded()){
13677 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13686 this.queryDelay = Math.max(this.queryDelay || 10,
13687 this.mode == 'local' ? 10 : 250);
13690 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13692 if(this.typeAhead){
13693 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13695 if(this.editable !== false){
13696 this.inputEl().on("keyup", this.onKeyUp, this);
13698 if(this.forceSelection){
13699 this.inputEl().on('blur', this.doForce, this);
13703 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13704 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13708 initTickableEvents: function()
13712 if(this.hiddenName){
13714 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13716 this.hiddenField.dom.value =
13717 this.hiddenValue !== undefined ? this.hiddenValue :
13718 this.value !== undefined ? this.value : '';
13720 // prevent input submission
13721 this.el.dom.removeAttribute('name');
13722 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13727 // this.list = this.el.select('ul.dropdown-menu',true).first();
13729 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13730 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13731 if(this.triggerList){
13732 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13735 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13736 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13738 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13739 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13741 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13742 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13744 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13745 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13746 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13749 this.cancelBtn.hide();
13754 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13755 _this.list.setWidth(lw);
13758 this.list.on('mouseover', this.onViewOver, this);
13759 this.list.on('mousemove', this.onViewMove, this);
13761 this.list.on('scroll', this.onViewScroll, this);
13764 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13765 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13768 this.view = new Roo.View(this.list, this.tpl, {
13773 selectedClass: this.selectedClass
13776 //this.view.wrapEl.setDisplayed(false);
13777 this.view.on('click', this.onViewClick, this);
13781 this.store.on('beforeload', this.onBeforeLoad, this);
13782 this.store.on('load', this.onLoad, this);
13783 this.store.on('loadexception', this.onLoadException, this);
13786 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13787 "up" : function(e){
13788 this.inKeyMode = true;
13792 "down" : function(e){
13793 this.inKeyMode = true;
13797 "enter" : function(e){
13798 if(this.fireEvent("specialkey", this, e)){
13799 this.onViewClick(false);
13805 "esc" : function(e){
13806 this.onTickableFooterButtonClick(e, false, false);
13809 "tab" : function(e){
13810 this.fireEvent("specialkey", this, e);
13812 this.onTickableFooterButtonClick(e, false, false);
13819 doRelay : function(e, fn, key){
13820 if(this.scope.isExpanded()){
13821 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13830 this.queryDelay = Math.max(this.queryDelay || 10,
13831 this.mode == 'local' ? 10 : 250);
13834 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13836 if(this.typeAhead){
13837 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13840 if(this.editable !== false){
13841 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13844 this.indicator = this.indicatorEl();
13846 if(this.indicator){
13847 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13848 this.indicator.hide();
13853 onDestroy : function(){
13855 this.view.setStore(null);
13856 this.view.el.removeAllListeners();
13857 this.view.el.remove();
13858 this.view.purgeListeners();
13861 this.list.dom.innerHTML = '';
13865 this.store.un('beforeload', this.onBeforeLoad, this);
13866 this.store.un('load', this.onLoad, this);
13867 this.store.un('loadexception', this.onLoadException, this);
13869 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13873 fireKey : function(e){
13874 if(e.isNavKeyPress() && !this.list.isVisible()){
13875 this.fireEvent("specialkey", this, e);
13880 onResize: function(w, h){
13881 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13883 // if(typeof w != 'number'){
13884 // // we do not handle it!?!?
13887 // var tw = this.trigger.getWidth();
13888 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13889 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13891 // this.inputEl().setWidth( this.adjustWidth('input', x));
13893 // //this.trigger.setStyle('left', x+'px');
13895 // if(this.list && this.listWidth === undefined){
13896 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13897 // this.list.setWidth(lw);
13898 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13906 * Allow or prevent the user from directly editing the field text. If false is passed,
13907 * the user will only be able to select from the items defined in the dropdown list. This method
13908 * is the runtime equivalent of setting the 'editable' config option at config time.
13909 * @param {Boolean} value True to allow the user to directly edit the field text
13911 setEditable : function(value){
13912 if(value == this.editable){
13915 this.editable = value;
13917 this.inputEl().dom.setAttribute('readOnly', true);
13918 this.inputEl().on('mousedown', this.onTriggerClick, this);
13919 this.inputEl().addClass('x-combo-noedit');
13921 this.inputEl().dom.setAttribute('readOnly', false);
13922 this.inputEl().un('mousedown', this.onTriggerClick, this);
13923 this.inputEl().removeClass('x-combo-noedit');
13929 onBeforeLoad : function(combo,opts){
13930 if(!this.hasFocus){
13934 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13936 this.restrictHeight();
13937 this.selectedIndex = -1;
13941 onLoad : function(){
13943 this.hasQuery = false;
13945 if(!this.hasFocus){
13949 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13950 this.loading.hide();
13953 if(this.store.getCount() > 0){
13956 this.restrictHeight();
13957 if(this.lastQuery == this.allQuery){
13958 if(this.editable && !this.tickable){
13959 this.inputEl().dom.select();
13963 !this.selectByValue(this.value, true) &&
13966 !this.store.lastOptions ||
13967 typeof(this.store.lastOptions.add) == 'undefined' ||
13968 this.store.lastOptions.add != true
13971 this.select(0, true);
13974 if(this.autoFocus){
13977 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13978 this.taTask.delay(this.typeAheadDelay);
13982 this.onEmptyResults();
13988 onLoadException : function()
13990 this.hasQuery = false;
13992 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13993 this.loading.hide();
13996 if(this.tickable && this.editable){
14001 // only causes errors at present
14002 //Roo.log(this.store.reader.jsonData);
14003 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14005 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14011 onTypeAhead : function(){
14012 if(this.store.getCount() > 0){
14013 var r = this.store.getAt(0);
14014 var newValue = r.data[this.displayField];
14015 var len = newValue.length;
14016 var selStart = this.getRawValue().length;
14018 if(selStart != len){
14019 this.setRawValue(newValue);
14020 this.selectText(selStart, newValue.length);
14026 onSelect : function(record, index){
14028 if(this.fireEvent('beforeselect', this, record, index) !== false){
14030 this.setFromData(index > -1 ? record.data : false);
14033 this.fireEvent('select', this, record, index);
14038 * Returns the currently selected field value or empty string if no value is set.
14039 * @return {String} value The selected value
14041 getValue : function()
14043 if(Roo.isIOS && this.useNativeIOS){
14044 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14048 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14051 if(this.valueField){
14052 return typeof this.value != 'undefined' ? this.value : '';
14054 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14058 getRawValue : function()
14060 if(Roo.isIOS && this.useNativeIOS){
14061 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14064 var v = this.inputEl().getValue();
14070 * Clears any text/value currently set in the field
14072 clearValue : function(){
14074 if(this.hiddenField){
14075 this.hiddenField.dom.value = '';
14078 this.setRawValue('');
14079 this.lastSelectionText = '';
14080 this.lastData = false;
14082 var close = this.closeTriggerEl();
14093 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14094 * will be displayed in the field. If the value does not match the data value of an existing item,
14095 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14096 * Otherwise the field will be blank (although the value will still be set).
14097 * @param {String} value The value to match
14099 setValue : function(v)
14101 if(Roo.isIOS && this.useNativeIOS){
14102 this.setIOSValue(v);
14112 if(this.valueField){
14113 var r = this.findRecord(this.valueField, v);
14115 text = r.data[this.displayField];
14116 }else if(this.valueNotFoundText !== undefined){
14117 text = this.valueNotFoundText;
14120 this.lastSelectionText = text;
14121 if(this.hiddenField){
14122 this.hiddenField.dom.value = v;
14124 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14127 var close = this.closeTriggerEl();
14130 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14136 * @property {Object} the last set data for the element
14141 * Sets the value of the field based on a object which is related to the record format for the store.
14142 * @param {Object} value the value to set as. or false on reset?
14144 setFromData : function(o){
14151 var dv = ''; // display value
14152 var vv = ''; // value value..
14154 if (this.displayField) {
14155 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14157 // this is an error condition!!!
14158 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14161 if(this.valueField){
14162 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14165 var close = this.closeTriggerEl();
14168 if(dv.length || vv * 1 > 0){
14170 this.blockFocus=true;
14176 if(this.hiddenField){
14177 this.hiddenField.dom.value = vv;
14179 this.lastSelectionText = dv;
14180 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14184 // no hidden field.. - we store the value in 'value', but still display
14185 // display field!!!!
14186 this.lastSelectionText = dv;
14187 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14194 reset : function(){
14195 // overridden so that last data is reset..
14202 this.setValue(this.originalValue);
14203 //this.clearInvalid();
14204 this.lastData = false;
14206 this.view.clearSelections();
14212 findRecord : function(prop, value){
14214 if(this.store.getCount() > 0){
14215 this.store.each(function(r){
14216 if(r.data[prop] == value){
14226 getName: function()
14228 // returns hidden if it's set..
14229 if (!this.rendered) {return ''};
14230 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14234 onViewMove : function(e, t){
14235 this.inKeyMode = false;
14239 onViewOver : function(e, t){
14240 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14243 var item = this.view.findItemFromChild(t);
14246 var index = this.view.indexOf(item);
14247 this.select(index, false);
14252 onViewClick : function(view, doFocus, el, e)
14254 var index = this.view.getSelectedIndexes()[0];
14256 var r = this.store.getAt(index);
14260 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14267 Roo.each(this.tickItems, function(v,k){
14269 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14271 _this.tickItems.splice(k, 1);
14273 if(typeof(e) == 'undefined' && view == false){
14274 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14286 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14287 this.tickItems.push(r.data);
14290 if(typeof(e) == 'undefined' && view == false){
14291 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14298 this.onSelect(r, index);
14300 if(doFocus !== false && !this.blockFocus){
14301 this.inputEl().focus();
14306 restrictHeight : function(){
14307 //this.innerList.dom.style.height = '';
14308 //var inner = this.innerList.dom;
14309 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14310 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14311 //this.list.beginUpdate();
14312 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14313 this.list.alignTo(this.inputEl(), this.listAlign);
14314 this.list.alignTo(this.inputEl(), this.listAlign);
14315 //this.list.endUpdate();
14319 onEmptyResults : function(){
14321 if(this.tickable && this.editable){
14322 this.hasFocus = false;
14323 this.restrictHeight();
14331 * Returns true if the dropdown list is expanded, else false.
14333 isExpanded : function(){
14334 return this.list.isVisible();
14338 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14339 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14340 * @param {String} value The data value of the item to select
14341 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14342 * selected item if it is not currently in view (defaults to true)
14343 * @return {Boolean} True if the value matched an item in the list, else false
14345 selectByValue : function(v, scrollIntoView){
14346 if(v !== undefined && v !== null){
14347 var r = this.findRecord(this.valueField || this.displayField, v);
14349 this.select(this.store.indexOf(r), scrollIntoView);
14357 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14358 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14359 * @param {Number} index The zero-based index of the list item to select
14360 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14361 * selected item if it is not currently in view (defaults to true)
14363 select : function(index, scrollIntoView){
14364 this.selectedIndex = index;
14365 this.view.select(index);
14366 if(scrollIntoView !== false){
14367 var el = this.view.getNode(index);
14369 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14372 this.list.scrollChildIntoView(el, false);
14378 selectNext : function(){
14379 var ct = this.store.getCount();
14381 if(this.selectedIndex == -1){
14383 }else if(this.selectedIndex < ct-1){
14384 this.select(this.selectedIndex+1);
14390 selectPrev : function(){
14391 var ct = this.store.getCount();
14393 if(this.selectedIndex == -1){
14395 }else if(this.selectedIndex != 0){
14396 this.select(this.selectedIndex-1);
14402 onKeyUp : function(e){
14403 if(this.editable !== false && !e.isSpecialKey()){
14404 this.lastKey = e.getKey();
14405 this.dqTask.delay(this.queryDelay);
14410 validateBlur : function(){
14411 return !this.list || !this.list.isVisible();
14415 initQuery : function(){
14417 var v = this.getRawValue();
14419 if(this.tickable && this.editable){
14420 v = this.tickableInputEl().getValue();
14427 doForce : function(){
14428 if(this.inputEl().dom.value.length > 0){
14429 this.inputEl().dom.value =
14430 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14436 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14437 * query allowing the query action to be canceled if needed.
14438 * @param {String} query The SQL query to execute
14439 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14440 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14441 * saved in the current store (defaults to false)
14443 doQuery : function(q, forceAll){
14445 if(q === undefined || q === null){
14450 forceAll: forceAll,
14454 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14459 forceAll = qe.forceAll;
14460 if(forceAll === true || (q.length >= this.minChars)){
14462 this.hasQuery = true;
14464 if(this.lastQuery != q || this.alwaysQuery){
14465 this.lastQuery = q;
14466 if(this.mode == 'local'){
14467 this.selectedIndex = -1;
14469 this.store.clearFilter();
14472 if(this.specialFilter){
14473 this.fireEvent('specialfilter', this);
14478 this.store.filter(this.displayField, q);
14481 this.store.fireEvent("datachanged", this.store);
14488 this.store.baseParams[this.queryParam] = q;
14490 var options = {params : this.getParams(q)};
14493 options.add = true;
14494 options.params.start = this.page * this.pageSize;
14497 this.store.load(options);
14500 * this code will make the page width larger, at the beginning, the list not align correctly,
14501 * we should expand the list on onLoad
14502 * so command out it
14507 this.selectedIndex = -1;
14512 this.loadNext = false;
14516 getParams : function(q){
14518 //p[this.queryParam] = q;
14522 p.limit = this.pageSize;
14528 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14530 collapse : function(){
14531 if(!this.isExpanded()){
14537 this.hasFocus = false;
14541 this.cancelBtn.hide();
14542 this.trigger.show();
14545 this.tickableInputEl().dom.value = '';
14546 this.tickableInputEl().blur();
14551 Roo.get(document).un('mousedown', this.collapseIf, this);
14552 Roo.get(document).un('mousewheel', this.collapseIf, this);
14553 if (!this.editable) {
14554 Roo.get(document).un('keydown', this.listKeyPress, this);
14556 this.fireEvent('collapse', this);
14562 collapseIf : function(e){
14563 var in_combo = e.within(this.el);
14564 var in_list = e.within(this.list);
14565 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14567 if (in_combo || in_list || is_list) {
14568 //e.stopPropagation();
14573 this.onTickableFooterButtonClick(e, false, false);
14581 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14583 expand : function(){
14585 if(this.isExpanded() || !this.hasFocus){
14589 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14590 this.list.setWidth(lw);
14596 this.restrictHeight();
14600 this.tickItems = Roo.apply([], this.item);
14603 this.cancelBtn.show();
14604 this.trigger.hide();
14607 this.tickableInputEl().focus();
14612 Roo.get(document).on('mousedown', this.collapseIf, this);
14613 Roo.get(document).on('mousewheel', this.collapseIf, this);
14614 if (!this.editable) {
14615 Roo.get(document).on('keydown', this.listKeyPress, this);
14618 this.fireEvent('expand', this);
14622 // Implements the default empty TriggerField.onTriggerClick function
14623 onTriggerClick : function(e)
14625 Roo.log('trigger click');
14627 if(this.disabled || !this.triggerList){
14632 this.loadNext = false;
14634 if(this.isExpanded()){
14636 if (!this.blockFocus) {
14637 this.inputEl().focus();
14641 this.hasFocus = true;
14642 if(this.triggerAction == 'all') {
14643 this.doQuery(this.allQuery, true);
14645 this.doQuery(this.getRawValue());
14647 if (!this.blockFocus) {
14648 this.inputEl().focus();
14653 onTickableTriggerClick : function(e)
14660 this.loadNext = false;
14661 this.hasFocus = true;
14663 if(this.triggerAction == 'all') {
14664 this.doQuery(this.allQuery, true);
14666 this.doQuery(this.getRawValue());
14670 onSearchFieldClick : function(e)
14672 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14673 this.onTickableFooterButtonClick(e, false, false);
14677 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14682 this.loadNext = false;
14683 this.hasFocus = true;
14685 if(this.triggerAction == 'all') {
14686 this.doQuery(this.allQuery, true);
14688 this.doQuery(this.getRawValue());
14692 listKeyPress : function(e)
14694 //Roo.log('listkeypress');
14695 // scroll to first matching element based on key pres..
14696 if (e.isSpecialKey()) {
14699 var k = String.fromCharCode(e.getKey()).toUpperCase();
14702 var csel = this.view.getSelectedNodes();
14703 var cselitem = false;
14705 var ix = this.view.indexOf(csel[0]);
14706 cselitem = this.store.getAt(ix);
14707 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14713 this.store.each(function(v) {
14715 // start at existing selection.
14716 if (cselitem.id == v.id) {
14722 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14723 match = this.store.indexOf(v);
14729 if (match === false) {
14730 return true; // no more action?
14733 this.view.select(match);
14734 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14735 sn.scrollIntoView(sn.dom.parentNode, false);
14738 onViewScroll : function(e, t){
14740 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){
14744 this.hasQuery = true;
14746 this.loading = this.list.select('.loading', true).first();
14748 if(this.loading === null){
14749 this.list.createChild({
14751 cls: 'loading roo-select2-more-results roo-select2-active',
14752 html: 'Loading more results...'
14755 this.loading = this.list.select('.loading', true).first();
14757 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14759 this.loading.hide();
14762 this.loading.show();
14767 this.loadNext = true;
14769 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14774 addItem : function(o)
14776 var dv = ''; // display value
14778 if (this.displayField) {
14779 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14781 // this is an error condition!!!
14782 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14789 var choice = this.choices.createChild({
14791 cls: 'roo-select2-search-choice',
14800 cls: 'roo-select2-search-choice-close fa fa-times',
14805 }, this.searchField);
14807 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14809 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14817 this.inputEl().dom.value = '';
14822 onRemoveItem : function(e, _self, o)
14824 e.preventDefault();
14826 this.lastItem = Roo.apply([], this.item);
14828 var index = this.item.indexOf(o.data) * 1;
14831 Roo.log('not this item?!');
14835 this.item.splice(index, 1);
14840 this.fireEvent('remove', this, e);
14846 syncValue : function()
14848 if(!this.item.length){
14855 Roo.each(this.item, function(i){
14856 if(_this.valueField){
14857 value.push(i[_this.valueField]);
14864 this.value = value.join(',');
14866 if(this.hiddenField){
14867 this.hiddenField.dom.value = this.value;
14870 this.store.fireEvent("datachanged", this.store);
14875 clearItem : function()
14877 if(!this.multiple){
14883 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14891 if(this.tickable && !Roo.isTouch){
14892 this.view.refresh();
14896 inputEl: function ()
14898 if(Roo.isIOS && this.useNativeIOS){
14899 return this.el.select('select.roo-ios-select', true).first();
14902 if(Roo.isTouch && this.mobileTouchView){
14903 return this.el.select('input.form-control',true).first();
14907 return this.searchField;
14910 return this.el.select('input.form-control',true).first();
14913 onTickableFooterButtonClick : function(e, btn, el)
14915 e.preventDefault();
14917 this.lastItem = Roo.apply([], this.item);
14919 if(btn && btn.name == 'cancel'){
14920 this.tickItems = Roo.apply([], this.item);
14929 Roo.each(this.tickItems, function(o){
14937 validate : function()
14939 if(this.getVisibilityEl().hasClass('hidden')){
14943 var v = this.getRawValue();
14946 v = this.getValue();
14949 if(this.disabled || this.allowBlank || v.length){
14954 this.markInvalid();
14958 tickableInputEl : function()
14960 if(!this.tickable || !this.editable){
14961 return this.inputEl();
14964 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14968 getAutoCreateTouchView : function()
14973 cls: 'form-group' //input-group
14979 type : this.inputType,
14980 cls : 'form-control x-combo-noedit',
14981 autocomplete: 'new-password',
14982 placeholder : this.placeholder || '',
14987 input.name = this.name;
14991 input.cls += ' input-' + this.size;
14994 if (this.disabled) {
14995 input.disabled = true;
15006 inputblock.cls += ' input-group';
15008 inputblock.cn.unshift({
15010 cls : 'input-group-addon',
15015 if(this.removable && !this.multiple){
15016 inputblock.cls += ' roo-removable';
15018 inputblock.cn.push({
15021 cls : 'roo-combo-removable-btn close'
15025 if(this.hasFeedback && !this.allowBlank){
15027 inputblock.cls += ' has-feedback';
15029 inputblock.cn.push({
15031 cls: 'glyphicon form-control-feedback'
15038 inputblock.cls += (this.before) ? '' : ' input-group';
15040 inputblock.cn.push({
15042 cls : 'input-group-addon',
15053 cls: 'form-hidden-field'
15067 cls: 'form-hidden-field'
15071 cls: 'roo-select2-choices',
15075 cls: 'roo-select2-search-field',
15088 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15094 if(!this.multiple && this.showToggleBtn){
15101 if (this.caret != false) {
15104 cls: 'fa fa-' + this.caret
15111 cls : 'input-group-addon btn dropdown-toggle',
15116 cls: 'combobox-clear',
15130 combobox.cls += ' roo-select2-container-multi';
15133 var align = this.labelAlign || this.parentLabelAlign();
15135 if (align ==='left' && this.fieldLabel.length) {
15140 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15141 tooltip : 'This field is required'
15145 cls : 'control-label',
15146 html : this.fieldLabel
15157 var labelCfg = cfg.cn[1];
15158 var contentCfg = cfg.cn[2];
15161 if(this.indicatorpos == 'right'){
15166 cls : 'control-label',
15170 html : this.fieldLabel
15174 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15175 tooltip : 'This field is required'
15188 labelCfg = cfg.cn[0];
15189 contentCfg = cfg.cn[1];
15194 if(this.labelWidth > 12){
15195 labelCfg.style = "width: " + this.labelWidth + 'px';
15198 if(this.labelWidth < 13 && this.labelmd == 0){
15199 this.labelmd = this.labelWidth;
15202 if(this.labellg > 0){
15203 labelCfg.cls += ' col-lg-' + this.labellg;
15204 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15207 if(this.labelmd > 0){
15208 labelCfg.cls += ' col-md-' + this.labelmd;
15209 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15212 if(this.labelsm > 0){
15213 labelCfg.cls += ' col-sm-' + this.labelsm;
15214 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15217 if(this.labelxs > 0){
15218 labelCfg.cls += ' col-xs-' + this.labelxs;
15219 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15223 } else if ( this.fieldLabel.length) {
15227 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15228 tooltip : 'This field is required'
15232 cls : 'control-label',
15233 html : this.fieldLabel
15244 if(this.indicatorpos == 'right'){
15248 cls : 'control-label',
15249 html : this.fieldLabel,
15253 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15254 tooltip : 'This field is required'
15271 var settings = this;
15273 ['xs','sm','md','lg'].map(function(size){
15274 if (settings[size]) {
15275 cfg.cls += ' col-' + size + '-' + settings[size];
15282 initTouchView : function()
15284 this.renderTouchView();
15286 this.touchViewEl.on('scroll', function(){
15287 this.el.dom.scrollTop = 0;
15290 this.originalValue = this.getValue();
15292 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15294 this.inputEl().on("click", this.showTouchView, this);
15295 if (this.triggerEl) {
15296 this.triggerEl.on("click", this.showTouchView, this);
15300 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15301 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15303 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15305 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15306 this.store.on('load', this.onTouchViewLoad, this);
15307 this.store.on('loadexception', this.onTouchViewLoadException, this);
15309 if(this.hiddenName){
15311 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15313 this.hiddenField.dom.value =
15314 this.hiddenValue !== undefined ? this.hiddenValue :
15315 this.value !== undefined ? this.value : '';
15317 this.el.dom.removeAttribute('name');
15318 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15322 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15323 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15326 if(this.removable && !this.multiple){
15327 var close = this.closeTriggerEl();
15329 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15330 close.on('click', this.removeBtnClick, this, close);
15334 * fix the bug in Safari iOS8
15336 this.inputEl().on("focus", function(e){
15337 document.activeElement.blur();
15340 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15347 renderTouchView : function()
15349 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15350 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15352 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15353 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15355 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15356 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15357 this.touchViewBodyEl.setStyle('overflow', 'auto');
15359 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15360 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15362 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15363 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15367 showTouchView : function()
15373 this.touchViewHeaderEl.hide();
15375 if(this.modalTitle.length){
15376 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15377 this.touchViewHeaderEl.show();
15380 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15381 this.touchViewEl.show();
15383 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15385 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15386 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15388 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15390 if(this.modalTitle.length){
15391 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15394 this.touchViewBodyEl.setHeight(bodyHeight);
15398 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15400 this.touchViewEl.addClass('in');
15403 if(this._touchViewMask){
15404 Roo.get(document.body).addClass("x-body-masked");
15405 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15406 this._touchViewMask.setStyle('z-index', 10000);
15407 this._touchViewMask.addClass('show');
15410 this.doTouchViewQuery();
15414 hideTouchView : function()
15416 this.touchViewEl.removeClass('in');
15420 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15422 this.touchViewEl.setStyle('display', 'none');
15425 if(this._touchViewMask){
15426 this._touchViewMask.removeClass('show');
15427 Roo.get(document.body).removeClass("x-body-masked");
15431 setTouchViewValue : function()
15438 Roo.each(this.tickItems, function(o){
15443 this.hideTouchView();
15446 doTouchViewQuery : function()
15455 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15459 if(!this.alwaysQuery || this.mode == 'local'){
15460 this.onTouchViewLoad();
15467 onTouchViewBeforeLoad : function(combo,opts)
15473 onTouchViewLoad : function()
15475 if(this.store.getCount() < 1){
15476 this.onTouchViewEmptyResults();
15480 this.clearTouchView();
15482 var rawValue = this.getRawValue();
15484 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15486 this.tickItems = [];
15488 this.store.data.each(function(d, rowIndex){
15489 var row = this.touchViewListGroup.createChild(template);
15491 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15492 row.addClass(d.data.cls);
15495 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15498 html : d.data[this.displayField]
15501 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15502 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15505 row.removeClass('selected');
15506 if(!this.multiple && this.valueField &&
15507 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15510 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15511 row.addClass('selected');
15514 if(this.multiple && this.valueField &&
15515 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15519 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15520 this.tickItems.push(d.data);
15523 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15527 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15529 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15531 if(this.modalTitle.length){
15532 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15535 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15537 if(this.mobile_restrict_height && listHeight < bodyHeight){
15538 this.touchViewBodyEl.setHeight(listHeight);
15543 if(firstChecked && listHeight > bodyHeight){
15544 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15549 onTouchViewLoadException : function()
15551 this.hideTouchView();
15554 onTouchViewEmptyResults : function()
15556 this.clearTouchView();
15558 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15560 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15564 clearTouchView : function()
15566 this.touchViewListGroup.dom.innerHTML = '';
15569 onTouchViewClick : function(e, el, o)
15571 e.preventDefault();
15574 var rowIndex = o.rowIndex;
15576 var r = this.store.getAt(rowIndex);
15578 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15580 if(!this.multiple){
15581 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15582 c.dom.removeAttribute('checked');
15585 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15587 this.setFromData(r.data);
15589 var close = this.closeTriggerEl();
15595 this.hideTouchView();
15597 this.fireEvent('select', this, r, rowIndex);
15602 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15603 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15604 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15608 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15609 this.addItem(r.data);
15610 this.tickItems.push(r.data);
15614 getAutoCreateNativeIOS : function()
15617 cls: 'form-group' //input-group,
15622 cls : 'roo-ios-select'
15626 combobox.name = this.name;
15629 if (this.disabled) {
15630 combobox.disabled = true;
15633 var settings = this;
15635 ['xs','sm','md','lg'].map(function(size){
15636 if (settings[size]) {
15637 cfg.cls += ' col-' + size + '-' + settings[size];
15647 initIOSView : function()
15649 this.store.on('load', this.onIOSViewLoad, this);
15654 onIOSViewLoad : function()
15656 if(this.store.getCount() < 1){
15660 this.clearIOSView();
15662 if(this.allowBlank) {
15664 var default_text = '-- SELECT --';
15666 if(this.placeholder.length){
15667 default_text = this.placeholder;
15670 if(this.emptyTitle.length){
15671 default_text += ' - ' + this.emptyTitle + ' -';
15674 var opt = this.inputEl().createChild({
15677 html : default_text
15681 o[this.valueField] = 0;
15682 o[this.displayField] = default_text;
15684 this.ios_options.push({
15691 this.store.data.each(function(d, rowIndex){
15695 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15696 html = d.data[this.displayField];
15701 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15702 value = d.data[this.valueField];
15711 if(this.value == d.data[this.valueField]){
15712 option['selected'] = true;
15715 var opt = this.inputEl().createChild(option);
15717 this.ios_options.push({
15724 this.inputEl().on('change', function(){
15725 this.fireEvent('select', this);
15730 clearIOSView: function()
15732 this.inputEl().dom.innerHTML = '';
15734 this.ios_options = [];
15737 setIOSValue: function(v)
15741 if(!this.ios_options){
15745 Roo.each(this.ios_options, function(opts){
15747 opts.el.dom.removeAttribute('selected');
15749 if(opts.data[this.valueField] != v){
15753 opts.el.dom.setAttribute('selected', true);
15759 * @cfg {Boolean} grow
15763 * @cfg {Number} growMin
15767 * @cfg {Number} growMax
15776 Roo.apply(Roo.bootstrap.ComboBox, {
15780 cls: 'modal-header',
15802 cls: 'list-group-item',
15806 cls: 'roo-combobox-list-group-item-value'
15810 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15824 listItemCheckbox : {
15826 cls: 'list-group-item',
15830 cls: 'roo-combobox-list-group-item-value'
15834 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15850 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15855 cls: 'modal-footer',
15863 cls: 'col-xs-6 text-left',
15866 cls: 'btn btn-danger roo-touch-view-cancel',
15872 cls: 'col-xs-6 text-right',
15875 cls: 'btn btn-success roo-touch-view-ok',
15886 Roo.apply(Roo.bootstrap.ComboBox, {
15888 touchViewTemplate : {
15890 cls: 'modal fade roo-combobox-touch-view',
15894 cls: 'modal-dialog',
15895 style : 'position:fixed', // we have to fix position....
15899 cls: 'modal-content',
15901 Roo.bootstrap.ComboBox.header,
15902 Roo.bootstrap.ComboBox.body,
15903 Roo.bootstrap.ComboBox.footer
15912 * Ext JS Library 1.1.1
15913 * Copyright(c) 2006-2007, Ext JS, LLC.
15915 * Originally Released Under LGPL - original licence link has changed is not relivant.
15918 * <script type="text/javascript">
15923 * @extends Roo.util.Observable
15924 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15925 * This class also supports single and multi selection modes. <br>
15926 * Create a data model bound view:
15928 var store = new Roo.data.Store(...);
15930 var view = new Roo.View({
15932 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15934 singleSelect: true,
15935 selectedClass: "ydataview-selected",
15939 // listen for node click?
15940 view.on("click", function(vw, index, node, e){
15941 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15945 dataModel.load("foobar.xml");
15947 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15949 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15950 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15952 * Note: old style constructor is still suported (container, template, config)
15955 * Create a new View
15956 * @param {Object} config The config object
15959 Roo.View = function(config, depreciated_tpl, depreciated_config){
15961 this.parent = false;
15963 if (typeof(depreciated_tpl) == 'undefined') {
15964 // new way.. - universal constructor.
15965 Roo.apply(this, config);
15966 this.el = Roo.get(this.el);
15969 this.el = Roo.get(config);
15970 this.tpl = depreciated_tpl;
15971 Roo.apply(this, depreciated_config);
15973 this.wrapEl = this.el.wrap().wrap();
15974 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15977 if(typeof(this.tpl) == "string"){
15978 this.tpl = new Roo.Template(this.tpl);
15980 // support xtype ctors..
15981 this.tpl = new Roo.factory(this.tpl, Roo);
15985 this.tpl.compile();
15990 * @event beforeclick
15991 * Fires before a click is processed. Returns false to cancel the default action.
15992 * @param {Roo.View} this
15993 * @param {Number} index The index of the target node
15994 * @param {HTMLElement} node The target node
15995 * @param {Roo.EventObject} e The raw event object
15997 "beforeclick" : true,
16000 * Fires when a template node is clicked.
16001 * @param {Roo.View} this
16002 * @param {Number} index The index of the target node
16003 * @param {HTMLElement} node The target node
16004 * @param {Roo.EventObject} e The raw event object
16009 * Fires when a template node is double clicked.
16010 * @param {Roo.View} this
16011 * @param {Number} index The index of the target node
16012 * @param {HTMLElement} node The target node
16013 * @param {Roo.EventObject} e The raw event object
16017 * @event contextmenu
16018 * Fires when a template node is right clicked.
16019 * @param {Roo.View} this
16020 * @param {Number} index The index of the target node
16021 * @param {HTMLElement} node The target node
16022 * @param {Roo.EventObject} e The raw event object
16024 "contextmenu" : true,
16026 * @event selectionchange
16027 * Fires when the selected nodes change.
16028 * @param {Roo.View} this
16029 * @param {Array} selections Array of the selected nodes
16031 "selectionchange" : true,
16034 * @event beforeselect
16035 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16036 * @param {Roo.View} this
16037 * @param {HTMLElement} node The node to be selected
16038 * @param {Array} selections Array of currently selected nodes
16040 "beforeselect" : true,
16042 * @event preparedata
16043 * Fires on every row to render, to allow you to change the data.
16044 * @param {Roo.View} this
16045 * @param {Object} data to be rendered (change this)
16047 "preparedata" : true
16055 "click": this.onClick,
16056 "dblclick": this.onDblClick,
16057 "contextmenu": this.onContextMenu,
16061 this.selections = [];
16063 this.cmp = new Roo.CompositeElementLite([]);
16065 this.store = Roo.factory(this.store, Roo.data);
16066 this.setStore(this.store, true);
16069 if ( this.footer && this.footer.xtype) {
16071 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16073 this.footer.dataSource = this.store;
16074 this.footer.container = fctr;
16075 this.footer = Roo.factory(this.footer, Roo);
16076 fctr.insertFirst(this.el);
16078 // this is a bit insane - as the paging toolbar seems to detach the el..
16079 // dom.parentNode.parentNode.parentNode
16080 // they get detached?
16084 Roo.View.superclass.constructor.call(this);
16089 Roo.extend(Roo.View, Roo.util.Observable, {
16092 * @cfg {Roo.data.Store} store Data store to load data from.
16097 * @cfg {String|Roo.Element} el The container element.
16102 * @cfg {String|Roo.Template} tpl The template used by this View
16106 * @cfg {String} dataName the named area of the template to use as the data area
16107 * Works with domtemplates roo-name="name"
16111 * @cfg {String} selectedClass The css class to add to selected nodes
16113 selectedClass : "x-view-selected",
16115 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16120 * @cfg {String} text to display on mask (default Loading)
16124 * @cfg {Boolean} multiSelect Allow multiple selection
16126 multiSelect : false,
16128 * @cfg {Boolean} singleSelect Allow single selection
16130 singleSelect: false,
16133 * @cfg {Boolean} toggleSelect - selecting
16135 toggleSelect : false,
16138 * @cfg {Boolean} tickable - selecting
16143 * Returns the element this view is bound to.
16144 * @return {Roo.Element}
16146 getEl : function(){
16147 return this.wrapEl;
16153 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16155 refresh : function(){
16156 //Roo.log('refresh');
16159 // if we are using something like 'domtemplate', then
16160 // the what gets used is:
16161 // t.applySubtemplate(NAME, data, wrapping data..)
16162 // the outer template then get' applied with
16163 // the store 'extra data'
16164 // and the body get's added to the
16165 // roo-name="data" node?
16166 // <span class='roo-tpl-{name}'></span> ?????
16170 this.clearSelections();
16171 this.el.update("");
16173 var records = this.store.getRange();
16174 if(records.length < 1) {
16176 // is this valid?? = should it render a template??
16178 this.el.update(this.emptyText);
16182 if (this.dataName) {
16183 this.el.update(t.apply(this.store.meta)); //????
16184 el = this.el.child('.roo-tpl-' + this.dataName);
16187 for(var i = 0, len = records.length; i < len; i++){
16188 var data = this.prepareData(records[i].data, i, records[i]);
16189 this.fireEvent("preparedata", this, data, i, records[i]);
16191 var d = Roo.apply({}, data);
16194 Roo.apply(d, {'roo-id' : Roo.id()});
16198 Roo.each(this.parent.item, function(item){
16199 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16202 Roo.apply(d, {'roo-data-checked' : 'checked'});
16206 html[html.length] = Roo.util.Format.trim(
16208 t.applySubtemplate(this.dataName, d, this.store.meta) :
16215 el.update(html.join(""));
16216 this.nodes = el.dom.childNodes;
16217 this.updateIndexes(0);
16222 * Function to override to reformat the data that is sent to
16223 * the template for each node.
16224 * DEPRICATED - use the preparedata event handler.
16225 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16226 * a JSON object for an UpdateManager bound view).
16228 prepareData : function(data, index, record)
16230 this.fireEvent("preparedata", this, data, index, record);
16234 onUpdate : function(ds, record){
16235 // Roo.log('on update');
16236 this.clearSelections();
16237 var index = this.store.indexOf(record);
16238 var n = this.nodes[index];
16239 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16240 n.parentNode.removeChild(n);
16241 this.updateIndexes(index, index);
16247 onAdd : function(ds, records, index)
16249 //Roo.log(['on Add', ds, records, index] );
16250 this.clearSelections();
16251 if(this.nodes.length == 0){
16255 var n = this.nodes[index];
16256 for(var i = 0, len = records.length; i < len; i++){
16257 var d = this.prepareData(records[i].data, i, records[i]);
16259 this.tpl.insertBefore(n, d);
16262 this.tpl.append(this.el, d);
16265 this.updateIndexes(index);
16268 onRemove : function(ds, record, index){
16269 // Roo.log('onRemove');
16270 this.clearSelections();
16271 var el = this.dataName ?
16272 this.el.child('.roo-tpl-' + this.dataName) :
16275 el.dom.removeChild(this.nodes[index]);
16276 this.updateIndexes(index);
16280 * Refresh an individual node.
16281 * @param {Number} index
16283 refreshNode : function(index){
16284 this.onUpdate(this.store, this.store.getAt(index));
16287 updateIndexes : function(startIndex, endIndex){
16288 var ns = this.nodes;
16289 startIndex = startIndex || 0;
16290 endIndex = endIndex || ns.length - 1;
16291 for(var i = startIndex; i <= endIndex; i++){
16292 ns[i].nodeIndex = i;
16297 * Changes the data store this view uses and refresh the view.
16298 * @param {Store} store
16300 setStore : function(store, initial){
16301 if(!initial && this.store){
16302 this.store.un("datachanged", this.refresh);
16303 this.store.un("add", this.onAdd);
16304 this.store.un("remove", this.onRemove);
16305 this.store.un("update", this.onUpdate);
16306 this.store.un("clear", this.refresh);
16307 this.store.un("beforeload", this.onBeforeLoad);
16308 this.store.un("load", this.onLoad);
16309 this.store.un("loadexception", this.onLoad);
16313 store.on("datachanged", this.refresh, this);
16314 store.on("add", this.onAdd, this);
16315 store.on("remove", this.onRemove, this);
16316 store.on("update", this.onUpdate, this);
16317 store.on("clear", this.refresh, this);
16318 store.on("beforeload", this.onBeforeLoad, this);
16319 store.on("load", this.onLoad, this);
16320 store.on("loadexception", this.onLoad, this);
16328 * onbeforeLoad - masks the loading area.
16331 onBeforeLoad : function(store,opts)
16333 //Roo.log('onBeforeLoad');
16335 this.el.update("");
16337 this.el.mask(this.mask ? this.mask : "Loading" );
16339 onLoad : function ()
16346 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16347 * @param {HTMLElement} node
16348 * @return {HTMLElement} The template node
16350 findItemFromChild : function(node){
16351 var el = this.dataName ?
16352 this.el.child('.roo-tpl-' + this.dataName,true) :
16355 if(!node || node.parentNode == el){
16358 var p = node.parentNode;
16359 while(p && p != el){
16360 if(p.parentNode == el){
16369 onClick : function(e){
16370 var item = this.findItemFromChild(e.getTarget());
16372 var index = this.indexOf(item);
16373 if(this.onItemClick(item, index, e) !== false){
16374 this.fireEvent("click", this, index, item, e);
16377 this.clearSelections();
16382 onContextMenu : function(e){
16383 var item = this.findItemFromChild(e.getTarget());
16385 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16390 onDblClick : function(e){
16391 var item = this.findItemFromChild(e.getTarget());
16393 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16397 onItemClick : function(item, index, e)
16399 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16402 if (this.toggleSelect) {
16403 var m = this.isSelected(item) ? 'unselect' : 'select';
16406 _t[m](item, true, false);
16409 if(this.multiSelect || this.singleSelect){
16410 if(this.multiSelect && e.shiftKey && this.lastSelection){
16411 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16413 this.select(item, this.multiSelect && e.ctrlKey);
16414 this.lastSelection = item;
16417 if(!this.tickable){
16418 e.preventDefault();
16426 * Get the number of selected nodes.
16429 getSelectionCount : function(){
16430 return this.selections.length;
16434 * Get the currently selected nodes.
16435 * @return {Array} An array of HTMLElements
16437 getSelectedNodes : function(){
16438 return this.selections;
16442 * Get the indexes of the selected nodes.
16445 getSelectedIndexes : function(){
16446 var indexes = [], s = this.selections;
16447 for(var i = 0, len = s.length; i < len; i++){
16448 indexes.push(s[i].nodeIndex);
16454 * Clear all selections
16455 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16457 clearSelections : function(suppressEvent){
16458 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16459 this.cmp.elements = this.selections;
16460 this.cmp.removeClass(this.selectedClass);
16461 this.selections = [];
16462 if(!suppressEvent){
16463 this.fireEvent("selectionchange", this, this.selections);
16469 * Returns true if the passed node is selected
16470 * @param {HTMLElement/Number} node The node or node index
16471 * @return {Boolean}
16473 isSelected : function(node){
16474 var s = this.selections;
16478 node = this.getNode(node);
16479 return s.indexOf(node) !== -1;
16484 * @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
16485 * @param {Boolean} keepExisting (optional) true to keep existing selections
16486 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16488 select : function(nodeInfo, keepExisting, suppressEvent){
16489 if(nodeInfo instanceof Array){
16491 this.clearSelections(true);
16493 for(var i = 0, len = nodeInfo.length; i < len; i++){
16494 this.select(nodeInfo[i], true, true);
16498 var node = this.getNode(nodeInfo);
16499 if(!node || this.isSelected(node)){
16500 return; // already selected.
16503 this.clearSelections(true);
16506 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16507 Roo.fly(node).addClass(this.selectedClass);
16508 this.selections.push(node);
16509 if(!suppressEvent){
16510 this.fireEvent("selectionchange", this, this.selections);
16518 * @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
16519 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16520 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16522 unselect : function(nodeInfo, keepExisting, suppressEvent)
16524 if(nodeInfo instanceof Array){
16525 Roo.each(this.selections, function(s) {
16526 this.unselect(s, nodeInfo);
16530 var node = this.getNode(nodeInfo);
16531 if(!node || !this.isSelected(node)){
16532 //Roo.log("not selected");
16533 return; // not selected.
16537 Roo.each(this.selections, function(s) {
16539 Roo.fly(node).removeClass(this.selectedClass);
16546 this.selections= ns;
16547 this.fireEvent("selectionchange", this, this.selections);
16551 * Gets a template node.
16552 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16553 * @return {HTMLElement} The node or null if it wasn't found
16555 getNode : function(nodeInfo){
16556 if(typeof nodeInfo == "string"){
16557 return document.getElementById(nodeInfo);
16558 }else if(typeof nodeInfo == "number"){
16559 return this.nodes[nodeInfo];
16565 * Gets a range template nodes.
16566 * @param {Number} startIndex
16567 * @param {Number} endIndex
16568 * @return {Array} An array of nodes
16570 getNodes : function(start, end){
16571 var ns = this.nodes;
16572 start = start || 0;
16573 end = typeof end == "undefined" ? ns.length - 1 : end;
16576 for(var i = start; i <= end; i++){
16580 for(var i = start; i >= end; i--){
16588 * Finds the index of the passed node
16589 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16590 * @return {Number} The index of the node or -1
16592 indexOf : function(node){
16593 node = this.getNode(node);
16594 if(typeof node.nodeIndex == "number"){
16595 return node.nodeIndex;
16597 var ns = this.nodes;
16598 for(var i = 0, len = ns.length; i < len; i++){
16609 * based on jquery fullcalendar
16613 Roo.bootstrap = Roo.bootstrap || {};
16615 * @class Roo.bootstrap.Calendar
16616 * @extends Roo.bootstrap.Component
16617 * Bootstrap Calendar class
16618 * @cfg {Boolean} loadMask (true|false) default false
16619 * @cfg {Object} header generate the user specific header of the calendar, default false
16622 * Create a new Container
16623 * @param {Object} config The config object
16628 Roo.bootstrap.Calendar = function(config){
16629 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16633 * Fires when a date is selected
16634 * @param {DatePicker} this
16635 * @param {Date} date The selected date
16639 * @event monthchange
16640 * Fires when the displayed month changes
16641 * @param {DatePicker} this
16642 * @param {Date} date The selected month
16644 'monthchange': true,
16646 * @event evententer
16647 * Fires when mouse over an event
16648 * @param {Calendar} this
16649 * @param {event} Event
16651 'evententer': true,
16653 * @event eventleave
16654 * Fires when the mouse leaves an
16655 * @param {Calendar} this
16658 'eventleave': true,
16660 * @event eventclick
16661 * Fires when the mouse click an
16662 * @param {Calendar} this
16671 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16674 * @cfg {Number} startDay
16675 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16683 getAutoCreate : function(){
16686 var fc_button = function(name, corner, style, content ) {
16687 return Roo.apply({},{
16689 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16691 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16694 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16705 style : 'width:100%',
16712 cls : 'fc-header-left',
16714 fc_button('prev', 'left', 'arrow', '‹' ),
16715 fc_button('next', 'right', 'arrow', '›' ),
16716 { tag: 'span', cls: 'fc-header-space' },
16717 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16725 cls : 'fc-header-center',
16729 cls: 'fc-header-title',
16732 html : 'month / year'
16740 cls : 'fc-header-right',
16742 /* fc_button('month', 'left', '', 'month' ),
16743 fc_button('week', '', '', 'week' ),
16744 fc_button('day', 'right', '', 'day' )
16756 header = this.header;
16759 var cal_heads = function() {
16761 // fixme - handle this.
16763 for (var i =0; i < Date.dayNames.length; i++) {
16764 var d = Date.dayNames[i];
16767 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16768 html : d.substring(0,3)
16772 ret[0].cls += ' fc-first';
16773 ret[6].cls += ' fc-last';
16776 var cal_cell = function(n) {
16779 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16784 cls: 'fc-day-number',
16788 cls: 'fc-day-content',
16792 style: 'position: relative;' // height: 17px;
16804 var cal_rows = function() {
16807 for (var r = 0; r < 6; r++) {
16814 for (var i =0; i < Date.dayNames.length; i++) {
16815 var d = Date.dayNames[i];
16816 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16819 row.cn[0].cls+=' fc-first';
16820 row.cn[0].cn[0].style = 'min-height:90px';
16821 row.cn[6].cls+=' fc-last';
16825 ret[0].cls += ' fc-first';
16826 ret[4].cls += ' fc-prev-last';
16827 ret[5].cls += ' fc-last';
16834 cls: 'fc-border-separate',
16835 style : 'width:100%',
16843 cls : 'fc-first fc-last',
16861 cls : 'fc-content',
16862 style : "position: relative;",
16865 cls : 'fc-view fc-view-month fc-grid',
16866 style : 'position: relative',
16867 unselectable : 'on',
16870 cls : 'fc-event-container',
16871 style : 'position:absolute;z-index:8;top:0;left:0;'
16889 initEvents : function()
16892 throw "can not find store for calendar";
16898 style: "text-align:center",
16902 style: "background-color:white;width:50%;margin:250 auto",
16906 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16917 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16919 var size = this.el.select('.fc-content', true).first().getSize();
16920 this.maskEl.setSize(size.width, size.height);
16921 this.maskEl.enableDisplayMode("block");
16922 if(!this.loadMask){
16923 this.maskEl.hide();
16926 this.store = Roo.factory(this.store, Roo.data);
16927 this.store.on('load', this.onLoad, this);
16928 this.store.on('beforeload', this.onBeforeLoad, this);
16932 this.cells = this.el.select('.fc-day',true);
16933 //Roo.log(this.cells);
16934 this.textNodes = this.el.query('.fc-day-number');
16935 this.cells.addClassOnOver('fc-state-hover');
16937 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16938 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16939 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16940 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16942 this.on('monthchange', this.onMonthChange, this);
16944 this.update(new Date().clearTime());
16947 resize : function() {
16948 var sz = this.el.getSize();
16950 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16951 this.el.select('.fc-day-content div',true).setHeight(34);
16956 showPrevMonth : function(e){
16957 this.update(this.activeDate.add("mo", -1));
16959 showToday : function(e){
16960 this.update(new Date().clearTime());
16963 showNextMonth : function(e){
16964 this.update(this.activeDate.add("mo", 1));
16968 showPrevYear : function(){
16969 this.update(this.activeDate.add("y", -1));
16973 showNextYear : function(){
16974 this.update(this.activeDate.add("y", 1));
16979 update : function(date)
16981 var vd = this.activeDate;
16982 this.activeDate = date;
16983 // if(vd && this.el){
16984 // var t = date.getTime();
16985 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16986 // Roo.log('using add remove');
16988 // this.fireEvent('monthchange', this, date);
16990 // this.cells.removeClass("fc-state-highlight");
16991 // this.cells.each(function(c){
16992 // if(c.dateValue == t){
16993 // c.addClass("fc-state-highlight");
16994 // setTimeout(function(){
16995 // try{c.dom.firstChild.focus();}catch(e){}
17005 var days = date.getDaysInMonth();
17007 var firstOfMonth = date.getFirstDateOfMonth();
17008 var startingPos = firstOfMonth.getDay()-this.startDay;
17010 if(startingPos < this.startDay){
17014 var pm = date.add(Date.MONTH, -1);
17015 var prevStart = pm.getDaysInMonth()-startingPos;
17017 this.cells = this.el.select('.fc-day',true);
17018 this.textNodes = this.el.query('.fc-day-number');
17019 this.cells.addClassOnOver('fc-state-hover');
17021 var cells = this.cells.elements;
17022 var textEls = this.textNodes;
17024 Roo.each(cells, function(cell){
17025 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17028 days += startingPos;
17030 // convert everything to numbers so it's fast
17031 var day = 86400000;
17032 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17035 //Roo.log(prevStart);
17037 var today = new Date().clearTime().getTime();
17038 var sel = date.clearTime().getTime();
17039 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17040 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17041 var ddMatch = this.disabledDatesRE;
17042 var ddText = this.disabledDatesText;
17043 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17044 var ddaysText = this.disabledDaysText;
17045 var format = this.format;
17047 var setCellClass = function(cal, cell){
17051 //Roo.log('set Cell Class');
17053 var t = d.getTime();
17057 cell.dateValue = t;
17059 cell.className += " fc-today";
17060 cell.className += " fc-state-highlight";
17061 cell.title = cal.todayText;
17064 // disable highlight in other month..
17065 //cell.className += " fc-state-highlight";
17070 cell.className = " fc-state-disabled";
17071 cell.title = cal.minText;
17075 cell.className = " fc-state-disabled";
17076 cell.title = cal.maxText;
17080 if(ddays.indexOf(d.getDay()) != -1){
17081 cell.title = ddaysText;
17082 cell.className = " fc-state-disabled";
17085 if(ddMatch && format){
17086 var fvalue = d.dateFormat(format);
17087 if(ddMatch.test(fvalue)){
17088 cell.title = ddText.replace("%0", fvalue);
17089 cell.className = " fc-state-disabled";
17093 if (!cell.initialClassName) {
17094 cell.initialClassName = cell.dom.className;
17097 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17102 for(; i < startingPos; i++) {
17103 textEls[i].innerHTML = (++prevStart);
17104 d.setDate(d.getDate()+1);
17106 cells[i].className = "fc-past fc-other-month";
17107 setCellClass(this, cells[i]);
17112 for(; i < days; i++){
17113 intDay = i - startingPos + 1;
17114 textEls[i].innerHTML = (intDay);
17115 d.setDate(d.getDate()+1);
17117 cells[i].className = ''; // "x-date-active";
17118 setCellClass(this, cells[i]);
17122 for(; i < 42; i++) {
17123 textEls[i].innerHTML = (++extraDays);
17124 d.setDate(d.getDate()+1);
17126 cells[i].className = "fc-future fc-other-month";
17127 setCellClass(this, cells[i]);
17130 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17132 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17134 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17135 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17137 if(totalRows != 6){
17138 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17139 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17142 this.fireEvent('monthchange', this, date);
17146 if(!this.internalRender){
17147 var main = this.el.dom.firstChild;
17148 var w = main.offsetWidth;
17149 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17150 Roo.fly(main).setWidth(w);
17151 this.internalRender = true;
17152 // opera does not respect the auto grow header center column
17153 // then, after it gets a width opera refuses to recalculate
17154 // without a second pass
17155 if(Roo.isOpera && !this.secondPass){
17156 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17157 this.secondPass = true;
17158 this.update.defer(10, this, [date]);
17165 findCell : function(dt) {
17166 dt = dt.clearTime().getTime();
17168 this.cells.each(function(c){
17169 //Roo.log("check " +c.dateValue + '?=' + dt);
17170 if(c.dateValue == dt){
17180 findCells : function(ev) {
17181 var s = ev.start.clone().clearTime().getTime();
17183 var e= ev.end.clone().clearTime().getTime();
17186 this.cells.each(function(c){
17187 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17189 if(c.dateValue > e){
17192 if(c.dateValue < s){
17201 // findBestRow: function(cells)
17205 // for (var i =0 ; i < cells.length;i++) {
17206 // ret = Math.max(cells[i].rows || 0,ret);
17213 addItem : function(ev)
17215 // look for vertical location slot in
17216 var cells = this.findCells(ev);
17218 // ev.row = this.findBestRow(cells);
17220 // work out the location.
17224 for(var i =0; i < cells.length; i++) {
17226 cells[i].row = cells[0].row;
17229 cells[i].row = cells[i].row + 1;
17239 if (crow.start.getY() == cells[i].getY()) {
17241 crow.end = cells[i];
17258 cells[0].events.push(ev);
17260 this.calevents.push(ev);
17263 clearEvents: function() {
17265 if(!this.calevents){
17269 Roo.each(this.cells.elements, function(c){
17275 Roo.each(this.calevents, function(e) {
17276 Roo.each(e.els, function(el) {
17277 el.un('mouseenter' ,this.onEventEnter, this);
17278 el.un('mouseleave' ,this.onEventLeave, this);
17283 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17289 renderEvents: function()
17293 this.cells.each(function(c) {
17302 if(c.row != c.events.length){
17303 r = 4 - (4 - (c.row - c.events.length));
17306 c.events = ev.slice(0, r);
17307 c.more = ev.slice(r);
17309 if(c.more.length && c.more.length == 1){
17310 c.events.push(c.more.pop());
17313 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17317 this.cells.each(function(c) {
17319 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17322 for (var e = 0; e < c.events.length; e++){
17323 var ev = c.events[e];
17324 var rows = ev.rows;
17326 for(var i = 0; i < rows.length; i++) {
17328 // how many rows should it span..
17331 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17332 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17334 unselectable : "on",
17337 cls: 'fc-event-inner',
17341 // cls: 'fc-event-time',
17342 // html : cells.length > 1 ? '' : ev.time
17346 cls: 'fc-event-title',
17347 html : String.format('{0}', ev.title)
17354 cls: 'ui-resizable-handle ui-resizable-e',
17355 html : '  '
17362 cfg.cls += ' fc-event-start';
17364 if ((i+1) == rows.length) {
17365 cfg.cls += ' fc-event-end';
17368 var ctr = _this.el.select('.fc-event-container',true).first();
17369 var cg = ctr.createChild(cfg);
17371 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17372 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17374 var r = (c.more.length) ? 1 : 0;
17375 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17376 cg.setWidth(ebox.right - sbox.x -2);
17378 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17379 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17380 cg.on('click', _this.onEventClick, _this, ev);
17391 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17392 style : 'position: absolute',
17393 unselectable : "on",
17396 cls: 'fc-event-inner',
17400 cls: 'fc-event-title',
17408 cls: 'ui-resizable-handle ui-resizable-e',
17409 html : '  '
17415 var ctr = _this.el.select('.fc-event-container',true).first();
17416 var cg = ctr.createChild(cfg);
17418 var sbox = c.select('.fc-day-content',true).first().getBox();
17419 var ebox = c.select('.fc-day-content',true).first().getBox();
17421 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17422 cg.setWidth(ebox.right - sbox.x -2);
17424 cg.on('click', _this.onMoreEventClick, _this, c.more);
17434 onEventEnter: function (e, el,event,d) {
17435 this.fireEvent('evententer', this, el, event);
17438 onEventLeave: function (e, el,event,d) {
17439 this.fireEvent('eventleave', this, el, event);
17442 onEventClick: function (e, el,event,d) {
17443 this.fireEvent('eventclick', this, el, event);
17446 onMonthChange: function () {
17450 onMoreEventClick: function(e, el, more)
17454 this.calpopover.placement = 'right';
17455 this.calpopover.setTitle('More');
17457 this.calpopover.setContent('');
17459 var ctr = this.calpopover.el.select('.popover-content', true).first();
17461 Roo.each(more, function(m){
17463 cls : 'fc-event-hori fc-event-draggable',
17466 var cg = ctr.createChild(cfg);
17468 cg.on('click', _this.onEventClick, _this, m);
17471 this.calpopover.show(el);
17476 onLoad: function ()
17478 this.calevents = [];
17481 if(this.store.getCount() > 0){
17482 this.store.data.each(function(d){
17485 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17486 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17487 time : d.data.start_time,
17488 title : d.data.title,
17489 description : d.data.description,
17490 venue : d.data.venue
17495 this.renderEvents();
17497 if(this.calevents.length && this.loadMask){
17498 this.maskEl.hide();
17502 onBeforeLoad: function()
17504 this.clearEvents();
17506 this.maskEl.show();
17520 * @class Roo.bootstrap.Popover
17521 * @extends Roo.bootstrap.Component
17522 * Bootstrap Popover class
17523 * @cfg {String} html contents of the popover (or false to use children..)
17524 * @cfg {String} title of popover (or false to hide)
17525 * @cfg {String} placement how it is placed
17526 * @cfg {String} trigger click || hover (or false to trigger manually)
17527 * @cfg {String} over what (parent or false to trigger manually.)
17528 * @cfg {Number} delay - delay before showing
17531 * Create a new Popover
17532 * @param {Object} config The config object
17535 Roo.bootstrap.Popover = function(config){
17536 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17542 * After the popover show
17544 * @param {Roo.bootstrap.Popover} this
17549 * After the popover hide
17551 * @param {Roo.bootstrap.Popover} this
17557 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17559 title: 'Fill in a title',
17562 placement : 'right',
17563 trigger : 'hover', // hover
17569 can_build_overlaid : false,
17571 getChildContainer : function()
17573 return this.el.select('.popover-content',true).first();
17576 getAutoCreate : function(){
17579 cls : 'popover roo-dynamic',
17580 style: 'display:block',
17586 cls : 'popover-inner',
17590 cls: 'popover-title',
17594 cls : 'popover-content',
17605 setTitle: function(str)
17608 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17610 setContent: function(str)
17613 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17615 // as it get's added to the bottom of the page.
17616 onRender : function(ct, position)
17618 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17620 var cfg = Roo.apply({}, this.getAutoCreate());
17624 cfg.cls += ' ' + this.cls;
17627 cfg.style = this.style;
17629 //Roo.log("adding to ");
17630 this.el = Roo.get(document.body).createChild(cfg, position);
17631 // Roo.log(this.el);
17636 initEvents : function()
17638 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17639 this.el.enableDisplayMode('block');
17641 if (this.over === false) {
17644 if (this.triggers === false) {
17647 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17648 var triggers = this.trigger ? this.trigger.split(' ') : [];
17649 Roo.each(triggers, function(trigger) {
17651 if (trigger == 'click') {
17652 on_el.on('click', this.toggle, this);
17653 } else if (trigger != 'manual') {
17654 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17655 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17657 on_el.on(eventIn ,this.enter, this);
17658 on_el.on(eventOut, this.leave, this);
17669 toggle : function () {
17670 this.hoverState == 'in' ? this.leave() : this.enter();
17673 enter : function () {
17675 clearTimeout(this.timeout);
17677 this.hoverState = 'in';
17679 if (!this.delay || !this.delay.show) {
17684 this.timeout = setTimeout(function () {
17685 if (_t.hoverState == 'in') {
17688 }, this.delay.show)
17691 leave : function() {
17692 clearTimeout(this.timeout);
17694 this.hoverState = 'out';
17696 if (!this.delay || !this.delay.hide) {
17701 this.timeout = setTimeout(function () {
17702 if (_t.hoverState == 'out') {
17705 }, this.delay.hide)
17708 show : function (on_el)
17711 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17715 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17716 if (this.html !== false) {
17717 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17719 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17720 if (!this.title.length) {
17721 this.el.select('.popover-title',true).hide();
17724 var placement = typeof this.placement == 'function' ?
17725 this.placement.call(this, this.el, on_el) :
17728 var autoToken = /\s?auto?\s?/i;
17729 var autoPlace = autoToken.test(placement);
17731 placement = placement.replace(autoToken, '') || 'top';
17735 //this.el.setXY([0,0]);
17737 this.el.dom.style.display='block';
17738 this.el.addClass(placement);
17740 //this.el.appendTo(on_el);
17742 var p = this.getPosition();
17743 var box = this.el.getBox();
17748 var align = Roo.bootstrap.Popover.alignment[placement];
17751 this.el.alignTo(on_el, align[0],align[1]);
17752 //var arrow = this.el.select('.arrow',true).first();
17753 //arrow.set(align[2],
17755 this.el.addClass('in');
17758 if (this.el.hasClass('fade')) {
17762 this.hoverState = 'in';
17764 this.fireEvent('show', this);
17769 this.el.setXY([0,0]);
17770 this.el.removeClass('in');
17772 this.hoverState = null;
17774 this.fireEvent('hide', this);
17779 Roo.bootstrap.Popover.alignment = {
17780 'left' : ['r-l', [-10,0], 'right'],
17781 'right' : ['l-r', [10,0], 'left'],
17782 'bottom' : ['t-b', [0,10], 'top'],
17783 'top' : [ 'b-t', [0,-10], 'bottom']
17794 * @class Roo.bootstrap.Progress
17795 * @extends Roo.bootstrap.Component
17796 * Bootstrap Progress class
17797 * @cfg {Boolean} striped striped of the progress bar
17798 * @cfg {Boolean} active animated of the progress bar
17802 * Create a new Progress
17803 * @param {Object} config The config object
17806 Roo.bootstrap.Progress = function(config){
17807 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17810 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17815 getAutoCreate : function(){
17823 cfg.cls += ' progress-striped';
17827 cfg.cls += ' active';
17846 * @class Roo.bootstrap.ProgressBar
17847 * @extends Roo.bootstrap.Component
17848 * Bootstrap ProgressBar class
17849 * @cfg {Number} aria_valuenow aria-value now
17850 * @cfg {Number} aria_valuemin aria-value min
17851 * @cfg {Number} aria_valuemax aria-value max
17852 * @cfg {String} label label for the progress bar
17853 * @cfg {String} panel (success | info | warning | danger )
17854 * @cfg {String} role role of the progress bar
17855 * @cfg {String} sr_only text
17859 * Create a new ProgressBar
17860 * @param {Object} config The config object
17863 Roo.bootstrap.ProgressBar = function(config){
17864 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17867 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17871 aria_valuemax : 100,
17877 getAutoCreate : function()
17882 cls: 'progress-bar',
17883 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17895 cfg.role = this.role;
17898 if(this.aria_valuenow){
17899 cfg['aria-valuenow'] = this.aria_valuenow;
17902 if(this.aria_valuemin){
17903 cfg['aria-valuemin'] = this.aria_valuemin;
17906 if(this.aria_valuemax){
17907 cfg['aria-valuemax'] = this.aria_valuemax;
17910 if(this.label && !this.sr_only){
17911 cfg.html = this.label;
17915 cfg.cls += ' progress-bar-' + this.panel;
17921 update : function(aria_valuenow)
17923 this.aria_valuenow = aria_valuenow;
17925 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17940 * @class Roo.bootstrap.TabGroup
17941 * @extends Roo.bootstrap.Column
17942 * Bootstrap Column class
17943 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17944 * @cfg {Boolean} carousel true to make the group behave like a carousel
17945 * @cfg {Boolean} bullets show bullets for the panels
17946 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17947 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17948 * @cfg {Boolean} showarrow (true|false) show arrow default true
17951 * Create a new TabGroup
17952 * @param {Object} config The config object
17955 Roo.bootstrap.TabGroup = function(config){
17956 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17958 this.navId = Roo.id();
17961 Roo.bootstrap.TabGroup.register(this);
17965 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17968 transition : false,
17973 slideOnTouch : false,
17976 getAutoCreate : function()
17978 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17980 cfg.cls += ' tab-content';
17982 if (this.carousel) {
17983 cfg.cls += ' carousel slide';
17986 cls : 'carousel-inner',
17990 if(this.bullets && !Roo.isTouch){
17993 cls : 'carousel-bullets',
17997 if(this.bullets_cls){
17998 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18005 cfg.cn[0].cn.push(bullets);
18008 if(this.showarrow){
18009 cfg.cn[0].cn.push({
18011 class : 'carousel-arrow',
18015 class : 'carousel-prev',
18019 class : 'fa fa-chevron-left'
18025 class : 'carousel-next',
18029 class : 'fa fa-chevron-right'
18042 initEvents: function()
18044 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18045 // this.el.on("touchstart", this.onTouchStart, this);
18048 if(this.autoslide){
18051 this.slideFn = window.setInterval(function() {
18052 _this.showPanelNext();
18056 if(this.showarrow){
18057 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18058 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18064 // onTouchStart : function(e, el, o)
18066 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18070 // this.showPanelNext();
18074 getChildContainer : function()
18076 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18080 * register a Navigation item
18081 * @param {Roo.bootstrap.NavItem} the navitem to add
18083 register : function(item)
18085 this.tabs.push( item);
18086 item.navId = this.navId; // not really needed..
18091 getActivePanel : function()
18094 Roo.each(this.tabs, function(t) {
18104 getPanelByName : function(n)
18107 Roo.each(this.tabs, function(t) {
18108 if (t.tabId == n) {
18116 indexOfPanel : function(p)
18119 Roo.each(this.tabs, function(t,i) {
18120 if (t.tabId == p.tabId) {
18129 * show a specific panel
18130 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18131 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18133 showPanel : function (pan)
18135 if(this.transition || typeof(pan) == 'undefined'){
18136 Roo.log("waiting for the transitionend");
18140 if (typeof(pan) == 'number') {
18141 pan = this.tabs[pan];
18144 if (typeof(pan) == 'string') {
18145 pan = this.getPanelByName(pan);
18148 var cur = this.getActivePanel();
18151 Roo.log('pan or acitve pan is undefined');
18155 if (pan.tabId == this.getActivePanel().tabId) {
18159 if (false === cur.fireEvent('beforedeactivate')) {
18163 if(this.bullets > 0 && !Roo.isTouch){
18164 this.setActiveBullet(this.indexOfPanel(pan));
18167 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18169 this.transition = true;
18170 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18171 var lr = dir == 'next' ? 'left' : 'right';
18172 pan.el.addClass(dir); // or prev
18173 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18174 cur.el.addClass(lr); // or right
18175 pan.el.addClass(lr);
18178 cur.el.on('transitionend', function() {
18179 Roo.log("trans end?");
18181 pan.el.removeClass([lr,dir]);
18182 pan.setActive(true);
18184 cur.el.removeClass([lr]);
18185 cur.setActive(false);
18187 _this.transition = false;
18189 }, this, { single: true } );
18194 cur.setActive(false);
18195 pan.setActive(true);
18200 showPanelNext : function()
18202 var i = this.indexOfPanel(this.getActivePanel());
18204 if (i >= this.tabs.length - 1 && !this.autoslide) {
18208 if (i >= this.tabs.length - 1 && this.autoslide) {
18212 this.showPanel(this.tabs[i+1]);
18215 showPanelPrev : function()
18217 var i = this.indexOfPanel(this.getActivePanel());
18219 if (i < 1 && !this.autoslide) {
18223 if (i < 1 && this.autoslide) {
18224 i = this.tabs.length;
18227 this.showPanel(this.tabs[i-1]);
18231 addBullet: function()
18233 if(!this.bullets || Roo.isTouch){
18236 var ctr = this.el.select('.carousel-bullets',true).first();
18237 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18238 var bullet = ctr.createChild({
18239 cls : 'bullet bullet-' + i
18240 },ctr.dom.lastChild);
18245 bullet.on('click', (function(e, el, o, ii, t){
18247 e.preventDefault();
18249 this.showPanel(ii);
18251 if(this.autoslide && this.slideFn){
18252 clearInterval(this.slideFn);
18253 this.slideFn = window.setInterval(function() {
18254 _this.showPanelNext();
18258 }).createDelegate(this, [i, bullet], true));
18263 setActiveBullet : function(i)
18269 Roo.each(this.el.select('.bullet', true).elements, function(el){
18270 el.removeClass('selected');
18273 var bullet = this.el.select('.bullet-' + i, true).first();
18279 bullet.addClass('selected');
18290 Roo.apply(Roo.bootstrap.TabGroup, {
18294 * register a Navigation Group
18295 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18297 register : function(navgrp)
18299 this.groups[navgrp.navId] = navgrp;
18303 * fetch a Navigation Group based on the navigation ID
18304 * if one does not exist , it will get created.
18305 * @param {string} the navgroup to add
18306 * @returns {Roo.bootstrap.NavGroup} the navgroup
18308 get: function(navId) {
18309 if (typeof(this.groups[navId]) == 'undefined') {
18310 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18312 return this.groups[navId] ;
18327 * @class Roo.bootstrap.TabPanel
18328 * @extends Roo.bootstrap.Component
18329 * Bootstrap TabPanel class
18330 * @cfg {Boolean} active panel active
18331 * @cfg {String} html panel content
18332 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18333 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18334 * @cfg {String} href click to link..
18338 * Create a new TabPanel
18339 * @param {Object} config The config object
18342 Roo.bootstrap.TabPanel = function(config){
18343 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18347 * Fires when the active status changes
18348 * @param {Roo.bootstrap.TabPanel} this
18349 * @param {Boolean} state the new state
18354 * @event beforedeactivate
18355 * Fires before a tab is de-activated - can be used to do validation on a form.
18356 * @param {Roo.bootstrap.TabPanel} this
18357 * @return {Boolean} false if there is an error
18360 'beforedeactivate': true
18363 this.tabId = this.tabId || Roo.id();
18367 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18375 getAutoCreate : function(){
18378 // item is needed for carousel - not sure if it has any effect otherwise
18379 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18380 html: this.html || ''
18384 cfg.cls += ' active';
18388 cfg.tabId = this.tabId;
18395 initEvents: function()
18397 var p = this.parent();
18399 this.navId = this.navId || p.navId;
18401 if (typeof(this.navId) != 'undefined') {
18402 // not really needed.. but just in case.. parent should be a NavGroup.
18403 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18407 var i = tg.tabs.length - 1;
18409 if(this.active && tg.bullets > 0 && i < tg.bullets){
18410 tg.setActiveBullet(i);
18414 this.el.on('click', this.onClick, this);
18417 this.el.on("touchstart", this.onTouchStart, this);
18418 this.el.on("touchmove", this.onTouchMove, this);
18419 this.el.on("touchend", this.onTouchEnd, this);
18424 onRender : function(ct, position)
18426 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18429 setActive : function(state)
18431 Roo.log("panel - set active " + this.tabId + "=" + state);
18433 this.active = state;
18435 this.el.removeClass('active');
18437 } else if (!this.el.hasClass('active')) {
18438 this.el.addClass('active');
18441 this.fireEvent('changed', this, state);
18444 onClick : function(e)
18446 e.preventDefault();
18448 if(!this.href.length){
18452 window.location.href = this.href;
18461 onTouchStart : function(e)
18463 this.swiping = false;
18465 this.startX = e.browserEvent.touches[0].clientX;
18466 this.startY = e.browserEvent.touches[0].clientY;
18469 onTouchMove : function(e)
18471 this.swiping = true;
18473 this.endX = e.browserEvent.touches[0].clientX;
18474 this.endY = e.browserEvent.touches[0].clientY;
18477 onTouchEnd : function(e)
18484 var tabGroup = this.parent();
18486 if(this.endX > this.startX){ // swiping right
18487 tabGroup.showPanelPrev();
18491 if(this.startX > this.endX){ // swiping left
18492 tabGroup.showPanelNext();
18511 * @class Roo.bootstrap.DateField
18512 * @extends Roo.bootstrap.Input
18513 * Bootstrap DateField class
18514 * @cfg {Number} weekStart default 0
18515 * @cfg {String} viewMode default empty, (months|years)
18516 * @cfg {String} minViewMode default empty, (months|years)
18517 * @cfg {Number} startDate default -Infinity
18518 * @cfg {Number} endDate default Infinity
18519 * @cfg {Boolean} todayHighlight default false
18520 * @cfg {Boolean} todayBtn default false
18521 * @cfg {Boolean} calendarWeeks default false
18522 * @cfg {Object} daysOfWeekDisabled default empty
18523 * @cfg {Boolean} singleMode default false (true | false)
18525 * @cfg {Boolean} keyboardNavigation default true
18526 * @cfg {String} language default en
18529 * Create a new DateField
18530 * @param {Object} config The config object
18533 Roo.bootstrap.DateField = function(config){
18534 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18538 * Fires when this field show.
18539 * @param {Roo.bootstrap.DateField} this
18540 * @param {Mixed} date The date value
18545 * Fires when this field hide.
18546 * @param {Roo.bootstrap.DateField} this
18547 * @param {Mixed} date The date value
18552 * Fires when select a date.
18553 * @param {Roo.bootstrap.DateField} this
18554 * @param {Mixed} date The date value
18558 * @event beforeselect
18559 * Fires when before select a date.
18560 * @param {Roo.bootstrap.DateField} this
18561 * @param {Mixed} date The date value
18563 beforeselect : true
18567 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18570 * @cfg {String} format
18571 * The default date format string which can be overriden for localization support. The format must be
18572 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18576 * @cfg {String} altFormats
18577 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18578 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18580 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18588 todayHighlight : false,
18594 keyboardNavigation: true,
18596 calendarWeeks: false,
18598 startDate: -Infinity,
18602 daysOfWeekDisabled: [],
18606 singleMode : false,
18608 UTCDate: function()
18610 return new Date(Date.UTC.apply(Date, arguments));
18613 UTCToday: function()
18615 var today = new Date();
18616 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18619 getDate: function() {
18620 var d = this.getUTCDate();
18621 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18624 getUTCDate: function() {
18628 setDate: function(d) {
18629 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18632 setUTCDate: function(d) {
18634 this.setValue(this.formatDate(this.date));
18637 onRender: function(ct, position)
18640 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18642 this.language = this.language || 'en';
18643 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18644 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18646 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18647 this.format = this.format || 'm/d/y';
18648 this.isInline = false;
18649 this.isInput = true;
18650 this.component = this.el.select('.add-on', true).first() || false;
18651 this.component = (this.component && this.component.length === 0) ? false : this.component;
18652 this.hasInput = this.component && this.inputEl().length;
18654 if (typeof(this.minViewMode === 'string')) {
18655 switch (this.minViewMode) {
18657 this.minViewMode = 1;
18660 this.minViewMode = 2;
18663 this.minViewMode = 0;
18668 if (typeof(this.viewMode === 'string')) {
18669 switch (this.viewMode) {
18682 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18684 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18686 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18688 this.picker().on('mousedown', this.onMousedown, this);
18689 this.picker().on('click', this.onClick, this);
18691 this.picker().addClass('datepicker-dropdown');
18693 this.startViewMode = this.viewMode;
18695 if(this.singleMode){
18696 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18697 v.setVisibilityMode(Roo.Element.DISPLAY);
18701 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18702 v.setStyle('width', '189px');
18706 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18707 if(!this.calendarWeeks){
18712 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18713 v.attr('colspan', function(i, val){
18714 return parseInt(val) + 1;
18719 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18721 this.setStartDate(this.startDate);
18722 this.setEndDate(this.endDate);
18724 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18731 if(this.isInline) {
18736 picker : function()
18738 return this.pickerEl;
18739 // return this.el.select('.datepicker', true).first();
18742 fillDow: function()
18744 var dowCnt = this.weekStart;
18753 if(this.calendarWeeks){
18761 while (dowCnt < this.weekStart + 7) {
18765 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18769 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18772 fillMonths: function()
18775 var months = this.picker().select('>.datepicker-months td', true).first();
18777 months.dom.innerHTML = '';
18783 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18786 months.createChild(month);
18793 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;
18795 if (this.date < this.startDate) {
18796 this.viewDate = new Date(this.startDate);
18797 } else if (this.date > this.endDate) {
18798 this.viewDate = new Date(this.endDate);
18800 this.viewDate = new Date(this.date);
18808 var d = new Date(this.viewDate),
18809 year = d.getUTCFullYear(),
18810 month = d.getUTCMonth(),
18811 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18812 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18813 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18814 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18815 currentDate = this.date && this.date.valueOf(),
18816 today = this.UTCToday();
18818 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18820 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18822 // this.picker.select('>tfoot th.today').
18823 // .text(dates[this.language].today)
18824 // .toggle(this.todayBtn !== false);
18826 this.updateNavArrows();
18829 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18831 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18833 prevMonth.setUTCDate(day);
18835 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18837 var nextMonth = new Date(prevMonth);
18839 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18841 nextMonth = nextMonth.valueOf();
18843 var fillMonths = false;
18845 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18847 while(prevMonth.valueOf() <= nextMonth) {
18850 if (prevMonth.getUTCDay() === this.weekStart) {
18852 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18860 if(this.calendarWeeks){
18861 // ISO 8601: First week contains first thursday.
18862 // ISO also states week starts on Monday, but we can be more abstract here.
18864 // Start of current week: based on weekstart/current date
18865 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18866 // Thursday of this week
18867 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18868 // First Thursday of year, year from thursday
18869 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18870 // Calendar week: ms between thursdays, div ms per day, div 7 days
18871 calWeek = (th - yth) / 864e5 / 7 + 1;
18873 fillMonths.cn.push({
18881 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18883 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18886 if (this.todayHighlight &&
18887 prevMonth.getUTCFullYear() == today.getFullYear() &&
18888 prevMonth.getUTCMonth() == today.getMonth() &&
18889 prevMonth.getUTCDate() == today.getDate()) {
18890 clsName += ' today';
18893 if (currentDate && prevMonth.valueOf() === currentDate) {
18894 clsName += ' active';
18897 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18898 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18899 clsName += ' disabled';
18902 fillMonths.cn.push({
18904 cls: 'day ' + clsName,
18905 html: prevMonth.getDate()
18908 prevMonth.setDate(prevMonth.getDate()+1);
18911 var currentYear = this.date && this.date.getUTCFullYear();
18912 var currentMonth = this.date && this.date.getUTCMonth();
18914 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18916 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18917 v.removeClass('active');
18919 if(currentYear === year && k === currentMonth){
18920 v.addClass('active');
18923 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18924 v.addClass('disabled');
18930 year = parseInt(year/10, 10) * 10;
18932 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18934 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18937 for (var i = -1; i < 11; i++) {
18938 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18940 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18948 showMode: function(dir)
18951 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18954 Roo.each(this.picker().select('>div',true).elements, function(v){
18955 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18958 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18963 if(this.isInline) {
18967 this.picker().removeClass(['bottom', 'top']);
18969 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18971 * place to the top of element!
18975 this.picker().addClass('top');
18976 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18981 this.picker().addClass('bottom');
18983 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18986 parseDate : function(value)
18988 if(!value || value instanceof Date){
18991 var v = Date.parseDate(value, this.format);
18992 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18993 v = Date.parseDate(value, 'Y-m-d');
18995 if(!v && this.altFormats){
18996 if(!this.altFormatsArray){
18997 this.altFormatsArray = this.altFormats.split("|");
18999 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19000 v = Date.parseDate(value, this.altFormatsArray[i]);
19006 formatDate : function(date, fmt)
19008 return (!date || !(date instanceof Date)) ?
19009 date : date.dateFormat(fmt || this.format);
19012 onFocus : function()
19014 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19018 onBlur : function()
19020 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19022 var d = this.inputEl().getValue();
19029 showPopup : function()
19031 this.picker().show();
19035 this.fireEvent('showpopup', this, this.date);
19038 hidePopup : function()
19040 if(this.isInline) {
19043 this.picker().hide();
19044 this.viewMode = this.startViewMode;
19047 this.fireEvent('hidepopup', this, this.date);
19051 onMousedown: function(e)
19053 e.stopPropagation();
19054 e.preventDefault();
19059 Roo.bootstrap.DateField.superclass.keyup.call(this);
19063 setValue: function(v)
19065 if(this.fireEvent('beforeselect', this, v) !== false){
19066 var d = new Date(this.parseDate(v) ).clearTime();
19068 if(isNaN(d.getTime())){
19069 this.date = this.viewDate = '';
19070 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19074 v = this.formatDate(d);
19076 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19078 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19082 this.fireEvent('select', this, this.date);
19086 getValue: function()
19088 return this.formatDate(this.date);
19091 fireKey: function(e)
19093 if (!this.picker().isVisible()){
19094 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19100 var dateChanged = false,
19102 newDate, newViewDate;
19107 e.preventDefault();
19111 if (!this.keyboardNavigation) {
19114 dir = e.keyCode == 37 ? -1 : 1;
19117 newDate = this.moveYear(this.date, dir);
19118 newViewDate = this.moveYear(this.viewDate, dir);
19119 } else if (e.shiftKey){
19120 newDate = this.moveMonth(this.date, dir);
19121 newViewDate = this.moveMonth(this.viewDate, dir);
19123 newDate = new Date(this.date);
19124 newDate.setUTCDate(this.date.getUTCDate() + dir);
19125 newViewDate = new Date(this.viewDate);
19126 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19128 if (this.dateWithinRange(newDate)){
19129 this.date = newDate;
19130 this.viewDate = newViewDate;
19131 this.setValue(this.formatDate(this.date));
19133 e.preventDefault();
19134 dateChanged = true;
19139 if (!this.keyboardNavigation) {
19142 dir = e.keyCode == 38 ? -1 : 1;
19144 newDate = this.moveYear(this.date, dir);
19145 newViewDate = this.moveYear(this.viewDate, dir);
19146 } else if (e.shiftKey){
19147 newDate = this.moveMonth(this.date, dir);
19148 newViewDate = this.moveMonth(this.viewDate, dir);
19150 newDate = new Date(this.date);
19151 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19152 newViewDate = new Date(this.viewDate);
19153 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19155 if (this.dateWithinRange(newDate)){
19156 this.date = newDate;
19157 this.viewDate = newViewDate;
19158 this.setValue(this.formatDate(this.date));
19160 e.preventDefault();
19161 dateChanged = true;
19165 this.setValue(this.formatDate(this.date));
19167 e.preventDefault();
19170 this.setValue(this.formatDate(this.date));
19184 onClick: function(e)
19186 e.stopPropagation();
19187 e.preventDefault();
19189 var target = e.getTarget();
19191 if(target.nodeName.toLowerCase() === 'i'){
19192 target = Roo.get(target).dom.parentNode;
19195 var nodeName = target.nodeName;
19196 var className = target.className;
19197 var html = target.innerHTML;
19198 //Roo.log(nodeName);
19200 switch(nodeName.toLowerCase()) {
19202 switch(className) {
19208 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19209 switch(this.viewMode){
19211 this.viewDate = this.moveMonth(this.viewDate, dir);
19215 this.viewDate = this.moveYear(this.viewDate, dir);
19221 var date = new Date();
19222 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19224 this.setValue(this.formatDate(this.date));
19231 if (className.indexOf('disabled') < 0) {
19232 this.viewDate.setUTCDate(1);
19233 if (className.indexOf('month') > -1) {
19234 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19236 var year = parseInt(html, 10) || 0;
19237 this.viewDate.setUTCFullYear(year);
19241 if(this.singleMode){
19242 this.setValue(this.formatDate(this.viewDate));
19253 //Roo.log(className);
19254 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19255 var day = parseInt(html, 10) || 1;
19256 var year = this.viewDate.getUTCFullYear(),
19257 month = this.viewDate.getUTCMonth();
19259 if (className.indexOf('old') > -1) {
19266 } else if (className.indexOf('new') > -1) {
19274 //Roo.log([year,month,day]);
19275 this.date = this.UTCDate(year, month, day,0,0,0,0);
19276 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19278 //Roo.log(this.formatDate(this.date));
19279 this.setValue(this.formatDate(this.date));
19286 setStartDate: function(startDate)
19288 this.startDate = startDate || -Infinity;
19289 if (this.startDate !== -Infinity) {
19290 this.startDate = this.parseDate(this.startDate);
19293 this.updateNavArrows();
19296 setEndDate: function(endDate)
19298 this.endDate = endDate || Infinity;
19299 if (this.endDate !== Infinity) {
19300 this.endDate = this.parseDate(this.endDate);
19303 this.updateNavArrows();
19306 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19308 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19309 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19310 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19312 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19313 return parseInt(d, 10);
19316 this.updateNavArrows();
19319 updateNavArrows: function()
19321 if(this.singleMode){
19325 var d = new Date(this.viewDate),
19326 year = d.getUTCFullYear(),
19327 month = d.getUTCMonth();
19329 Roo.each(this.picker().select('.prev', true).elements, function(v){
19331 switch (this.viewMode) {
19334 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19340 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19347 Roo.each(this.picker().select('.next', true).elements, function(v){
19349 switch (this.viewMode) {
19352 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19358 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19366 moveMonth: function(date, dir)
19371 var new_date = new Date(date.valueOf()),
19372 day = new_date.getUTCDate(),
19373 month = new_date.getUTCMonth(),
19374 mag = Math.abs(dir),
19376 dir = dir > 0 ? 1 : -1;
19379 // If going back one month, make sure month is not current month
19380 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19382 return new_date.getUTCMonth() == month;
19384 // If going forward one month, make sure month is as expected
19385 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19387 return new_date.getUTCMonth() != new_month;
19389 new_month = month + dir;
19390 new_date.setUTCMonth(new_month);
19391 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19392 if (new_month < 0 || new_month > 11) {
19393 new_month = (new_month + 12) % 12;
19396 // For magnitudes >1, move one month at a time...
19397 for (var i=0; i<mag; i++) {
19398 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19399 new_date = this.moveMonth(new_date, dir);
19401 // ...then reset the day, keeping it in the new month
19402 new_month = new_date.getUTCMonth();
19403 new_date.setUTCDate(day);
19405 return new_month != new_date.getUTCMonth();
19408 // Common date-resetting loop -- if date is beyond end of month, make it
19411 new_date.setUTCDate(--day);
19412 new_date.setUTCMonth(new_month);
19417 moveYear: function(date, dir)
19419 return this.moveMonth(date, dir*12);
19422 dateWithinRange: function(date)
19424 return date >= this.startDate && date <= this.endDate;
19430 this.picker().remove();
19433 validateValue : function(value)
19435 if(this.getVisibilityEl().hasClass('hidden')){
19439 if(value.length < 1) {
19440 if(this.allowBlank){
19446 if(value.length < this.minLength){
19449 if(value.length > this.maxLength){
19453 var vt = Roo.form.VTypes;
19454 if(!vt[this.vtype](value, this)){
19458 if(typeof this.validator == "function"){
19459 var msg = this.validator(value);
19465 if(this.regex && !this.regex.test(value)){
19469 if(typeof(this.parseDate(value)) == 'undefined'){
19473 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19477 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19487 this.date = this.viewDate = '';
19489 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19494 Roo.apply(Roo.bootstrap.DateField, {
19505 html: '<i class="fa fa-arrow-left"/>'
19515 html: '<i class="fa fa-arrow-right"/>'
19557 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19558 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19559 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19560 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19561 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19574 navFnc: 'FullYear',
19579 navFnc: 'FullYear',
19584 Roo.apply(Roo.bootstrap.DateField, {
19588 cls: 'datepicker dropdown-menu roo-dynamic',
19592 cls: 'datepicker-days',
19596 cls: 'table-condensed',
19598 Roo.bootstrap.DateField.head,
19602 Roo.bootstrap.DateField.footer
19609 cls: 'datepicker-months',
19613 cls: 'table-condensed',
19615 Roo.bootstrap.DateField.head,
19616 Roo.bootstrap.DateField.content,
19617 Roo.bootstrap.DateField.footer
19624 cls: 'datepicker-years',
19628 cls: 'table-condensed',
19630 Roo.bootstrap.DateField.head,
19631 Roo.bootstrap.DateField.content,
19632 Roo.bootstrap.DateField.footer
19651 * @class Roo.bootstrap.TimeField
19652 * @extends Roo.bootstrap.Input
19653 * Bootstrap DateField class
19657 * Create a new TimeField
19658 * @param {Object} config The config object
19661 Roo.bootstrap.TimeField = function(config){
19662 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19666 * Fires when this field show.
19667 * @param {Roo.bootstrap.DateField} thisthis
19668 * @param {Mixed} date The date value
19673 * Fires when this field hide.
19674 * @param {Roo.bootstrap.DateField} this
19675 * @param {Mixed} date The date value
19680 * Fires when select a date.
19681 * @param {Roo.bootstrap.DateField} this
19682 * @param {Mixed} date The date value
19688 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19691 * @cfg {String} format
19692 * The default time format string which can be overriden for localization support. The format must be
19693 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19697 onRender: function(ct, position)
19700 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19702 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19704 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19706 this.pop = this.picker().select('>.datepicker-time',true).first();
19707 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19709 this.picker().on('mousedown', this.onMousedown, this);
19710 this.picker().on('click', this.onClick, this);
19712 this.picker().addClass('datepicker-dropdown');
19717 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19718 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19719 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19720 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19721 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19722 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19726 fireKey: function(e){
19727 if (!this.picker().isVisible()){
19728 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19734 e.preventDefault();
19742 this.onTogglePeriod();
19745 this.onIncrementMinutes();
19748 this.onDecrementMinutes();
19757 onClick: function(e) {
19758 e.stopPropagation();
19759 e.preventDefault();
19762 picker : function()
19764 return this.el.select('.datepicker', true).first();
19767 fillTime: function()
19769 var time = this.pop.select('tbody', true).first();
19771 time.dom.innerHTML = '';
19786 cls: 'hours-up glyphicon glyphicon-chevron-up'
19806 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19827 cls: 'timepicker-hour',
19842 cls: 'timepicker-minute',
19857 cls: 'btn btn-primary period',
19879 cls: 'hours-down glyphicon glyphicon-chevron-down'
19899 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19917 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19924 var hours = this.time.getHours();
19925 var minutes = this.time.getMinutes();
19938 hours = hours - 12;
19942 hours = '0' + hours;
19946 minutes = '0' + minutes;
19949 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19950 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19951 this.pop.select('button', true).first().dom.innerHTML = period;
19957 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19959 var cls = ['bottom'];
19961 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19968 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19973 this.picker().addClass(cls.join('-'));
19977 Roo.each(cls, function(c){
19979 _this.picker().setTop(_this.inputEl().getHeight());
19983 _this.picker().setTop(0 - _this.picker().getHeight());
19988 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19992 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19999 onFocus : function()
20001 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20005 onBlur : function()
20007 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20013 this.picker().show();
20018 this.fireEvent('show', this, this.date);
20023 this.picker().hide();
20026 this.fireEvent('hide', this, this.date);
20029 setTime : function()
20032 this.setValue(this.time.format(this.format));
20034 this.fireEvent('select', this, this.date);
20039 onMousedown: function(e){
20040 e.stopPropagation();
20041 e.preventDefault();
20044 onIncrementHours: function()
20046 Roo.log('onIncrementHours');
20047 this.time = this.time.add(Date.HOUR, 1);
20052 onDecrementHours: function()
20054 Roo.log('onDecrementHours');
20055 this.time = this.time.add(Date.HOUR, -1);
20059 onIncrementMinutes: function()
20061 Roo.log('onIncrementMinutes');
20062 this.time = this.time.add(Date.MINUTE, 1);
20066 onDecrementMinutes: function()
20068 Roo.log('onDecrementMinutes');
20069 this.time = this.time.add(Date.MINUTE, -1);
20073 onTogglePeriod: function()
20075 Roo.log('onTogglePeriod');
20076 this.time = this.time.add(Date.HOUR, 12);
20083 Roo.apply(Roo.bootstrap.TimeField, {
20113 cls: 'btn btn-info ok',
20125 Roo.apply(Roo.bootstrap.TimeField, {
20129 cls: 'datepicker dropdown-menu',
20133 cls: 'datepicker-time',
20137 cls: 'table-condensed',
20139 Roo.bootstrap.TimeField.content,
20140 Roo.bootstrap.TimeField.footer
20159 * @class Roo.bootstrap.MonthField
20160 * @extends Roo.bootstrap.Input
20161 * Bootstrap MonthField class
20163 * @cfg {String} language default en
20166 * Create a new MonthField
20167 * @param {Object} config The config object
20170 Roo.bootstrap.MonthField = function(config){
20171 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20176 * Fires when this field show.
20177 * @param {Roo.bootstrap.MonthField} this
20178 * @param {Mixed} date The date value
20183 * Fires when this field hide.
20184 * @param {Roo.bootstrap.MonthField} this
20185 * @param {Mixed} date The date value
20190 * Fires when select a date.
20191 * @param {Roo.bootstrap.MonthField} this
20192 * @param {String} oldvalue The old value
20193 * @param {String} newvalue The new value
20199 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20201 onRender: function(ct, position)
20204 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20206 this.language = this.language || 'en';
20207 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20208 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20210 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20211 this.isInline = false;
20212 this.isInput = true;
20213 this.component = this.el.select('.add-on', true).first() || false;
20214 this.component = (this.component && this.component.length === 0) ? false : this.component;
20215 this.hasInput = this.component && this.inputEL().length;
20217 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20219 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20221 this.picker().on('mousedown', this.onMousedown, this);
20222 this.picker().on('click', this.onClick, this);
20224 this.picker().addClass('datepicker-dropdown');
20226 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20227 v.setStyle('width', '189px');
20234 if(this.isInline) {
20240 setValue: function(v, suppressEvent)
20242 var o = this.getValue();
20244 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20248 if(suppressEvent !== true){
20249 this.fireEvent('select', this, o, v);
20254 getValue: function()
20259 onClick: function(e)
20261 e.stopPropagation();
20262 e.preventDefault();
20264 var target = e.getTarget();
20266 if(target.nodeName.toLowerCase() === 'i'){
20267 target = Roo.get(target).dom.parentNode;
20270 var nodeName = target.nodeName;
20271 var className = target.className;
20272 var html = target.innerHTML;
20274 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20278 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20280 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20286 picker : function()
20288 return this.pickerEl;
20291 fillMonths: function()
20294 var months = this.picker().select('>.datepicker-months td', true).first();
20296 months.dom.innerHTML = '';
20302 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20305 months.createChild(month);
20314 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20315 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20318 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20319 e.removeClass('active');
20321 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20322 e.addClass('active');
20329 if(this.isInline) {
20333 this.picker().removeClass(['bottom', 'top']);
20335 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20337 * place to the top of element!
20341 this.picker().addClass('top');
20342 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20347 this.picker().addClass('bottom');
20349 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20352 onFocus : function()
20354 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20358 onBlur : function()
20360 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20362 var d = this.inputEl().getValue();
20371 this.picker().show();
20372 this.picker().select('>.datepicker-months', true).first().show();
20376 this.fireEvent('show', this, this.date);
20381 if(this.isInline) {
20384 this.picker().hide();
20385 this.fireEvent('hide', this, this.date);
20389 onMousedown: function(e)
20391 e.stopPropagation();
20392 e.preventDefault();
20397 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20401 fireKey: function(e)
20403 if (!this.picker().isVisible()){
20404 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20415 e.preventDefault();
20419 dir = e.keyCode == 37 ? -1 : 1;
20421 this.vIndex = this.vIndex + dir;
20423 if(this.vIndex < 0){
20427 if(this.vIndex > 11){
20431 if(isNaN(this.vIndex)){
20435 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20441 dir = e.keyCode == 38 ? -1 : 1;
20443 this.vIndex = this.vIndex + dir * 4;
20445 if(this.vIndex < 0){
20449 if(this.vIndex > 11){
20453 if(isNaN(this.vIndex)){
20457 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20462 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20463 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20467 e.preventDefault();
20470 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20471 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20487 this.picker().remove();
20492 Roo.apply(Roo.bootstrap.MonthField, {
20511 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20512 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20517 Roo.apply(Roo.bootstrap.MonthField, {
20521 cls: 'datepicker dropdown-menu roo-dynamic',
20525 cls: 'datepicker-months',
20529 cls: 'table-condensed',
20531 Roo.bootstrap.DateField.content
20551 * @class Roo.bootstrap.CheckBox
20552 * @extends Roo.bootstrap.Input
20553 * Bootstrap CheckBox class
20555 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20556 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20557 * @cfg {String} boxLabel The text that appears beside the checkbox
20558 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20559 * @cfg {Boolean} checked initnal the element
20560 * @cfg {Boolean} inline inline the element (default false)
20561 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20562 * @cfg {String} tooltip label tooltip
20565 * Create a new CheckBox
20566 * @param {Object} config The config object
20569 Roo.bootstrap.CheckBox = function(config){
20570 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20575 * Fires when the element is checked or unchecked.
20576 * @param {Roo.bootstrap.CheckBox} this This input
20577 * @param {Boolean} checked The new checked value
20582 * Fires when the element is click.
20583 * @param {Roo.bootstrap.CheckBox} this This input
20590 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20592 inputType: 'checkbox',
20601 getAutoCreate : function()
20603 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20609 cfg.cls = 'form-group ' + this.inputType; //input-group
20612 cfg.cls += ' ' + this.inputType + '-inline';
20618 type : this.inputType,
20619 value : this.inputValue,
20620 cls : 'roo-' + this.inputType, //'form-box',
20621 placeholder : this.placeholder || ''
20625 if(this.inputType != 'radio'){
20629 cls : 'roo-hidden-value',
20630 value : this.checked ? this.inputValue : this.valueOff
20635 if (this.weight) { // Validity check?
20636 cfg.cls += " " + this.inputType + "-" + this.weight;
20639 if (this.disabled) {
20640 input.disabled=true;
20644 input.checked = this.checked;
20649 input.name = this.name;
20651 if(this.inputType != 'radio'){
20652 hidden.name = this.name;
20653 input.name = '_hidden_' + this.name;
20658 input.cls += ' input-' + this.size;
20663 ['xs','sm','md','lg'].map(function(size){
20664 if (settings[size]) {
20665 cfg.cls += ' col-' + size + '-' + settings[size];
20669 var inputblock = input;
20671 if (this.before || this.after) {
20674 cls : 'input-group',
20679 inputblock.cn.push({
20681 cls : 'input-group-addon',
20686 inputblock.cn.push(input);
20688 if(this.inputType != 'radio'){
20689 inputblock.cn.push(hidden);
20693 inputblock.cn.push({
20695 cls : 'input-group-addon',
20702 if (align ==='left' && this.fieldLabel.length) {
20703 // Roo.log("left and has label");
20708 cls : 'control-label',
20709 html : this.fieldLabel
20719 if(this.labelWidth > 12){
20720 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20723 if(this.labelWidth < 13 && this.labelmd == 0){
20724 this.labelmd = this.labelWidth;
20727 if(this.labellg > 0){
20728 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20729 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20732 if(this.labelmd > 0){
20733 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20734 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20737 if(this.labelsm > 0){
20738 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20739 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20742 if(this.labelxs > 0){
20743 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20744 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20747 } else if ( this.fieldLabel.length) {
20748 // Roo.log(" label");
20752 tag: this.boxLabel ? 'span' : 'label',
20754 cls: 'control-label box-input-label',
20755 //cls : 'input-group-addon',
20756 html : this.fieldLabel
20765 // Roo.log(" no label && no align");
20766 cfg.cn = [ inputblock ] ;
20772 var boxLabelCfg = {
20774 //'for': id, // box label is handled by onclick - so no for...
20776 html: this.boxLabel
20780 boxLabelCfg.tooltip = this.tooltip;
20783 cfg.cn.push(boxLabelCfg);
20786 if(this.inputType != 'radio'){
20787 cfg.cn.push(hidden);
20795 * return the real input element.
20797 inputEl: function ()
20799 return this.el.select('input.roo-' + this.inputType,true).first();
20801 hiddenEl: function ()
20803 return this.el.select('input.roo-hidden-value',true).first();
20806 labelEl: function()
20808 return this.el.select('label.control-label',true).first();
20810 /* depricated... */
20814 return this.labelEl();
20817 boxLabelEl: function()
20819 return this.el.select('label.box-label',true).first();
20822 initEvents : function()
20824 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20826 this.inputEl().on('click', this.onClick, this);
20828 if (this.boxLabel) {
20829 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20832 this.startValue = this.getValue();
20835 Roo.bootstrap.CheckBox.register(this);
20839 onClick : function(e)
20841 if(this.fireEvent('click', this, e) !== false){
20842 this.setChecked(!this.checked);
20847 setChecked : function(state,suppressEvent)
20849 this.startValue = this.getValue();
20851 if(this.inputType == 'radio'){
20853 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20854 e.dom.checked = false;
20857 this.inputEl().dom.checked = true;
20859 this.inputEl().dom.value = this.inputValue;
20861 if(suppressEvent !== true){
20862 this.fireEvent('check', this, true);
20870 this.checked = state;
20872 this.inputEl().dom.checked = state;
20875 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20877 if(suppressEvent !== true){
20878 this.fireEvent('check', this, state);
20884 getValue : function()
20886 if(this.inputType == 'radio'){
20887 return this.getGroupValue();
20890 return this.hiddenEl().dom.value;
20894 getGroupValue : function()
20896 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20900 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20903 setValue : function(v,suppressEvent)
20905 if(this.inputType == 'radio'){
20906 this.setGroupValue(v, suppressEvent);
20910 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20915 setGroupValue : function(v, suppressEvent)
20917 this.startValue = this.getValue();
20919 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20920 e.dom.checked = false;
20922 if(e.dom.value == v){
20923 e.dom.checked = true;
20927 if(suppressEvent !== true){
20928 this.fireEvent('check', this, true);
20936 validate : function()
20938 if(this.getVisibilityEl().hasClass('hidden')){
20944 (this.inputType == 'radio' && this.validateRadio()) ||
20945 (this.inputType == 'checkbox' && this.validateCheckbox())
20951 this.markInvalid();
20955 validateRadio : function()
20957 if(this.getVisibilityEl().hasClass('hidden')){
20961 if(this.allowBlank){
20967 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20968 if(!e.dom.checked){
20980 validateCheckbox : function()
20983 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20984 //return (this.getValue() == this.inputValue) ? true : false;
20987 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20995 for(var i in group){
20996 if(group[i].el.isVisible(true)){
21004 for(var i in group){
21009 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21016 * Mark this field as valid
21018 markValid : function()
21022 this.fireEvent('valid', this);
21024 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21027 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21034 if(this.inputType == 'radio'){
21035 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21036 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21037 e.findParent('.form-group', false, true).addClass(_this.validClass);
21044 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21045 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21049 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21055 for(var i in group){
21056 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21057 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21062 * Mark this field as invalid
21063 * @param {String} msg The validation message
21065 markInvalid : function(msg)
21067 if(this.allowBlank){
21073 this.fireEvent('invalid', this, msg);
21075 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21078 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21082 label.markInvalid();
21085 if(this.inputType == 'radio'){
21086 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21087 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21088 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21095 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21096 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21100 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21106 for(var i in group){
21107 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21108 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21113 clearInvalid : function()
21115 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21117 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21119 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21121 if (label && label.iconEl) {
21122 label.iconEl.removeClass(label.validClass);
21123 label.iconEl.removeClass(label.invalidClass);
21127 disable : function()
21129 if(this.inputType != 'radio'){
21130 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21137 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21138 _this.getActionEl().addClass(this.disabledClass);
21139 e.dom.disabled = true;
21143 this.disabled = true;
21144 this.fireEvent("disable", this);
21148 enable : function()
21150 if(this.inputType != 'radio'){
21151 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21158 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21159 _this.getActionEl().removeClass(this.disabledClass);
21160 e.dom.disabled = false;
21164 this.disabled = false;
21165 this.fireEvent("enable", this);
21169 setBoxLabel : function(v)
21174 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21180 Roo.apply(Roo.bootstrap.CheckBox, {
21185 * register a CheckBox Group
21186 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21188 register : function(checkbox)
21190 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21191 this.groups[checkbox.groupId] = {};
21194 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21198 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21202 * fetch a CheckBox Group based on the group ID
21203 * @param {string} the group ID
21204 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21206 get: function(groupId) {
21207 if (typeof(this.groups[groupId]) == 'undefined') {
21211 return this.groups[groupId] ;
21224 * @class Roo.bootstrap.Radio
21225 * @extends Roo.bootstrap.Component
21226 * Bootstrap Radio class
21227 * @cfg {String} boxLabel - the label associated
21228 * @cfg {String} value - the value of radio
21231 * Create a new Radio
21232 * @param {Object} config The config object
21234 Roo.bootstrap.Radio = function(config){
21235 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21239 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21245 getAutoCreate : function()
21249 cls : 'form-group radio',
21254 html : this.boxLabel
21262 initEvents : function()
21264 this.parent().register(this);
21266 this.el.on('click', this.onClick, this);
21270 onClick : function(e)
21272 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21273 this.setChecked(true);
21277 setChecked : function(state, suppressEvent)
21279 this.parent().setValue(this.value, suppressEvent);
21283 setBoxLabel : function(v)
21288 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21303 * @class Roo.bootstrap.SecurePass
21304 * @extends Roo.bootstrap.Input
21305 * Bootstrap SecurePass class
21309 * Create a new SecurePass
21310 * @param {Object} config The config object
21313 Roo.bootstrap.SecurePass = function (config) {
21314 // these go here, so the translation tool can replace them..
21316 PwdEmpty: "Please type a password, and then retype it to confirm.",
21317 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21318 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21319 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21320 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21321 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21322 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21323 TooWeak: "Your password is Too Weak."
21325 this.meterLabel = "Password strength:";
21326 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21327 this.meterClass = [
21328 "roo-password-meter-tooweak",
21329 "roo-password-meter-weak",
21330 "roo-password-meter-medium",
21331 "roo-password-meter-strong",
21332 "roo-password-meter-grey"
21337 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21340 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21342 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21344 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21345 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21346 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21347 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21348 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21349 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21350 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21360 * @cfg {String/Object} Label for the strength meter (defaults to
21361 * 'Password strength:')
21366 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21367 * ['Weak', 'Medium', 'Strong'])
21370 pwdStrengths: false,
21383 initEvents: function ()
21385 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21387 if (this.el.is('input[type=password]') && Roo.isSafari) {
21388 this.el.on('keydown', this.SafariOnKeyDown, this);
21391 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21394 onRender: function (ct, position)
21396 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21397 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21398 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21400 this.trigger.createChild({
21405 cls: 'roo-password-meter-grey col-xs-12',
21408 //width: this.meterWidth + 'px'
21412 cls: 'roo-password-meter-text'
21418 if (this.hideTrigger) {
21419 this.trigger.setDisplayed(false);
21421 this.setSize(this.width || '', this.height || '');
21424 onDestroy: function ()
21426 if (this.trigger) {
21427 this.trigger.removeAllListeners();
21428 this.trigger.remove();
21431 this.wrap.remove();
21433 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21436 checkStrength: function ()
21438 var pwd = this.inputEl().getValue();
21439 if (pwd == this._lastPwd) {
21444 if (this.ClientSideStrongPassword(pwd)) {
21446 } else if (this.ClientSideMediumPassword(pwd)) {
21448 } else if (this.ClientSideWeakPassword(pwd)) {
21454 Roo.log('strength1: ' + strength);
21456 //var pm = this.trigger.child('div/div/div').dom;
21457 var pm = this.trigger.child('div/div');
21458 pm.removeClass(this.meterClass);
21459 pm.addClass(this.meterClass[strength]);
21462 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21464 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21466 this._lastPwd = pwd;
21470 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21472 this._lastPwd = '';
21474 var pm = this.trigger.child('div/div');
21475 pm.removeClass(this.meterClass);
21476 pm.addClass('roo-password-meter-grey');
21479 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21482 this.inputEl().dom.type='password';
21485 validateValue: function (value)
21488 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21491 if (value.length == 0) {
21492 if (this.allowBlank) {
21493 this.clearInvalid();
21497 this.markInvalid(this.errors.PwdEmpty);
21498 this.errorMsg = this.errors.PwdEmpty;
21506 if ('[\x21-\x7e]*'.match(value)) {
21507 this.markInvalid(this.errors.PwdBadChar);
21508 this.errorMsg = this.errors.PwdBadChar;
21511 if (value.length < 6) {
21512 this.markInvalid(this.errors.PwdShort);
21513 this.errorMsg = this.errors.PwdShort;
21516 if (value.length > 16) {
21517 this.markInvalid(this.errors.PwdLong);
21518 this.errorMsg = this.errors.PwdLong;
21522 if (this.ClientSideStrongPassword(value)) {
21524 } else if (this.ClientSideMediumPassword(value)) {
21526 } else if (this.ClientSideWeakPassword(value)) {
21533 if (strength < 2) {
21534 //this.markInvalid(this.errors.TooWeak);
21535 this.errorMsg = this.errors.TooWeak;
21540 console.log('strength2: ' + strength);
21542 //var pm = this.trigger.child('div/div/div').dom;
21544 var pm = this.trigger.child('div/div');
21545 pm.removeClass(this.meterClass);
21546 pm.addClass(this.meterClass[strength]);
21548 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21550 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21552 this.errorMsg = '';
21556 CharacterSetChecks: function (type)
21559 this.fResult = false;
21562 isctype: function (character, type)
21565 case this.kCapitalLetter:
21566 if (character >= 'A' && character <= 'Z') {
21571 case this.kSmallLetter:
21572 if (character >= 'a' && character <= 'z') {
21578 if (character >= '0' && character <= '9') {
21583 case this.kPunctuation:
21584 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21595 IsLongEnough: function (pwd, size)
21597 return !(pwd == null || isNaN(size) || pwd.length < size);
21600 SpansEnoughCharacterSets: function (word, nb)
21602 if (!this.IsLongEnough(word, nb))
21607 var characterSetChecks = new Array(
21608 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21609 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21612 for (var index = 0; index < word.length; ++index) {
21613 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21614 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21615 characterSetChecks[nCharSet].fResult = true;
21622 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21623 if (characterSetChecks[nCharSet].fResult) {
21628 if (nCharSets < nb) {
21634 ClientSideStrongPassword: function (pwd)
21636 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21639 ClientSideMediumPassword: function (pwd)
21641 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21644 ClientSideWeakPassword: function (pwd)
21646 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21649 })//<script type="text/javascript">
21652 * Based Ext JS Library 1.1.1
21653 * Copyright(c) 2006-2007, Ext JS, LLC.
21659 * @class Roo.HtmlEditorCore
21660 * @extends Roo.Component
21661 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21663 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21666 Roo.HtmlEditorCore = function(config){
21669 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21674 * @event initialize
21675 * Fires when the editor is fully initialized (including the iframe)
21676 * @param {Roo.HtmlEditorCore} this
21681 * Fires when the editor is first receives the focus. Any insertion must wait
21682 * until after this event.
21683 * @param {Roo.HtmlEditorCore} this
21687 * @event beforesync
21688 * Fires before the textarea is updated with content from the editor iframe. Return false
21689 * to cancel the sync.
21690 * @param {Roo.HtmlEditorCore} this
21691 * @param {String} html
21695 * @event beforepush
21696 * Fires before the iframe editor is updated with content from the textarea. Return false
21697 * to cancel the push.
21698 * @param {Roo.HtmlEditorCore} this
21699 * @param {String} html
21704 * Fires when the textarea is updated with content from the editor iframe.
21705 * @param {Roo.HtmlEditorCore} this
21706 * @param {String} html
21711 * Fires when the iframe editor is updated with content from the textarea.
21712 * @param {Roo.HtmlEditorCore} this
21713 * @param {String} html
21718 * @event editorevent
21719 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21720 * @param {Roo.HtmlEditorCore} this
21726 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21728 // defaults : white / black...
21729 this.applyBlacklists();
21736 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21740 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21746 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21751 * @cfg {Number} height (in pixels)
21755 * @cfg {Number} width (in pixels)
21760 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21763 stylesheets: false,
21768 // private properties
21769 validationEvent : false,
21771 initialized : false,
21773 sourceEditMode : false,
21774 onFocus : Roo.emptyFn,
21776 hideMode:'offsets',
21780 // blacklist + whitelisted elements..
21787 * Protected method that will not generally be called directly. It
21788 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21789 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21791 getDocMarkup : function(){
21795 // inherit styels from page...??
21796 if (this.stylesheets === false) {
21798 Roo.get(document.head).select('style').each(function(node) {
21799 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21802 Roo.get(document.head).select('link').each(function(node) {
21803 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21806 } else if (!this.stylesheets.length) {
21808 st = '<style type="text/css">' +
21809 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21812 st = '<style type="text/css">' +
21817 st += '<style type="text/css">' +
21818 'IMG { cursor: pointer } ' +
21821 var cls = 'roo-htmleditor-body';
21823 if(this.bodyCls.length){
21824 cls += ' ' + this.bodyCls;
21827 return '<html><head>' + st +
21828 //<style type="text/css">' +
21829 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21831 ' </head><body class="' + cls + '"></body></html>';
21835 onRender : function(ct, position)
21838 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21839 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21842 this.el.dom.style.border = '0 none';
21843 this.el.dom.setAttribute('tabIndex', -1);
21844 this.el.addClass('x-hidden hide');
21848 if(Roo.isIE){ // fix IE 1px bogus margin
21849 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21853 this.frameId = Roo.id();
21857 var iframe = this.owner.wrap.createChild({
21859 cls: 'form-control', // bootstrap..
21861 name: this.frameId,
21862 frameBorder : 'no',
21863 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21868 this.iframe = iframe.dom;
21870 this.assignDocWin();
21872 this.doc.designMode = 'on';
21875 this.doc.write(this.getDocMarkup());
21879 var task = { // must defer to wait for browser to be ready
21881 //console.log("run task?" + this.doc.readyState);
21882 this.assignDocWin();
21883 if(this.doc.body || this.doc.readyState == 'complete'){
21885 this.doc.designMode="on";
21889 Roo.TaskMgr.stop(task);
21890 this.initEditor.defer(10, this);
21897 Roo.TaskMgr.start(task);
21902 onResize : function(w, h)
21904 Roo.log('resize: ' +w + ',' + h );
21905 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21909 if(typeof w == 'number'){
21911 this.iframe.style.width = w + 'px';
21913 if(typeof h == 'number'){
21915 this.iframe.style.height = h + 'px';
21917 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21924 * Toggles the editor between standard and source edit mode.
21925 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21927 toggleSourceEdit : function(sourceEditMode){
21929 this.sourceEditMode = sourceEditMode === true;
21931 if(this.sourceEditMode){
21933 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21936 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21937 //this.iframe.className = '';
21940 //this.setSize(this.owner.wrap.getSize());
21941 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21948 * Protected method that will not generally be called directly. If you need/want
21949 * custom HTML cleanup, this is the method you should override.
21950 * @param {String} html The HTML to be cleaned
21951 * return {String} The cleaned HTML
21953 cleanHtml : function(html){
21954 html = String(html);
21955 if(html.length > 5){
21956 if(Roo.isSafari){ // strip safari nonsense
21957 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21960 if(html == ' '){
21967 * HTML Editor -> Textarea
21968 * Protected method that will not generally be called directly. Syncs the contents
21969 * of the editor iframe with the textarea.
21971 syncValue : function(){
21972 if(this.initialized){
21973 var bd = (this.doc.body || this.doc.documentElement);
21974 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21975 var html = bd.innerHTML;
21977 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21978 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21980 html = '<div style="'+m[0]+'">' + html + '</div>';
21983 html = this.cleanHtml(html);
21984 // fix up the special chars.. normaly like back quotes in word...
21985 // however we do not want to do this with chinese..
21986 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21987 var cc = b.charCodeAt();
21989 (cc >= 0x4E00 && cc < 0xA000 ) ||
21990 (cc >= 0x3400 && cc < 0x4E00 ) ||
21991 (cc >= 0xf900 && cc < 0xfb00 )
21997 if(this.owner.fireEvent('beforesync', this, html) !== false){
21998 this.el.dom.value = html;
21999 this.owner.fireEvent('sync', this, html);
22005 * Protected method that will not generally be called directly. Pushes the value of the textarea
22006 * into the iframe editor.
22008 pushValue : function(){
22009 if(this.initialized){
22010 var v = this.el.dom.value.trim();
22012 // if(v.length < 1){
22016 if(this.owner.fireEvent('beforepush', this, v) !== false){
22017 var d = (this.doc.body || this.doc.documentElement);
22019 this.cleanUpPaste();
22020 this.el.dom.value = d.innerHTML;
22021 this.owner.fireEvent('push', this, v);
22027 deferFocus : function(){
22028 this.focus.defer(10, this);
22032 focus : function(){
22033 if(this.win && !this.sourceEditMode){
22040 assignDocWin: function()
22042 var iframe = this.iframe;
22045 this.doc = iframe.contentWindow.document;
22046 this.win = iframe.contentWindow;
22048 // if (!Roo.get(this.frameId)) {
22051 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22052 // this.win = Roo.get(this.frameId).dom.contentWindow;
22054 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22058 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22059 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22064 initEditor : function(){
22065 //console.log("INIT EDITOR");
22066 this.assignDocWin();
22070 this.doc.designMode="on";
22072 this.doc.write(this.getDocMarkup());
22075 var dbody = (this.doc.body || this.doc.documentElement);
22076 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22077 // this copies styles from the containing element into thsi one..
22078 // not sure why we need all of this..
22079 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22081 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22082 //ss['background-attachment'] = 'fixed'; // w3c
22083 dbody.bgProperties = 'fixed'; // ie
22084 //Roo.DomHelper.applyStyles(dbody, ss);
22085 Roo.EventManager.on(this.doc, {
22086 //'mousedown': this.onEditorEvent,
22087 'mouseup': this.onEditorEvent,
22088 'dblclick': this.onEditorEvent,
22089 'click': this.onEditorEvent,
22090 'keyup': this.onEditorEvent,
22095 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22097 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22098 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22100 this.initialized = true;
22102 this.owner.fireEvent('initialize', this);
22107 onDestroy : function(){
22113 //for (var i =0; i < this.toolbars.length;i++) {
22114 // // fixme - ask toolbars for heights?
22115 // this.toolbars[i].onDestroy();
22118 //this.wrap.dom.innerHTML = '';
22119 //this.wrap.remove();
22124 onFirstFocus : function(){
22126 this.assignDocWin();
22129 this.activated = true;
22132 if(Roo.isGecko){ // prevent silly gecko errors
22134 var s = this.win.getSelection();
22135 if(!s.focusNode || s.focusNode.nodeType != 3){
22136 var r = s.getRangeAt(0);
22137 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22142 this.execCmd('useCSS', true);
22143 this.execCmd('styleWithCSS', false);
22146 this.owner.fireEvent('activate', this);
22150 adjustFont: function(btn){
22151 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22152 //if(Roo.isSafari){ // safari
22155 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22156 if(Roo.isSafari){ // safari
22157 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22158 v = (v < 10) ? 10 : v;
22159 v = (v > 48) ? 48 : v;
22160 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22165 v = Math.max(1, v+adjust);
22167 this.execCmd('FontSize', v );
22170 onEditorEvent : function(e)
22172 this.owner.fireEvent('editorevent', this, e);
22173 // this.updateToolbar();
22174 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22177 insertTag : function(tg)
22179 // could be a bit smarter... -> wrap the current selected tRoo..
22180 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22182 range = this.createRange(this.getSelection());
22183 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22184 wrappingNode.appendChild(range.extractContents());
22185 range.insertNode(wrappingNode);
22192 this.execCmd("formatblock", tg);
22196 insertText : function(txt)
22200 var range = this.createRange();
22201 range.deleteContents();
22202 //alert(Sender.getAttribute('label'));
22204 range.insertNode(this.doc.createTextNode(txt));
22210 * Executes a Midas editor command on the editor document and performs necessary focus and
22211 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22212 * @param {String} cmd The Midas command
22213 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22215 relayCmd : function(cmd, value){
22217 this.execCmd(cmd, value);
22218 this.owner.fireEvent('editorevent', this);
22219 //this.updateToolbar();
22220 this.owner.deferFocus();
22224 * Executes a Midas editor command directly on the editor document.
22225 * For visual commands, you should use {@link #relayCmd} instead.
22226 * <b>This should only be called after the editor is initialized.</b>
22227 * @param {String} cmd The Midas command
22228 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22230 execCmd : function(cmd, value){
22231 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22238 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22240 * @param {String} text | dom node..
22242 insertAtCursor : function(text)
22245 if(!this.activated){
22251 var r = this.doc.selection.createRange();
22262 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22266 // from jquery ui (MIT licenced)
22268 var win = this.win;
22270 if (win.getSelection && win.getSelection().getRangeAt) {
22271 range = win.getSelection().getRangeAt(0);
22272 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22273 range.insertNode(node);
22274 } else if (win.document.selection && win.document.selection.createRange) {
22275 // no firefox support
22276 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22277 win.document.selection.createRange().pasteHTML(txt);
22279 // no firefox support
22280 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22281 this.execCmd('InsertHTML', txt);
22290 mozKeyPress : function(e){
22292 var c = e.getCharCode(), cmd;
22295 c = String.fromCharCode(c).toLowerCase();
22309 this.cleanUpPaste.defer(100, this);
22317 e.preventDefault();
22325 fixKeys : function(){ // load time branching for fastest keydown performance
22327 return function(e){
22328 var k = e.getKey(), r;
22331 r = this.doc.selection.createRange();
22334 r.pasteHTML('    ');
22341 r = this.doc.selection.createRange();
22343 var target = r.parentElement();
22344 if(!target || target.tagName.toLowerCase() != 'li'){
22346 r.pasteHTML('<br />');
22352 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22353 this.cleanUpPaste.defer(100, this);
22359 }else if(Roo.isOpera){
22360 return function(e){
22361 var k = e.getKey();
22365 this.execCmd('InsertHTML','    ');
22368 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22369 this.cleanUpPaste.defer(100, this);
22374 }else if(Roo.isSafari){
22375 return function(e){
22376 var k = e.getKey();
22380 this.execCmd('InsertText','\t');
22384 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22385 this.cleanUpPaste.defer(100, this);
22393 getAllAncestors: function()
22395 var p = this.getSelectedNode();
22398 a.push(p); // push blank onto stack..
22399 p = this.getParentElement();
22403 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22407 a.push(this.doc.body);
22411 lastSelNode : false,
22414 getSelection : function()
22416 this.assignDocWin();
22417 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22420 getSelectedNode: function()
22422 // this may only work on Gecko!!!
22424 // should we cache this!!!!
22429 var range = this.createRange(this.getSelection()).cloneRange();
22432 var parent = range.parentElement();
22434 var testRange = range.duplicate();
22435 testRange.moveToElementText(parent);
22436 if (testRange.inRange(range)) {
22439 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22442 parent = parent.parentElement;
22447 // is ancestor a text element.
22448 var ac = range.commonAncestorContainer;
22449 if (ac.nodeType == 3) {
22450 ac = ac.parentNode;
22453 var ar = ac.childNodes;
22456 var other_nodes = [];
22457 var has_other_nodes = false;
22458 for (var i=0;i<ar.length;i++) {
22459 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22462 // fullly contained node.
22464 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22469 // probably selected..
22470 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22471 other_nodes.push(ar[i]);
22475 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22480 has_other_nodes = true;
22482 if (!nodes.length && other_nodes.length) {
22483 nodes= other_nodes;
22485 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22491 createRange: function(sel)
22493 // this has strange effects when using with
22494 // top toolbar - not sure if it's a great idea.
22495 //this.editor.contentWindow.focus();
22496 if (typeof sel != "undefined") {
22498 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22500 return this.doc.createRange();
22503 return this.doc.createRange();
22506 getParentElement: function()
22509 this.assignDocWin();
22510 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22512 var range = this.createRange(sel);
22515 var p = range.commonAncestorContainer;
22516 while (p.nodeType == 3) { // text node
22527 * Range intersection.. the hard stuff...
22531 * [ -- selected range --- ]
22535 * if end is before start or hits it. fail.
22536 * if start is after end or hits it fail.
22538 * if either hits (but other is outside. - then it's not
22544 // @see http://www.thismuchiknow.co.uk/?p=64.
22545 rangeIntersectsNode : function(range, node)
22547 var nodeRange = node.ownerDocument.createRange();
22549 nodeRange.selectNode(node);
22551 nodeRange.selectNodeContents(node);
22554 var rangeStartRange = range.cloneRange();
22555 rangeStartRange.collapse(true);
22557 var rangeEndRange = range.cloneRange();
22558 rangeEndRange.collapse(false);
22560 var nodeStartRange = nodeRange.cloneRange();
22561 nodeStartRange.collapse(true);
22563 var nodeEndRange = nodeRange.cloneRange();
22564 nodeEndRange.collapse(false);
22566 return rangeStartRange.compareBoundaryPoints(
22567 Range.START_TO_START, nodeEndRange) == -1 &&
22568 rangeEndRange.compareBoundaryPoints(
22569 Range.START_TO_START, nodeStartRange) == 1;
22573 rangeCompareNode : function(range, node)
22575 var nodeRange = node.ownerDocument.createRange();
22577 nodeRange.selectNode(node);
22579 nodeRange.selectNodeContents(node);
22583 range.collapse(true);
22585 nodeRange.collapse(true);
22587 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22588 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22590 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22592 var nodeIsBefore = ss == 1;
22593 var nodeIsAfter = ee == -1;
22595 if (nodeIsBefore && nodeIsAfter) {
22598 if (!nodeIsBefore && nodeIsAfter) {
22599 return 1; //right trailed.
22602 if (nodeIsBefore && !nodeIsAfter) {
22603 return 2; // left trailed.
22609 // private? - in a new class?
22610 cleanUpPaste : function()
22612 // cleans up the whole document..
22613 Roo.log('cleanuppaste');
22615 this.cleanUpChildren(this.doc.body);
22616 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22617 if (clean != this.doc.body.innerHTML) {
22618 this.doc.body.innerHTML = clean;
22623 cleanWordChars : function(input) {// change the chars to hex code
22624 var he = Roo.HtmlEditorCore;
22626 var output = input;
22627 Roo.each(he.swapCodes, function(sw) {
22628 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22630 output = output.replace(swapper, sw[1]);
22637 cleanUpChildren : function (n)
22639 if (!n.childNodes.length) {
22642 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22643 this.cleanUpChild(n.childNodes[i]);
22650 cleanUpChild : function (node)
22653 //console.log(node);
22654 if (node.nodeName == "#text") {
22655 // clean up silly Windows -- stuff?
22658 if (node.nodeName == "#comment") {
22659 node.parentNode.removeChild(node);
22660 // clean up silly Windows -- stuff?
22663 var lcname = node.tagName.toLowerCase();
22664 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22665 // whitelist of tags..
22667 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22669 node.parentNode.removeChild(node);
22674 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22676 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22677 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22679 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22680 // remove_keep_children = true;
22683 if (remove_keep_children) {
22684 this.cleanUpChildren(node);
22685 // inserts everything just before this node...
22686 while (node.childNodes.length) {
22687 var cn = node.childNodes[0];
22688 node.removeChild(cn);
22689 node.parentNode.insertBefore(cn, node);
22691 node.parentNode.removeChild(node);
22695 if (!node.attributes || !node.attributes.length) {
22696 this.cleanUpChildren(node);
22700 function cleanAttr(n,v)
22703 if (v.match(/^\./) || v.match(/^\//)) {
22706 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22709 if (v.match(/^#/)) {
22712 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22713 node.removeAttribute(n);
22717 var cwhite = this.cwhite;
22718 var cblack = this.cblack;
22720 function cleanStyle(n,v)
22722 if (v.match(/expression/)) { //XSS?? should we even bother..
22723 node.removeAttribute(n);
22727 var parts = v.split(/;/);
22730 Roo.each(parts, function(p) {
22731 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22735 var l = p.split(':').shift().replace(/\s+/g,'');
22736 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22738 if ( cwhite.length && cblack.indexOf(l) > -1) {
22739 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22740 //node.removeAttribute(n);
22744 // only allow 'c whitelisted system attributes'
22745 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22746 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22747 //node.removeAttribute(n);
22757 if (clean.length) {
22758 node.setAttribute(n, clean.join(';'));
22760 node.removeAttribute(n);
22766 for (var i = node.attributes.length-1; i > -1 ; i--) {
22767 var a = node.attributes[i];
22770 if (a.name.toLowerCase().substr(0,2)=='on') {
22771 node.removeAttribute(a.name);
22774 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22775 node.removeAttribute(a.name);
22778 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22779 cleanAttr(a.name,a.value); // fixme..
22782 if (a.name == 'style') {
22783 cleanStyle(a.name,a.value);
22786 /// clean up MS crap..
22787 // tecnically this should be a list of valid class'es..
22790 if (a.name == 'class') {
22791 if (a.value.match(/^Mso/)) {
22792 node.className = '';
22795 if (a.value.match(/^body$/)) {
22796 node.className = '';
22807 this.cleanUpChildren(node);
22813 * Clean up MS wordisms...
22815 cleanWord : function(node)
22820 this.cleanWord(this.doc.body);
22823 if (node.nodeName == "#text") {
22824 // clean up silly Windows -- stuff?
22827 if (node.nodeName == "#comment") {
22828 node.parentNode.removeChild(node);
22829 // clean up silly Windows -- stuff?
22833 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22834 node.parentNode.removeChild(node);
22838 // remove - but keep children..
22839 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22840 while (node.childNodes.length) {
22841 var cn = node.childNodes[0];
22842 node.removeChild(cn);
22843 node.parentNode.insertBefore(cn, node);
22845 node.parentNode.removeChild(node);
22846 this.iterateChildren(node, this.cleanWord);
22850 if (node.className.length) {
22852 var cn = node.className.split(/\W+/);
22854 Roo.each(cn, function(cls) {
22855 if (cls.match(/Mso[a-zA-Z]+/)) {
22860 node.className = cna.length ? cna.join(' ') : '';
22862 node.removeAttribute("class");
22866 if (node.hasAttribute("lang")) {
22867 node.removeAttribute("lang");
22870 if (node.hasAttribute("style")) {
22872 var styles = node.getAttribute("style").split(";");
22874 Roo.each(styles, function(s) {
22875 if (!s.match(/:/)) {
22878 var kv = s.split(":");
22879 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22882 // what ever is left... we allow.
22885 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22886 if (!nstyle.length) {
22887 node.removeAttribute('style');
22890 this.iterateChildren(node, this.cleanWord);
22896 * iterateChildren of a Node, calling fn each time, using this as the scole..
22897 * @param {DomNode} node node to iterate children of.
22898 * @param {Function} fn method of this class to call on each item.
22900 iterateChildren : function(node, fn)
22902 if (!node.childNodes.length) {
22905 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22906 fn.call(this, node.childNodes[i])
22912 * cleanTableWidths.
22914 * Quite often pasting from word etc.. results in tables with column and widths.
22915 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22918 cleanTableWidths : function(node)
22923 this.cleanTableWidths(this.doc.body);
22928 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22931 Roo.log(node.tagName);
22932 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22933 this.iterateChildren(node, this.cleanTableWidths);
22936 if (node.hasAttribute('width')) {
22937 node.removeAttribute('width');
22941 if (node.hasAttribute("style")) {
22944 var styles = node.getAttribute("style").split(";");
22946 Roo.each(styles, function(s) {
22947 if (!s.match(/:/)) {
22950 var kv = s.split(":");
22951 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22954 // what ever is left... we allow.
22957 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22958 if (!nstyle.length) {
22959 node.removeAttribute('style');
22963 this.iterateChildren(node, this.cleanTableWidths);
22971 domToHTML : function(currentElement, depth, nopadtext) {
22973 depth = depth || 0;
22974 nopadtext = nopadtext || false;
22976 if (!currentElement) {
22977 return this.domToHTML(this.doc.body);
22980 //Roo.log(currentElement);
22982 var allText = false;
22983 var nodeName = currentElement.nodeName;
22984 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22986 if (nodeName == '#text') {
22988 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22993 if (nodeName != 'BODY') {
22996 // Prints the node tagName, such as <A>, <IMG>, etc
22999 for(i = 0; i < currentElement.attributes.length;i++) {
23001 var aname = currentElement.attributes.item(i).name;
23002 if (!currentElement.attributes.item(i).value.length) {
23005 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23008 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23017 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23020 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23025 // Traverse the tree
23027 var currentElementChild = currentElement.childNodes.item(i);
23028 var allText = true;
23029 var innerHTML = '';
23031 while (currentElementChild) {
23032 // Formatting code (indent the tree so it looks nice on the screen)
23033 var nopad = nopadtext;
23034 if (lastnode == 'SPAN') {
23038 if (currentElementChild.nodeName == '#text') {
23039 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23040 toadd = nopadtext ? toadd : toadd.trim();
23041 if (!nopad && toadd.length > 80) {
23042 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23044 innerHTML += toadd;
23047 currentElementChild = currentElement.childNodes.item(i);
23053 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23055 // Recursively traverse the tree structure of the child node
23056 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23057 lastnode = currentElementChild.nodeName;
23059 currentElementChild=currentElement.childNodes.item(i);
23065 // The remaining code is mostly for formatting the tree
23066 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23071 ret+= "</"+tagName+">";
23077 applyBlacklists : function()
23079 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23080 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23084 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23085 if (b.indexOf(tag) > -1) {
23088 this.white.push(tag);
23092 Roo.each(w, function(tag) {
23093 if (b.indexOf(tag) > -1) {
23096 if (this.white.indexOf(tag) > -1) {
23099 this.white.push(tag);
23104 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23105 if (w.indexOf(tag) > -1) {
23108 this.black.push(tag);
23112 Roo.each(b, function(tag) {
23113 if (w.indexOf(tag) > -1) {
23116 if (this.black.indexOf(tag) > -1) {
23119 this.black.push(tag);
23124 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23125 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23129 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23130 if (b.indexOf(tag) > -1) {
23133 this.cwhite.push(tag);
23137 Roo.each(w, function(tag) {
23138 if (b.indexOf(tag) > -1) {
23141 if (this.cwhite.indexOf(tag) > -1) {
23144 this.cwhite.push(tag);
23149 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23150 if (w.indexOf(tag) > -1) {
23153 this.cblack.push(tag);
23157 Roo.each(b, function(tag) {
23158 if (w.indexOf(tag) > -1) {
23161 if (this.cblack.indexOf(tag) > -1) {
23164 this.cblack.push(tag);
23169 setStylesheets : function(stylesheets)
23171 if(typeof(stylesheets) == 'string'){
23172 Roo.get(this.iframe.contentDocument.head).createChild({
23174 rel : 'stylesheet',
23183 Roo.each(stylesheets, function(s) {
23188 Roo.get(_this.iframe.contentDocument.head).createChild({
23190 rel : 'stylesheet',
23199 removeStylesheets : function()
23203 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23208 setStyle : function(style)
23210 Roo.get(this.iframe.contentDocument.head).createChild({
23219 // hide stuff that is not compatible
23233 * @event specialkey
23237 * @cfg {String} fieldClass @hide
23240 * @cfg {String} focusClass @hide
23243 * @cfg {String} autoCreate @hide
23246 * @cfg {String} inputType @hide
23249 * @cfg {String} invalidClass @hide
23252 * @cfg {String} invalidText @hide
23255 * @cfg {String} msgFx @hide
23258 * @cfg {String} validateOnBlur @hide
23262 Roo.HtmlEditorCore.white = [
23263 'area', 'br', 'img', 'input', 'hr', 'wbr',
23265 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23266 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23267 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23268 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23269 'table', 'ul', 'xmp',
23271 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23274 'dir', 'menu', 'ol', 'ul', 'dl',
23280 Roo.HtmlEditorCore.black = [
23281 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23283 'base', 'basefont', 'bgsound', 'blink', 'body',
23284 'frame', 'frameset', 'head', 'html', 'ilayer',
23285 'iframe', 'layer', 'link', 'meta', 'object',
23286 'script', 'style' ,'title', 'xml' // clean later..
23288 Roo.HtmlEditorCore.clean = [
23289 'script', 'style', 'title', 'xml'
23291 Roo.HtmlEditorCore.remove = [
23296 Roo.HtmlEditorCore.ablack = [
23300 Roo.HtmlEditorCore.aclean = [
23301 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23305 Roo.HtmlEditorCore.pwhite= [
23306 'http', 'https', 'mailto'
23309 // white listed style attributes.
23310 Roo.HtmlEditorCore.cwhite= [
23311 // 'text-align', /// default is to allow most things..
23317 // black listed style attributes.
23318 Roo.HtmlEditorCore.cblack= [
23319 // 'font-size' -- this can be set by the project
23323 Roo.HtmlEditorCore.swapCodes =[
23342 * @class Roo.bootstrap.HtmlEditor
23343 * @extends Roo.bootstrap.TextArea
23344 * Bootstrap HtmlEditor class
23347 * Create a new HtmlEditor
23348 * @param {Object} config The config object
23351 Roo.bootstrap.HtmlEditor = function(config){
23352 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23353 if (!this.toolbars) {
23354 this.toolbars = [];
23357 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23360 * @event initialize
23361 * Fires when the editor is fully initialized (including the iframe)
23362 * @param {HtmlEditor} this
23367 * Fires when the editor is first receives the focus. Any insertion must wait
23368 * until after this event.
23369 * @param {HtmlEditor} this
23373 * @event beforesync
23374 * Fires before the textarea is updated with content from the editor iframe. Return false
23375 * to cancel the sync.
23376 * @param {HtmlEditor} this
23377 * @param {String} html
23381 * @event beforepush
23382 * Fires before the iframe editor is updated with content from the textarea. Return false
23383 * to cancel the push.
23384 * @param {HtmlEditor} this
23385 * @param {String} html
23390 * Fires when the textarea is updated with content from the editor iframe.
23391 * @param {HtmlEditor} this
23392 * @param {String} html
23397 * Fires when the iframe editor is updated with content from the textarea.
23398 * @param {HtmlEditor} this
23399 * @param {String} html
23403 * @event editmodechange
23404 * Fires when the editor switches edit modes
23405 * @param {HtmlEditor} this
23406 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23408 editmodechange: true,
23410 * @event editorevent
23411 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23412 * @param {HtmlEditor} this
23416 * @event firstfocus
23417 * Fires when on first focus - needed by toolbars..
23418 * @param {HtmlEditor} this
23423 * Auto save the htmlEditor value as a file into Events
23424 * @param {HtmlEditor} this
23428 * @event savedpreview
23429 * preview the saved version of htmlEditor
23430 * @param {HtmlEditor} this
23437 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23441 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23446 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23451 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23456 * @cfg {Number} height (in pixels)
23460 * @cfg {Number} width (in pixels)
23465 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23468 stylesheets: false,
23473 // private properties
23474 validationEvent : false,
23476 initialized : false,
23479 onFocus : Roo.emptyFn,
23481 hideMode:'offsets',
23483 tbContainer : false,
23487 toolbarContainer :function() {
23488 return this.wrap.select('.x-html-editor-tb',true).first();
23492 * Protected method that will not generally be called directly. It
23493 * is called when the editor creates its toolbar. Override this method if you need to
23494 * add custom toolbar buttons.
23495 * @param {HtmlEditor} editor
23497 createToolbar : function(){
23498 Roo.log('renewing');
23499 Roo.log("create toolbars");
23501 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23502 this.toolbars[0].render(this.toolbarContainer());
23506 // if (!editor.toolbars || !editor.toolbars.length) {
23507 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23510 // for (var i =0 ; i < editor.toolbars.length;i++) {
23511 // editor.toolbars[i] = Roo.factory(
23512 // typeof(editor.toolbars[i]) == 'string' ?
23513 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23514 // Roo.bootstrap.HtmlEditor);
23515 // editor.toolbars[i].init(editor);
23521 onRender : function(ct, position)
23523 // Roo.log("Call onRender: " + this.xtype);
23525 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23527 this.wrap = this.inputEl().wrap({
23528 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23531 this.editorcore.onRender(ct, position);
23533 if (this.resizable) {
23534 this.resizeEl = new Roo.Resizable(this.wrap, {
23538 minHeight : this.height,
23539 height: this.height,
23540 handles : this.resizable,
23543 resize : function(r, w, h) {
23544 _t.onResize(w,h); // -something
23550 this.createToolbar(this);
23553 if(!this.width && this.resizable){
23554 this.setSize(this.wrap.getSize());
23556 if (this.resizeEl) {
23557 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23558 // should trigger onReize..
23564 onResize : function(w, h)
23566 Roo.log('resize: ' +w + ',' + h );
23567 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23571 if(this.inputEl() ){
23572 if(typeof w == 'number'){
23573 var aw = w - this.wrap.getFrameWidth('lr');
23574 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23577 if(typeof h == 'number'){
23578 var tbh = -11; // fixme it needs to tool bar size!
23579 for (var i =0; i < this.toolbars.length;i++) {
23580 // fixme - ask toolbars for heights?
23581 tbh += this.toolbars[i].el.getHeight();
23582 //if (this.toolbars[i].footer) {
23583 // tbh += this.toolbars[i].footer.el.getHeight();
23591 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23592 ah -= 5; // knock a few pixes off for look..
23593 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23597 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23598 this.editorcore.onResize(ew,eh);
23603 * Toggles the editor between standard and source edit mode.
23604 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23606 toggleSourceEdit : function(sourceEditMode)
23608 this.editorcore.toggleSourceEdit(sourceEditMode);
23610 if(this.editorcore.sourceEditMode){
23611 Roo.log('editor - showing textarea');
23614 // Roo.log(this.syncValue());
23616 this.inputEl().removeClass(['hide', 'x-hidden']);
23617 this.inputEl().dom.removeAttribute('tabIndex');
23618 this.inputEl().focus();
23620 Roo.log('editor - hiding textarea');
23622 // Roo.log(this.pushValue());
23625 this.inputEl().addClass(['hide', 'x-hidden']);
23626 this.inputEl().dom.setAttribute('tabIndex', -1);
23627 //this.deferFocus();
23630 if(this.resizable){
23631 this.setSize(this.wrap.getSize());
23634 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23637 // private (for BoxComponent)
23638 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23640 // private (for BoxComponent)
23641 getResizeEl : function(){
23645 // private (for BoxComponent)
23646 getPositionEl : function(){
23651 initEvents : function(){
23652 this.originalValue = this.getValue();
23656 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23659 // markInvalid : Roo.emptyFn,
23661 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23664 // clearInvalid : Roo.emptyFn,
23666 setValue : function(v){
23667 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23668 this.editorcore.pushValue();
23673 deferFocus : function(){
23674 this.focus.defer(10, this);
23678 focus : function(){
23679 this.editorcore.focus();
23685 onDestroy : function(){
23691 for (var i =0; i < this.toolbars.length;i++) {
23692 // fixme - ask toolbars for heights?
23693 this.toolbars[i].onDestroy();
23696 this.wrap.dom.innerHTML = '';
23697 this.wrap.remove();
23702 onFirstFocus : function(){
23703 //Roo.log("onFirstFocus");
23704 this.editorcore.onFirstFocus();
23705 for (var i =0; i < this.toolbars.length;i++) {
23706 this.toolbars[i].onFirstFocus();
23712 syncValue : function()
23714 this.editorcore.syncValue();
23717 pushValue : function()
23719 this.editorcore.pushValue();
23723 // hide stuff that is not compatible
23737 * @event specialkey
23741 * @cfg {String} fieldClass @hide
23744 * @cfg {String} focusClass @hide
23747 * @cfg {String} autoCreate @hide
23750 * @cfg {String} inputType @hide
23753 * @cfg {String} invalidClass @hide
23756 * @cfg {String} invalidText @hide
23759 * @cfg {String} msgFx @hide
23762 * @cfg {String} validateOnBlur @hide
23771 Roo.namespace('Roo.bootstrap.htmleditor');
23773 * @class Roo.bootstrap.HtmlEditorToolbar1
23778 new Roo.bootstrap.HtmlEditor({
23781 new Roo.bootstrap.HtmlEditorToolbar1({
23782 disable : { fonts: 1 , format: 1, ..., ... , ...],
23788 * @cfg {Object} disable List of elements to disable..
23789 * @cfg {Array} btns List of additional buttons.
23793 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23796 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23799 Roo.apply(this, config);
23801 // default disabled, based on 'good practice'..
23802 this.disable = this.disable || {};
23803 Roo.applyIf(this.disable, {
23806 specialElements : true
23808 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23810 this.editor = config.editor;
23811 this.editorcore = config.editor.editorcore;
23813 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23815 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23816 // dont call parent... till later.
23818 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23823 editorcore : false,
23828 "h1","h2","h3","h4","h5","h6",
23830 "abbr", "acronym", "address", "cite", "samp", "var",
23834 onRender : function(ct, position)
23836 // Roo.log("Call onRender: " + this.xtype);
23838 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23840 this.el.dom.style.marginBottom = '0';
23842 var editorcore = this.editorcore;
23843 var editor= this.editor;
23846 var btn = function(id,cmd , toggle, handler, html){
23848 var event = toggle ? 'toggle' : 'click';
23853 xns: Roo.bootstrap,
23856 enableToggle:toggle !== false,
23858 pressed : toggle ? false : null,
23861 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23862 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23868 // var cb_box = function...
23873 xns: Roo.bootstrap,
23874 glyphicon : 'font',
23878 xns: Roo.bootstrap,
23882 Roo.each(this.formats, function(f) {
23883 style.menu.items.push({
23885 xns: Roo.bootstrap,
23886 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23891 editorcore.insertTag(this.tagname);
23898 children.push(style);
23900 btn('bold',false,true);
23901 btn('italic',false,true);
23902 btn('align-left', 'justifyleft',true);
23903 btn('align-center', 'justifycenter',true);
23904 btn('align-right' , 'justifyright',true);
23905 btn('link', false, false, function(btn) {
23906 //Roo.log("create link?");
23907 var url = prompt(this.createLinkText, this.defaultLinkValue);
23908 if(url && url != 'http:/'+'/'){
23909 this.editorcore.relayCmd('createlink', url);
23912 btn('list','insertunorderedlist',true);
23913 btn('pencil', false,true, function(btn){
23915 this.toggleSourceEdit(btn.pressed);
23918 if (this.editor.btns.length > 0) {
23919 for (var i = 0; i<this.editor.btns.length; i++) {
23920 children.push(this.editor.btns[i]);
23928 xns: Roo.bootstrap,
23933 xns: Roo.bootstrap,
23938 cog.menu.items.push({
23940 xns: Roo.bootstrap,
23941 html : Clean styles,
23946 editorcore.insertTag(this.tagname);
23955 this.xtype = 'NavSimplebar';
23957 for(var i=0;i< children.length;i++) {
23959 this.buttons.add(this.addxtypeChild(children[i]));
23963 editor.on('editorevent', this.updateToolbar, this);
23965 onBtnClick : function(id)
23967 this.editorcore.relayCmd(id);
23968 this.editorcore.focus();
23972 * Protected method that will not generally be called directly. It triggers
23973 * a toolbar update by reading the markup state of the current selection in the editor.
23975 updateToolbar: function(){
23977 if(!this.editorcore.activated){
23978 this.editor.onFirstFocus(); // is this neeed?
23982 var btns = this.buttons;
23983 var doc = this.editorcore.doc;
23984 btns.get('bold').setActive(doc.queryCommandState('bold'));
23985 btns.get('italic').setActive(doc.queryCommandState('italic'));
23986 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23988 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23989 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23990 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23992 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23993 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23996 var ans = this.editorcore.getAllAncestors();
23997 if (this.formatCombo) {
24000 var store = this.formatCombo.store;
24001 this.formatCombo.setValue("");
24002 for (var i =0; i < ans.length;i++) {
24003 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24005 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24013 // hides menus... - so this cant be on a menu...
24014 Roo.bootstrap.MenuMgr.hideAll();
24016 Roo.bootstrap.MenuMgr.hideAll();
24017 //this.editorsyncValue();
24019 onFirstFocus: function() {
24020 this.buttons.each(function(item){
24024 toggleSourceEdit : function(sourceEditMode){
24027 if(sourceEditMode){
24028 Roo.log("disabling buttons");
24029 this.buttons.each( function(item){
24030 if(item.cmd != 'pencil'){
24036 Roo.log("enabling buttons");
24037 if(this.editorcore.initialized){
24038 this.buttons.each( function(item){
24044 Roo.log("calling toggole on editor");
24045 // tell the editor that it's been pressed..
24046 this.editor.toggleSourceEdit(sourceEditMode);
24056 * @class Roo.bootstrap.Table.AbstractSelectionModel
24057 * @extends Roo.util.Observable
24058 * Abstract base class for grid SelectionModels. It provides the interface that should be
24059 * implemented by descendant classes. This class should not be directly instantiated.
24062 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24063 this.locked = false;
24064 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24068 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24069 /** @ignore Called by the grid automatically. Do not call directly. */
24070 init : function(grid){
24076 * Locks the selections.
24079 this.locked = true;
24083 * Unlocks the selections.
24085 unlock : function(){
24086 this.locked = false;
24090 * Returns true if the selections are locked.
24091 * @return {Boolean}
24093 isLocked : function(){
24094 return this.locked;
24098 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24099 * @class Roo.bootstrap.Table.RowSelectionModel
24100 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24101 * It supports multiple selections and keyboard selection/navigation.
24103 * @param {Object} config
24106 Roo.bootstrap.Table.RowSelectionModel = function(config){
24107 Roo.apply(this, config);
24108 this.selections = new Roo.util.MixedCollection(false, function(o){
24113 this.lastActive = false;
24117 * @event selectionchange
24118 * Fires when the selection changes
24119 * @param {SelectionModel} this
24121 "selectionchange" : true,
24123 * @event afterselectionchange
24124 * Fires after the selection changes (eg. by key press or clicking)
24125 * @param {SelectionModel} this
24127 "afterselectionchange" : true,
24129 * @event beforerowselect
24130 * Fires when a row is selected being selected, return false to cancel.
24131 * @param {SelectionModel} this
24132 * @param {Number} rowIndex The selected index
24133 * @param {Boolean} keepExisting False if other selections will be cleared
24135 "beforerowselect" : true,
24138 * Fires when a row is selected.
24139 * @param {SelectionModel} this
24140 * @param {Number} rowIndex The selected index
24141 * @param {Roo.data.Record} r The record
24143 "rowselect" : true,
24145 * @event rowdeselect
24146 * Fires when a row is deselected.
24147 * @param {SelectionModel} this
24148 * @param {Number} rowIndex The selected index
24150 "rowdeselect" : true
24152 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24153 this.locked = false;
24156 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24158 * @cfg {Boolean} singleSelect
24159 * True to allow selection of only one row at a time (defaults to false)
24161 singleSelect : false,
24164 initEvents : function()
24167 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24168 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24169 //}else{ // allow click to work like normal
24170 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24172 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24173 this.grid.on("rowclick", this.handleMouseDown, this);
24175 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24176 "up" : function(e){
24178 this.selectPrevious(e.shiftKey);
24179 }else if(this.last !== false && this.lastActive !== false){
24180 var last = this.last;
24181 this.selectRange(this.last, this.lastActive-1);
24182 this.grid.getView().focusRow(this.lastActive);
24183 if(last !== false){
24187 this.selectFirstRow();
24189 this.fireEvent("afterselectionchange", this);
24191 "down" : function(e){
24193 this.selectNext(e.shiftKey);
24194 }else if(this.last !== false && this.lastActive !== false){
24195 var last = this.last;
24196 this.selectRange(this.last, this.lastActive+1);
24197 this.grid.getView().focusRow(this.lastActive);
24198 if(last !== false){
24202 this.selectFirstRow();
24204 this.fireEvent("afterselectionchange", this);
24208 this.grid.store.on('load', function(){
24209 this.selections.clear();
24212 var view = this.grid.view;
24213 view.on("refresh", this.onRefresh, this);
24214 view.on("rowupdated", this.onRowUpdated, this);
24215 view.on("rowremoved", this.onRemove, this);
24220 onRefresh : function()
24222 var ds = this.grid.store, i, v = this.grid.view;
24223 var s = this.selections;
24224 s.each(function(r){
24225 if((i = ds.indexOfId(r.id)) != -1){
24234 onRemove : function(v, index, r){
24235 this.selections.remove(r);
24239 onRowUpdated : function(v, index, r){
24240 if(this.isSelected(r)){
24241 v.onRowSelect(index);
24247 * @param {Array} records The records to select
24248 * @param {Boolean} keepExisting (optional) True to keep existing selections
24250 selectRecords : function(records, keepExisting)
24253 this.clearSelections();
24255 var ds = this.grid.store;
24256 for(var i = 0, len = records.length; i < len; i++){
24257 this.selectRow(ds.indexOf(records[i]), true);
24262 * Gets the number of selected rows.
24265 getCount : function(){
24266 return this.selections.length;
24270 * Selects the first row in the grid.
24272 selectFirstRow : function(){
24277 * Select the last row.
24278 * @param {Boolean} keepExisting (optional) True to keep existing selections
24280 selectLastRow : function(keepExisting){
24281 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24282 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24286 * Selects the row immediately following the last selected row.
24287 * @param {Boolean} keepExisting (optional) True to keep existing selections
24289 selectNext : function(keepExisting)
24291 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24292 this.selectRow(this.last+1, keepExisting);
24293 this.grid.getView().focusRow(this.last);
24298 * Selects the row that precedes the last selected row.
24299 * @param {Boolean} keepExisting (optional) True to keep existing selections
24301 selectPrevious : function(keepExisting){
24303 this.selectRow(this.last-1, keepExisting);
24304 this.grid.getView().focusRow(this.last);
24309 * Returns the selected records
24310 * @return {Array} Array of selected records
24312 getSelections : function(){
24313 return [].concat(this.selections.items);
24317 * Returns the first selected record.
24320 getSelected : function(){
24321 return this.selections.itemAt(0);
24326 * Clears all selections.
24328 clearSelections : function(fast)
24334 var ds = this.grid.store;
24335 var s = this.selections;
24336 s.each(function(r){
24337 this.deselectRow(ds.indexOfId(r.id));
24341 this.selections.clear();
24348 * Selects all rows.
24350 selectAll : function(){
24354 this.selections.clear();
24355 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24356 this.selectRow(i, true);
24361 * Returns True if there is a selection.
24362 * @return {Boolean}
24364 hasSelection : function(){
24365 return this.selections.length > 0;
24369 * Returns True if the specified row is selected.
24370 * @param {Number/Record} record The record or index of the record to check
24371 * @return {Boolean}
24373 isSelected : function(index){
24374 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24375 return (r && this.selections.key(r.id) ? true : false);
24379 * Returns True if the specified record id is selected.
24380 * @param {String} id The id of record to check
24381 * @return {Boolean}
24383 isIdSelected : function(id){
24384 return (this.selections.key(id) ? true : false);
24389 handleMouseDBClick : function(e, t){
24393 handleMouseDown : function(e, t)
24395 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24396 if(this.isLocked() || rowIndex < 0 ){
24399 if(e.shiftKey && this.last !== false){
24400 var last = this.last;
24401 this.selectRange(last, rowIndex, e.ctrlKey);
24402 this.last = last; // reset the last
24406 var isSelected = this.isSelected(rowIndex);
24407 //Roo.log("select row:" + rowIndex);
24409 this.deselectRow(rowIndex);
24411 this.selectRow(rowIndex, true);
24415 if(e.button !== 0 && isSelected){
24416 alert('rowIndex 2: ' + rowIndex);
24417 view.focusRow(rowIndex);
24418 }else if(e.ctrlKey && isSelected){
24419 this.deselectRow(rowIndex);
24420 }else if(!isSelected){
24421 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24422 view.focusRow(rowIndex);
24426 this.fireEvent("afterselectionchange", this);
24429 handleDragableRowClick : function(grid, rowIndex, e)
24431 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24432 this.selectRow(rowIndex, false);
24433 grid.view.focusRow(rowIndex);
24434 this.fireEvent("afterselectionchange", this);
24439 * Selects multiple rows.
24440 * @param {Array} rows Array of the indexes of the row to select
24441 * @param {Boolean} keepExisting (optional) True to keep existing selections
24443 selectRows : function(rows, keepExisting){
24445 this.clearSelections();
24447 for(var i = 0, len = rows.length; i < len; i++){
24448 this.selectRow(rows[i], true);
24453 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24454 * @param {Number} startRow The index of the first row in the range
24455 * @param {Number} endRow The index of the last row in the range
24456 * @param {Boolean} keepExisting (optional) True to retain existing selections
24458 selectRange : function(startRow, endRow, keepExisting){
24463 this.clearSelections();
24465 if(startRow <= endRow){
24466 for(var i = startRow; i <= endRow; i++){
24467 this.selectRow(i, true);
24470 for(var i = startRow; i >= endRow; i--){
24471 this.selectRow(i, true);
24477 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24478 * @param {Number} startRow The index of the first row in the range
24479 * @param {Number} endRow The index of the last row in the range
24481 deselectRange : function(startRow, endRow, preventViewNotify){
24485 for(var i = startRow; i <= endRow; i++){
24486 this.deselectRow(i, preventViewNotify);
24492 * @param {Number} row The index of the row to select
24493 * @param {Boolean} keepExisting (optional) True to keep existing selections
24495 selectRow : function(index, keepExisting, preventViewNotify)
24497 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24500 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24501 if(!keepExisting || this.singleSelect){
24502 this.clearSelections();
24505 var r = this.grid.store.getAt(index);
24506 //console.log('selectRow - record id :' + r.id);
24508 this.selections.add(r);
24509 this.last = this.lastActive = index;
24510 if(!preventViewNotify){
24511 var proxy = new Roo.Element(
24512 this.grid.getRowDom(index)
24514 proxy.addClass('bg-info info');
24516 this.fireEvent("rowselect", this, index, r);
24517 this.fireEvent("selectionchange", this);
24523 * @param {Number} row The index of the row to deselect
24525 deselectRow : function(index, preventViewNotify)
24530 if(this.last == index){
24533 if(this.lastActive == index){
24534 this.lastActive = false;
24537 var r = this.grid.store.getAt(index);
24542 this.selections.remove(r);
24543 //.console.log('deselectRow - record id :' + r.id);
24544 if(!preventViewNotify){
24546 var proxy = new Roo.Element(
24547 this.grid.getRowDom(index)
24549 proxy.removeClass('bg-info info');
24551 this.fireEvent("rowdeselect", this, index);
24552 this.fireEvent("selectionchange", this);
24556 restoreLast : function(){
24558 this.last = this._last;
24563 acceptsNav : function(row, col, cm){
24564 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24568 onEditorKey : function(field, e){
24569 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24574 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24576 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24578 }else if(k == e.ENTER && !e.ctrlKey){
24582 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24584 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24586 }else if(k == e.ESC){
24590 g.startEditing(newCell[0], newCell[1]);
24596 * Ext JS Library 1.1.1
24597 * Copyright(c) 2006-2007, Ext JS, LLC.
24599 * Originally Released Under LGPL - original licence link has changed is not relivant.
24602 * <script type="text/javascript">
24606 * @class Roo.bootstrap.PagingToolbar
24607 * @extends Roo.bootstrap.NavSimplebar
24608 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24610 * Create a new PagingToolbar
24611 * @param {Object} config The config object
24612 * @param {Roo.data.Store} store
24614 Roo.bootstrap.PagingToolbar = function(config)
24616 // old args format still supported... - xtype is prefered..
24617 // created from xtype...
24619 this.ds = config.dataSource;
24621 if (config.store && !this.ds) {
24622 this.store= Roo.factory(config.store, Roo.data);
24623 this.ds = this.store;
24624 this.ds.xmodule = this.xmodule || false;
24627 this.toolbarItems = [];
24628 if (config.items) {
24629 this.toolbarItems = config.items;
24632 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24637 this.bind(this.ds);
24640 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24644 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24646 * @cfg {Roo.data.Store} dataSource
24647 * The underlying data store providing the paged data
24650 * @cfg {String/HTMLElement/Element} container
24651 * container The id or element that will contain the toolbar
24654 * @cfg {Boolean} displayInfo
24655 * True to display the displayMsg (defaults to false)
24658 * @cfg {Number} pageSize
24659 * The number of records to display per page (defaults to 20)
24663 * @cfg {String} displayMsg
24664 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24666 displayMsg : 'Displaying {0} - {1} of {2}',
24668 * @cfg {String} emptyMsg
24669 * The message to display when no records are found (defaults to "No data to display")
24671 emptyMsg : 'No data to display',
24673 * Customizable piece of the default paging text (defaults to "Page")
24676 beforePageText : "Page",
24678 * Customizable piece of the default paging text (defaults to "of %0")
24681 afterPageText : "of {0}",
24683 * Customizable piece of the default paging text (defaults to "First Page")
24686 firstText : "First Page",
24688 * Customizable piece of the default paging text (defaults to "Previous Page")
24691 prevText : "Previous Page",
24693 * Customizable piece of the default paging text (defaults to "Next Page")
24696 nextText : "Next Page",
24698 * Customizable piece of the default paging text (defaults to "Last Page")
24701 lastText : "Last Page",
24703 * Customizable piece of the default paging text (defaults to "Refresh")
24706 refreshText : "Refresh",
24710 onRender : function(ct, position)
24712 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24713 this.navgroup.parentId = this.id;
24714 this.navgroup.onRender(this.el, null);
24715 // add the buttons to the navgroup
24717 if(this.displayInfo){
24718 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24719 this.displayEl = this.el.select('.x-paging-info', true).first();
24720 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24721 // this.displayEl = navel.el.select('span',true).first();
24727 Roo.each(_this.buttons, function(e){ // this might need to use render????
24728 Roo.factory(e).render(_this.el);
24732 Roo.each(_this.toolbarItems, function(e) {
24733 _this.navgroup.addItem(e);
24737 this.first = this.navgroup.addItem({
24738 tooltip: this.firstText,
24740 icon : 'fa fa-backward',
24742 preventDefault: true,
24743 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24746 this.prev = this.navgroup.addItem({
24747 tooltip: this.prevText,
24749 icon : 'fa fa-step-backward',
24751 preventDefault: true,
24752 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24754 //this.addSeparator();
24757 var field = this.navgroup.addItem( {
24759 cls : 'x-paging-position',
24761 html : this.beforePageText +
24762 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24763 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24766 this.field = field.el.select('input', true).first();
24767 this.field.on("keydown", this.onPagingKeydown, this);
24768 this.field.on("focus", function(){this.dom.select();});
24771 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24772 //this.field.setHeight(18);
24773 //this.addSeparator();
24774 this.next = this.navgroup.addItem({
24775 tooltip: this.nextText,
24777 html : ' <i class="fa fa-step-forward">',
24779 preventDefault: true,
24780 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24782 this.last = this.navgroup.addItem({
24783 tooltip: this.lastText,
24784 icon : 'fa fa-forward',
24787 preventDefault: true,
24788 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24790 //this.addSeparator();
24791 this.loading = this.navgroup.addItem({
24792 tooltip: this.refreshText,
24793 icon: 'fa fa-refresh',
24794 preventDefault: true,
24795 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24801 updateInfo : function(){
24802 if(this.displayEl){
24803 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24804 var msg = count == 0 ?
24808 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24810 this.displayEl.update(msg);
24815 onLoad : function(ds, r, o)
24817 this.cursor = o.params.start ? o.params.start : 0;
24819 var d = this.getPageData(),
24824 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24825 this.field.dom.value = ap;
24826 this.first.setDisabled(ap == 1);
24827 this.prev.setDisabled(ap == 1);
24828 this.next.setDisabled(ap == ps);
24829 this.last.setDisabled(ap == ps);
24830 this.loading.enable();
24835 getPageData : function(){
24836 var total = this.ds.getTotalCount();
24839 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24840 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24845 onLoadError : function(){
24846 this.loading.enable();
24850 onPagingKeydown : function(e){
24851 var k = e.getKey();
24852 var d = this.getPageData();
24854 var v = this.field.dom.value, pageNum;
24855 if(!v || isNaN(pageNum = parseInt(v, 10))){
24856 this.field.dom.value = d.activePage;
24859 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24860 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24863 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))
24865 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24866 this.field.dom.value = pageNum;
24867 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24870 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24872 var v = this.field.dom.value, pageNum;
24873 var increment = (e.shiftKey) ? 10 : 1;
24874 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24877 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24878 this.field.dom.value = d.activePage;
24881 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24883 this.field.dom.value = parseInt(v, 10) + increment;
24884 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24885 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24892 beforeLoad : function(){
24894 this.loading.disable();
24899 onClick : function(which){
24908 ds.load({params:{start: 0, limit: this.pageSize}});
24911 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24914 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24917 var total = ds.getTotalCount();
24918 var extra = total % this.pageSize;
24919 var lastStart = extra ? (total - extra) : total-this.pageSize;
24920 ds.load({params:{start: lastStart, limit: this.pageSize}});
24923 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24929 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24930 * @param {Roo.data.Store} store The data store to unbind
24932 unbind : function(ds){
24933 ds.un("beforeload", this.beforeLoad, this);
24934 ds.un("load", this.onLoad, this);
24935 ds.un("loadexception", this.onLoadError, this);
24936 ds.un("remove", this.updateInfo, this);
24937 ds.un("add", this.updateInfo, this);
24938 this.ds = undefined;
24942 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24943 * @param {Roo.data.Store} store The data store to bind
24945 bind : function(ds){
24946 ds.on("beforeload", this.beforeLoad, this);
24947 ds.on("load", this.onLoad, this);
24948 ds.on("loadexception", this.onLoadError, this);
24949 ds.on("remove", this.updateInfo, this);
24950 ds.on("add", this.updateInfo, this);
24961 * @class Roo.bootstrap.MessageBar
24962 * @extends Roo.bootstrap.Component
24963 * Bootstrap MessageBar class
24964 * @cfg {String} html contents of the MessageBar
24965 * @cfg {String} weight (info | success | warning | danger) default info
24966 * @cfg {String} beforeClass insert the bar before the given class
24967 * @cfg {Boolean} closable (true | false) default false
24968 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24971 * Create a new Element
24972 * @param {Object} config The config object
24975 Roo.bootstrap.MessageBar = function(config){
24976 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24979 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24985 beforeClass: 'bootstrap-sticky-wrap',
24987 getAutoCreate : function(){
24991 cls: 'alert alert-dismissable alert-' + this.weight,
24996 html: this.html || ''
25002 cfg.cls += ' alert-messages-fixed';
25016 onRender : function(ct, position)
25018 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25021 var cfg = Roo.apply({}, this.getAutoCreate());
25025 cfg.cls += ' ' + this.cls;
25028 cfg.style = this.style;
25030 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25032 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25035 this.el.select('>button.close').on('click', this.hide, this);
25041 if (!this.rendered) {
25047 this.fireEvent('show', this);
25053 if (!this.rendered) {
25059 this.fireEvent('hide', this);
25062 update : function()
25064 // var e = this.el.dom.firstChild;
25066 // if(this.closable){
25067 // e = e.nextSibling;
25070 // e.data = this.html || '';
25072 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25088 * @class Roo.bootstrap.Graph
25089 * @extends Roo.bootstrap.Component
25090 * Bootstrap Graph class
25094 @cfg {String} graphtype bar | vbar | pie
25095 @cfg {number} g_x coodinator | centre x (pie)
25096 @cfg {number} g_y coodinator | centre y (pie)
25097 @cfg {number} g_r radius (pie)
25098 @cfg {number} g_height height of the chart (respected by all elements in the set)
25099 @cfg {number} g_width width of the chart (respected by all elements in the set)
25100 @cfg {Object} title The title of the chart
25103 -opts (object) options for the chart
25105 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25106 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25108 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.
25109 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25111 o stretch (boolean)
25113 -opts (object) options for the pie
25116 o startAngle (number)
25117 o endAngle (number)
25121 * Create a new Input
25122 * @param {Object} config The config object
25125 Roo.bootstrap.Graph = function(config){
25126 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25132 * The img click event for the img.
25133 * @param {Roo.EventObject} e
25139 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25150 //g_colors: this.colors,
25157 getAutoCreate : function(){
25168 onRender : function(ct,position){
25171 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25173 if (typeof(Raphael) == 'undefined') {
25174 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25178 this.raphael = Raphael(this.el.dom);
25180 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25181 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25182 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25183 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25185 r.text(160, 10, "Single Series Chart").attr(txtattr);
25186 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25187 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25188 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25190 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25191 r.barchart(330, 10, 300, 220, data1);
25192 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25193 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25196 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25197 // r.barchart(30, 30, 560, 250, xdata, {
25198 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25199 // axis : "0 0 1 1",
25200 // axisxlabels : xdata
25201 // //yvalues : cols,
25204 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25206 // this.load(null,xdata,{
25207 // axis : "0 0 1 1",
25208 // axisxlabels : xdata
25213 load : function(graphtype,xdata,opts)
25215 this.raphael.clear();
25217 graphtype = this.graphtype;
25222 var r = this.raphael,
25223 fin = function () {
25224 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25226 fout = function () {
25227 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25229 pfin = function() {
25230 this.sector.stop();
25231 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25234 this.label[0].stop();
25235 this.label[0].attr({ r: 7.5 });
25236 this.label[1].attr({ "font-weight": 800 });
25239 pfout = function() {
25240 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25243 this.label[0].animate({ r: 5 }, 500, "bounce");
25244 this.label[1].attr({ "font-weight": 400 });
25250 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25253 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25256 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25257 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25259 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25266 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25271 setTitle: function(o)
25276 initEvents: function() {
25279 this.el.on('click', this.onClick, this);
25283 onClick : function(e)
25285 Roo.log('img onclick');
25286 this.fireEvent('click', this, e);
25298 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25301 * @class Roo.bootstrap.dash.NumberBox
25302 * @extends Roo.bootstrap.Component
25303 * Bootstrap NumberBox class
25304 * @cfg {String} headline Box headline
25305 * @cfg {String} content Box content
25306 * @cfg {String} icon Box icon
25307 * @cfg {String} footer Footer text
25308 * @cfg {String} fhref Footer href
25311 * Create a new NumberBox
25312 * @param {Object} config The config object
25316 Roo.bootstrap.dash.NumberBox = function(config){
25317 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25321 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25330 getAutoCreate : function(){
25334 cls : 'small-box ',
25342 cls : 'roo-headline',
25343 html : this.headline
25347 cls : 'roo-content',
25348 html : this.content
25362 cls : 'ion ' + this.icon
25371 cls : 'small-box-footer',
25372 href : this.fhref || '#',
25376 cfg.cn.push(footer);
25383 onRender : function(ct,position){
25384 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25391 setHeadline: function (value)
25393 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25396 setFooter: function (value, href)
25398 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25401 this.el.select('a.small-box-footer',true).first().attr('href', href);
25406 setContent: function (value)
25408 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25411 initEvents: function()
25425 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25428 * @class Roo.bootstrap.dash.TabBox
25429 * @extends Roo.bootstrap.Component
25430 * Bootstrap TabBox class
25431 * @cfg {String} title Title of the TabBox
25432 * @cfg {String} icon Icon of the TabBox
25433 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25434 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25437 * Create a new TabBox
25438 * @param {Object} config The config object
25442 Roo.bootstrap.dash.TabBox = function(config){
25443 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25448 * When a pane is added
25449 * @param {Roo.bootstrap.dash.TabPane} pane
25453 * @event activatepane
25454 * When a pane is activated
25455 * @param {Roo.bootstrap.dash.TabPane} pane
25457 "activatepane" : true
25465 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25470 tabScrollable : false,
25472 getChildContainer : function()
25474 return this.el.select('.tab-content', true).first();
25477 getAutoCreate : function(){
25481 cls: 'pull-left header',
25489 cls: 'fa ' + this.icon
25495 cls: 'nav nav-tabs pull-right',
25501 if(this.tabScrollable){
25508 cls: 'nav nav-tabs pull-right',
25519 cls: 'nav-tabs-custom',
25524 cls: 'tab-content no-padding',
25532 initEvents : function()
25534 //Roo.log('add add pane handler');
25535 this.on('addpane', this.onAddPane, this);
25538 * Updates the box title
25539 * @param {String} html to set the title to.
25541 setTitle : function(value)
25543 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25545 onAddPane : function(pane)
25547 this.panes.push(pane);
25548 //Roo.log('addpane');
25550 // tabs are rendere left to right..
25551 if(!this.showtabs){
25555 var ctr = this.el.select('.nav-tabs', true).first();
25558 var existing = ctr.select('.nav-tab',true);
25559 var qty = existing.getCount();;
25562 var tab = ctr.createChild({
25564 cls : 'nav-tab' + (qty ? '' : ' active'),
25572 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25575 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25577 pane.el.addClass('active');
25582 onTabClick : function(ev,un,ob,pane)
25584 //Roo.log('tab - prev default');
25585 ev.preventDefault();
25588 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25589 pane.tab.addClass('active');
25590 //Roo.log(pane.title);
25591 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25592 // technically we should have a deactivate event.. but maybe add later.
25593 // and it should not de-activate the selected tab...
25594 this.fireEvent('activatepane', pane);
25595 pane.el.addClass('active');
25596 pane.fireEvent('activate');
25601 getActivePane : function()
25604 Roo.each(this.panes, function(p) {
25605 if(p.el.hasClass('active')){
25626 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25628 * @class Roo.bootstrap.TabPane
25629 * @extends Roo.bootstrap.Component
25630 * Bootstrap TabPane class
25631 * @cfg {Boolean} active (false | true) Default false
25632 * @cfg {String} title title of panel
25636 * Create a new TabPane
25637 * @param {Object} config The config object
25640 Roo.bootstrap.dash.TabPane = function(config){
25641 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25647 * When a pane is activated
25648 * @param {Roo.bootstrap.dash.TabPane} pane
25655 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25660 // the tabBox that this is attached to.
25663 getAutoCreate : function()
25671 cfg.cls += ' active';
25676 initEvents : function()
25678 //Roo.log('trigger add pane handler');
25679 this.parent().fireEvent('addpane', this)
25683 * Updates the tab title
25684 * @param {String} html to set the title to.
25686 setTitle: function(str)
25692 this.tab.select('a', true).first().dom.innerHTML = str;
25709 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25712 * @class Roo.bootstrap.menu.Menu
25713 * @extends Roo.bootstrap.Component
25714 * Bootstrap Menu class - container for Menu
25715 * @cfg {String} html Text of the menu
25716 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25717 * @cfg {String} icon Font awesome icon
25718 * @cfg {String} pos Menu align to (top | bottom) default bottom
25722 * Create a new Menu
25723 * @param {Object} config The config object
25727 Roo.bootstrap.menu.Menu = function(config){
25728 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25732 * @event beforeshow
25733 * Fires before this menu is displayed
25734 * @param {Roo.bootstrap.menu.Menu} this
25738 * @event beforehide
25739 * Fires before this menu is hidden
25740 * @param {Roo.bootstrap.menu.Menu} this
25745 * Fires after this menu is displayed
25746 * @param {Roo.bootstrap.menu.Menu} this
25751 * Fires after this menu is hidden
25752 * @param {Roo.bootstrap.menu.Menu} this
25757 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25758 * @param {Roo.bootstrap.menu.Menu} this
25759 * @param {Roo.EventObject} e
25766 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25770 weight : 'default',
25775 getChildContainer : function() {
25776 if(this.isSubMenu){
25780 return this.el.select('ul.dropdown-menu', true).first();
25783 getAutoCreate : function()
25788 cls : 'roo-menu-text',
25796 cls : 'fa ' + this.icon
25807 cls : 'dropdown-button btn btn-' + this.weight,
25812 cls : 'dropdown-toggle btn btn-' + this.weight,
25822 cls : 'dropdown-menu'
25828 if(this.pos == 'top'){
25829 cfg.cls += ' dropup';
25832 if(this.isSubMenu){
25835 cls : 'dropdown-menu'
25842 onRender : function(ct, position)
25844 this.isSubMenu = ct.hasClass('dropdown-submenu');
25846 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25849 initEvents : function()
25851 if(this.isSubMenu){
25855 this.hidden = true;
25857 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25858 this.triggerEl.on('click', this.onTriggerPress, this);
25860 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25861 this.buttonEl.on('click', this.onClick, this);
25867 if(this.isSubMenu){
25871 return this.el.select('ul.dropdown-menu', true).first();
25874 onClick : function(e)
25876 this.fireEvent("click", this, e);
25879 onTriggerPress : function(e)
25881 if (this.isVisible()) {
25888 isVisible : function(){
25889 return !this.hidden;
25894 this.fireEvent("beforeshow", this);
25896 this.hidden = false;
25897 this.el.addClass('open');
25899 Roo.get(document).on("mouseup", this.onMouseUp, this);
25901 this.fireEvent("show", this);
25908 this.fireEvent("beforehide", this);
25910 this.hidden = true;
25911 this.el.removeClass('open');
25913 Roo.get(document).un("mouseup", this.onMouseUp);
25915 this.fireEvent("hide", this);
25918 onMouseUp : function()
25932 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25935 * @class Roo.bootstrap.menu.Item
25936 * @extends Roo.bootstrap.Component
25937 * Bootstrap MenuItem class
25938 * @cfg {Boolean} submenu (true | false) default false
25939 * @cfg {String} html text of the item
25940 * @cfg {String} href the link
25941 * @cfg {Boolean} disable (true | false) default false
25942 * @cfg {Boolean} preventDefault (true | false) default true
25943 * @cfg {String} icon Font awesome icon
25944 * @cfg {String} pos Submenu align to (left | right) default right
25948 * Create a new Item
25949 * @param {Object} config The config object
25953 Roo.bootstrap.menu.Item = function(config){
25954 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25958 * Fires when the mouse is hovering over this menu
25959 * @param {Roo.bootstrap.menu.Item} this
25960 * @param {Roo.EventObject} e
25965 * Fires when the mouse exits this menu
25966 * @param {Roo.bootstrap.menu.Item} this
25967 * @param {Roo.EventObject} e
25973 * The raw click event for the entire grid.
25974 * @param {Roo.EventObject} e
25980 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25985 preventDefault: true,
25990 getAutoCreate : function()
25995 cls : 'roo-menu-item-text',
26003 cls : 'fa ' + this.icon
26012 href : this.href || '#',
26019 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26023 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26025 if(this.pos == 'left'){
26026 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26033 initEvents : function()
26035 this.el.on('mouseover', this.onMouseOver, this);
26036 this.el.on('mouseout', this.onMouseOut, this);
26038 this.el.select('a', true).first().on('click', this.onClick, this);
26042 onClick : function(e)
26044 if(this.preventDefault){
26045 e.preventDefault();
26048 this.fireEvent("click", this, e);
26051 onMouseOver : function(e)
26053 if(this.submenu && this.pos == 'left'){
26054 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26057 this.fireEvent("mouseover", this, e);
26060 onMouseOut : function(e)
26062 this.fireEvent("mouseout", this, e);
26074 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26077 * @class Roo.bootstrap.menu.Separator
26078 * @extends Roo.bootstrap.Component
26079 * Bootstrap Separator class
26082 * Create a new Separator
26083 * @param {Object} config The config object
26087 Roo.bootstrap.menu.Separator = function(config){
26088 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26091 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26093 getAutoCreate : function(){
26114 * @class Roo.bootstrap.Tooltip
26115 * Bootstrap Tooltip class
26116 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26117 * to determine which dom element triggers the tooltip.
26119 * It needs to add support for additional attributes like tooltip-position
26122 * Create a new Toolti
26123 * @param {Object} config The config object
26126 Roo.bootstrap.Tooltip = function(config){
26127 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26129 this.alignment = Roo.bootstrap.Tooltip.alignment;
26131 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26132 this.alignment = config.alignment;
26137 Roo.apply(Roo.bootstrap.Tooltip, {
26139 * @function init initialize tooltip monitoring.
26143 currentTip : false,
26144 currentRegion : false,
26150 Roo.get(document).on('mouseover', this.enter ,this);
26151 Roo.get(document).on('mouseout', this.leave, this);
26154 this.currentTip = new Roo.bootstrap.Tooltip();
26157 enter : function(ev)
26159 var dom = ev.getTarget();
26161 //Roo.log(['enter',dom]);
26162 var el = Roo.fly(dom);
26163 if (this.currentEl) {
26165 //Roo.log(this.currentEl);
26166 //Roo.log(this.currentEl.contains(dom));
26167 if (this.currentEl == el) {
26170 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26176 if (this.currentTip.el) {
26177 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26181 if(!el || el.dom == document){
26187 // you can not look for children, as if el is the body.. then everythign is the child..
26188 if (!el.attr('tooltip')) { //
26189 if (!el.select("[tooltip]").elements.length) {
26192 // is the mouse over this child...?
26193 bindEl = el.select("[tooltip]").first();
26194 var xy = ev.getXY();
26195 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26196 //Roo.log("not in region.");
26199 //Roo.log("child element over..");
26202 this.currentEl = bindEl;
26203 this.currentTip.bind(bindEl);
26204 this.currentRegion = Roo.lib.Region.getRegion(dom);
26205 this.currentTip.enter();
26208 leave : function(ev)
26210 var dom = ev.getTarget();
26211 //Roo.log(['leave',dom]);
26212 if (!this.currentEl) {
26217 if (dom != this.currentEl.dom) {
26220 var xy = ev.getXY();
26221 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26224 // only activate leave if mouse cursor is outside... bounding box..
26229 if (this.currentTip) {
26230 this.currentTip.leave();
26232 //Roo.log('clear currentEl');
26233 this.currentEl = false;
26238 'left' : ['r-l', [-2,0], 'right'],
26239 'right' : ['l-r', [2,0], 'left'],
26240 'bottom' : ['t-b', [0,2], 'top'],
26241 'top' : [ 'b-t', [0,-2], 'bottom']
26247 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26252 delay : null, // can be { show : 300 , hide: 500}
26256 hoverState : null, //???
26258 placement : 'bottom',
26262 getAutoCreate : function(){
26269 cls : 'tooltip-arrow'
26272 cls : 'tooltip-inner'
26279 bind : function(el)
26285 enter : function () {
26287 if (this.timeout != null) {
26288 clearTimeout(this.timeout);
26291 this.hoverState = 'in';
26292 //Roo.log("enter - show");
26293 if (!this.delay || !this.delay.show) {
26298 this.timeout = setTimeout(function () {
26299 if (_t.hoverState == 'in') {
26302 }, this.delay.show);
26306 clearTimeout(this.timeout);
26308 this.hoverState = 'out';
26309 if (!this.delay || !this.delay.hide) {
26315 this.timeout = setTimeout(function () {
26316 //Roo.log("leave - timeout");
26318 if (_t.hoverState == 'out') {
26320 Roo.bootstrap.Tooltip.currentEl = false;
26325 show : function (msg)
26328 this.render(document.body);
26331 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26333 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26335 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26337 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26339 var placement = typeof this.placement == 'function' ?
26340 this.placement.call(this, this.el, on_el) :
26343 var autoToken = /\s?auto?\s?/i;
26344 var autoPlace = autoToken.test(placement);
26346 placement = placement.replace(autoToken, '') || 'top';
26350 //this.el.setXY([0,0]);
26352 //this.el.dom.style.display='block';
26354 //this.el.appendTo(on_el);
26356 var p = this.getPosition();
26357 var box = this.el.getBox();
26363 var align = this.alignment[placement];
26365 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26367 if(placement == 'top' || placement == 'bottom'){
26369 placement = 'right';
26372 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26373 placement = 'left';
26376 var scroll = Roo.select('body', true).first().getScroll();
26378 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26382 align = this.alignment[placement];
26385 this.el.alignTo(this.bindEl, align[0],align[1]);
26386 //var arrow = this.el.select('.arrow',true).first();
26387 //arrow.set(align[2],
26389 this.el.addClass(placement);
26391 this.el.addClass('in fade');
26393 this.hoverState = null;
26395 if (this.el.hasClass('fade')) {
26406 //this.el.setXY([0,0]);
26407 this.el.removeClass('in');
26423 * @class Roo.bootstrap.LocationPicker
26424 * @extends Roo.bootstrap.Component
26425 * Bootstrap LocationPicker class
26426 * @cfg {Number} latitude Position when init default 0
26427 * @cfg {Number} longitude Position when init default 0
26428 * @cfg {Number} zoom default 15
26429 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26430 * @cfg {Boolean} mapTypeControl default false
26431 * @cfg {Boolean} disableDoubleClickZoom default false
26432 * @cfg {Boolean} scrollwheel default true
26433 * @cfg {Boolean} streetViewControl default false
26434 * @cfg {Number} radius default 0
26435 * @cfg {String} locationName
26436 * @cfg {Boolean} draggable default true
26437 * @cfg {Boolean} enableAutocomplete default false
26438 * @cfg {Boolean} enableReverseGeocode default true
26439 * @cfg {String} markerTitle
26442 * Create a new LocationPicker
26443 * @param {Object} config The config object
26447 Roo.bootstrap.LocationPicker = function(config){
26449 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26454 * Fires when the picker initialized.
26455 * @param {Roo.bootstrap.LocationPicker} this
26456 * @param {Google Location} location
26460 * @event positionchanged
26461 * Fires when the picker position changed.
26462 * @param {Roo.bootstrap.LocationPicker} this
26463 * @param {Google Location} location
26465 positionchanged : true,
26468 * Fires when the map resize.
26469 * @param {Roo.bootstrap.LocationPicker} this
26474 * Fires when the map show.
26475 * @param {Roo.bootstrap.LocationPicker} this
26480 * Fires when the map hide.
26481 * @param {Roo.bootstrap.LocationPicker} this
26486 * Fires when click the map.
26487 * @param {Roo.bootstrap.LocationPicker} this
26488 * @param {Map event} e
26492 * @event mapRightClick
26493 * Fires when right click the map.
26494 * @param {Roo.bootstrap.LocationPicker} this
26495 * @param {Map event} e
26497 mapRightClick : true,
26499 * @event markerClick
26500 * Fires when click the marker.
26501 * @param {Roo.bootstrap.LocationPicker} this
26502 * @param {Map event} e
26504 markerClick : true,
26506 * @event markerRightClick
26507 * Fires when right click the marker.
26508 * @param {Roo.bootstrap.LocationPicker} this
26509 * @param {Map event} e
26511 markerRightClick : true,
26513 * @event OverlayViewDraw
26514 * Fires when OverlayView Draw
26515 * @param {Roo.bootstrap.LocationPicker} this
26517 OverlayViewDraw : true,
26519 * @event OverlayViewOnAdd
26520 * Fires when OverlayView Draw
26521 * @param {Roo.bootstrap.LocationPicker} this
26523 OverlayViewOnAdd : true,
26525 * @event OverlayViewOnRemove
26526 * Fires when OverlayView Draw
26527 * @param {Roo.bootstrap.LocationPicker} this
26529 OverlayViewOnRemove : true,
26531 * @event OverlayViewShow
26532 * Fires when OverlayView Draw
26533 * @param {Roo.bootstrap.LocationPicker} this
26534 * @param {Pixel} cpx
26536 OverlayViewShow : true,
26538 * @event OverlayViewHide
26539 * Fires when OverlayView Draw
26540 * @param {Roo.bootstrap.LocationPicker} this
26542 OverlayViewHide : true,
26544 * @event loadexception
26545 * Fires when load google lib failed.
26546 * @param {Roo.bootstrap.LocationPicker} this
26548 loadexception : true
26553 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26555 gMapContext: false,
26561 mapTypeControl: false,
26562 disableDoubleClickZoom: false,
26564 streetViewControl: false,
26568 enableAutocomplete: false,
26569 enableReverseGeocode: true,
26572 getAutoCreate: function()
26577 cls: 'roo-location-picker'
26583 initEvents: function(ct, position)
26585 if(!this.el.getWidth() || this.isApplied()){
26589 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26594 initial: function()
26596 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26597 this.fireEvent('loadexception', this);
26601 if(!this.mapTypeId){
26602 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26605 this.gMapContext = this.GMapContext();
26607 this.initOverlayView();
26609 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26613 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26614 _this.setPosition(_this.gMapContext.marker.position);
26617 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26618 _this.fireEvent('mapClick', this, event);
26622 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26623 _this.fireEvent('mapRightClick', this, event);
26627 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26628 _this.fireEvent('markerClick', this, event);
26632 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26633 _this.fireEvent('markerRightClick', this, event);
26637 this.setPosition(this.gMapContext.location);
26639 this.fireEvent('initial', this, this.gMapContext.location);
26642 initOverlayView: function()
26646 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26650 _this.fireEvent('OverlayViewDraw', _this);
26655 _this.fireEvent('OverlayViewOnAdd', _this);
26658 onRemove: function()
26660 _this.fireEvent('OverlayViewOnRemove', _this);
26663 show: function(cpx)
26665 _this.fireEvent('OverlayViewShow', _this, cpx);
26670 _this.fireEvent('OverlayViewHide', _this);
26676 fromLatLngToContainerPixel: function(event)
26678 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26681 isApplied: function()
26683 return this.getGmapContext() == false ? false : true;
26686 getGmapContext: function()
26688 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26691 GMapContext: function()
26693 var position = new google.maps.LatLng(this.latitude, this.longitude);
26695 var _map = new google.maps.Map(this.el.dom, {
26698 mapTypeId: this.mapTypeId,
26699 mapTypeControl: this.mapTypeControl,
26700 disableDoubleClickZoom: this.disableDoubleClickZoom,
26701 scrollwheel: this.scrollwheel,
26702 streetViewControl: this.streetViewControl,
26703 locationName: this.locationName,
26704 draggable: this.draggable,
26705 enableAutocomplete: this.enableAutocomplete,
26706 enableReverseGeocode: this.enableReverseGeocode
26709 var _marker = new google.maps.Marker({
26710 position: position,
26712 title: this.markerTitle,
26713 draggable: this.draggable
26720 location: position,
26721 radius: this.radius,
26722 locationName: this.locationName,
26723 addressComponents: {
26724 formatted_address: null,
26725 addressLine1: null,
26726 addressLine2: null,
26728 streetNumber: null,
26732 stateOrProvince: null
26735 domContainer: this.el.dom,
26736 geodecoder: new google.maps.Geocoder()
26740 drawCircle: function(center, radius, options)
26742 if (this.gMapContext.circle != null) {
26743 this.gMapContext.circle.setMap(null);
26747 options = Roo.apply({}, options, {
26748 strokeColor: "#0000FF",
26749 strokeOpacity: .35,
26751 fillColor: "#0000FF",
26755 options.map = this.gMapContext.map;
26756 options.radius = radius;
26757 options.center = center;
26758 this.gMapContext.circle = new google.maps.Circle(options);
26759 return this.gMapContext.circle;
26765 setPosition: function(location)
26767 this.gMapContext.location = location;
26768 this.gMapContext.marker.setPosition(location);
26769 this.gMapContext.map.panTo(location);
26770 this.drawCircle(location, this.gMapContext.radius, {});
26774 if (this.gMapContext.settings.enableReverseGeocode) {
26775 this.gMapContext.geodecoder.geocode({
26776 latLng: this.gMapContext.location
26777 }, function(results, status) {
26779 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26780 _this.gMapContext.locationName = results[0].formatted_address;
26781 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26783 _this.fireEvent('positionchanged', this, location);
26790 this.fireEvent('positionchanged', this, location);
26795 google.maps.event.trigger(this.gMapContext.map, "resize");
26797 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26799 this.fireEvent('resize', this);
26802 setPositionByLatLng: function(latitude, longitude)
26804 this.setPosition(new google.maps.LatLng(latitude, longitude));
26807 getCurrentPosition: function()
26810 latitude: this.gMapContext.location.lat(),
26811 longitude: this.gMapContext.location.lng()
26815 getAddressName: function()
26817 return this.gMapContext.locationName;
26820 getAddressComponents: function()
26822 return this.gMapContext.addressComponents;
26825 address_component_from_google_geocode: function(address_components)
26829 for (var i = 0; i < address_components.length; i++) {
26830 var component = address_components[i];
26831 if (component.types.indexOf("postal_code") >= 0) {
26832 result.postalCode = component.short_name;
26833 } else if (component.types.indexOf("street_number") >= 0) {
26834 result.streetNumber = component.short_name;
26835 } else if (component.types.indexOf("route") >= 0) {
26836 result.streetName = component.short_name;
26837 } else if (component.types.indexOf("neighborhood") >= 0) {
26838 result.city = component.short_name;
26839 } else if (component.types.indexOf("locality") >= 0) {
26840 result.city = component.short_name;
26841 } else if (component.types.indexOf("sublocality") >= 0) {
26842 result.district = component.short_name;
26843 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26844 result.stateOrProvince = component.short_name;
26845 } else if (component.types.indexOf("country") >= 0) {
26846 result.country = component.short_name;
26850 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26851 result.addressLine2 = "";
26855 setZoomLevel: function(zoom)
26857 this.gMapContext.map.setZoom(zoom);
26870 this.fireEvent('show', this);
26881 this.fireEvent('hide', this);
26886 Roo.apply(Roo.bootstrap.LocationPicker, {
26888 OverlayView : function(map, options)
26890 options = options || {};
26904 * @class Roo.bootstrap.Alert
26905 * @extends Roo.bootstrap.Component
26906 * Bootstrap Alert class
26907 * @cfg {String} title The title of alert
26908 * @cfg {String} html The content of alert
26909 * @cfg {String} weight ( success | info | warning | danger )
26910 * @cfg {String} faicon font-awesomeicon
26913 * Create a new alert
26914 * @param {Object} config The config object
26918 Roo.bootstrap.Alert = function(config){
26919 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26923 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26930 getAutoCreate : function()
26939 cls : 'roo-alert-icon'
26944 cls : 'roo-alert-title',
26949 cls : 'roo-alert-text',
26956 cfg.cn[0].cls += ' fa ' + this.faicon;
26960 cfg.cls += ' alert-' + this.weight;
26966 initEvents: function()
26968 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26971 setTitle : function(str)
26973 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26976 setText : function(str)
26978 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26981 setWeight : function(weight)
26984 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26987 this.weight = weight;
26989 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26992 setIcon : function(icon)
26995 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26998 this.faicon = icon;
27000 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27021 * @class Roo.bootstrap.UploadCropbox
27022 * @extends Roo.bootstrap.Component
27023 * Bootstrap UploadCropbox class
27024 * @cfg {String} emptyText show when image has been loaded
27025 * @cfg {String} rotateNotify show when image too small to rotate
27026 * @cfg {Number} errorTimeout default 3000
27027 * @cfg {Number} minWidth default 300
27028 * @cfg {Number} minHeight default 300
27029 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27030 * @cfg {Boolean} isDocument (true|false) default false
27031 * @cfg {String} url action url
27032 * @cfg {String} paramName default 'imageUpload'
27033 * @cfg {String} method default POST
27034 * @cfg {Boolean} loadMask (true|false) default true
27035 * @cfg {Boolean} loadingText default 'Loading...'
27038 * Create a new UploadCropbox
27039 * @param {Object} config The config object
27042 Roo.bootstrap.UploadCropbox = function(config){
27043 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27047 * @event beforeselectfile
27048 * Fire before select file
27049 * @param {Roo.bootstrap.UploadCropbox} this
27051 "beforeselectfile" : true,
27054 * Fire after initEvent
27055 * @param {Roo.bootstrap.UploadCropbox} this
27060 * Fire after initEvent
27061 * @param {Roo.bootstrap.UploadCropbox} this
27062 * @param {String} data
27067 * Fire when preparing the file data
27068 * @param {Roo.bootstrap.UploadCropbox} this
27069 * @param {Object} file
27074 * Fire when get exception
27075 * @param {Roo.bootstrap.UploadCropbox} this
27076 * @param {XMLHttpRequest} xhr
27078 "exception" : true,
27080 * @event beforeloadcanvas
27081 * Fire before load the canvas
27082 * @param {Roo.bootstrap.UploadCropbox} this
27083 * @param {String} src
27085 "beforeloadcanvas" : true,
27088 * Fire when trash image
27089 * @param {Roo.bootstrap.UploadCropbox} this
27094 * Fire when download the image
27095 * @param {Roo.bootstrap.UploadCropbox} this
27099 * @event footerbuttonclick
27100 * Fire when footerbuttonclick
27101 * @param {Roo.bootstrap.UploadCropbox} this
27102 * @param {String} type
27104 "footerbuttonclick" : true,
27108 * @param {Roo.bootstrap.UploadCropbox} this
27113 * Fire when rotate the image
27114 * @param {Roo.bootstrap.UploadCropbox} this
27115 * @param {String} pos
27120 * Fire when inspect the file
27121 * @param {Roo.bootstrap.UploadCropbox} this
27122 * @param {Object} file
27127 * Fire when xhr upload the file
27128 * @param {Roo.bootstrap.UploadCropbox} this
27129 * @param {Object} data
27134 * Fire when arrange the file data
27135 * @param {Roo.bootstrap.UploadCropbox} this
27136 * @param {Object} formData
27141 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27144 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27146 emptyText : 'Click to upload image',
27147 rotateNotify : 'Image is too small to rotate',
27148 errorTimeout : 3000,
27162 cropType : 'image/jpeg',
27164 canvasLoaded : false,
27165 isDocument : false,
27167 paramName : 'imageUpload',
27169 loadingText : 'Loading...',
27172 getAutoCreate : function()
27176 cls : 'roo-upload-cropbox',
27180 cls : 'roo-upload-cropbox-selector',
27185 cls : 'roo-upload-cropbox-body',
27186 style : 'cursor:pointer',
27190 cls : 'roo-upload-cropbox-preview'
27194 cls : 'roo-upload-cropbox-thumb'
27198 cls : 'roo-upload-cropbox-empty-notify',
27199 html : this.emptyText
27203 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27204 html : this.rotateNotify
27210 cls : 'roo-upload-cropbox-footer',
27213 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27223 onRender : function(ct, position)
27225 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27227 if (this.buttons.length) {
27229 Roo.each(this.buttons, function(bb) {
27231 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27233 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27239 this.maskEl = this.el;
27243 initEvents : function()
27245 this.urlAPI = (window.createObjectURL && window) ||
27246 (window.URL && URL.revokeObjectURL && URL) ||
27247 (window.webkitURL && webkitURL);
27249 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27250 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27252 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27253 this.selectorEl.hide();
27255 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27256 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27258 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27259 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27260 this.thumbEl.hide();
27262 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27263 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27265 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27266 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27267 this.errorEl.hide();
27269 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27270 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27271 this.footerEl.hide();
27273 this.setThumbBoxSize();
27279 this.fireEvent('initial', this);
27286 window.addEventListener("resize", function() { _this.resize(); } );
27288 this.bodyEl.on('click', this.beforeSelectFile, this);
27291 this.bodyEl.on('touchstart', this.onTouchStart, this);
27292 this.bodyEl.on('touchmove', this.onTouchMove, this);
27293 this.bodyEl.on('touchend', this.onTouchEnd, this);
27297 this.bodyEl.on('mousedown', this.onMouseDown, this);
27298 this.bodyEl.on('mousemove', this.onMouseMove, this);
27299 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27300 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27301 Roo.get(document).on('mouseup', this.onMouseUp, this);
27304 this.selectorEl.on('change', this.onFileSelected, this);
27310 this.baseScale = 1;
27312 this.baseRotate = 1;
27313 this.dragable = false;
27314 this.pinching = false;
27317 this.cropData = false;
27318 this.notifyEl.dom.innerHTML = this.emptyText;
27320 this.selectorEl.dom.value = '';
27324 resize : function()
27326 if(this.fireEvent('resize', this) != false){
27327 this.setThumbBoxPosition();
27328 this.setCanvasPosition();
27332 onFooterButtonClick : function(e, el, o, type)
27335 case 'rotate-left' :
27336 this.onRotateLeft(e);
27338 case 'rotate-right' :
27339 this.onRotateRight(e);
27342 this.beforeSelectFile(e);
27357 this.fireEvent('footerbuttonclick', this, type);
27360 beforeSelectFile : function(e)
27362 e.preventDefault();
27364 if(this.fireEvent('beforeselectfile', this) != false){
27365 this.selectorEl.dom.click();
27369 onFileSelected : function(e)
27371 e.preventDefault();
27373 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27377 var file = this.selectorEl.dom.files[0];
27379 if(this.fireEvent('inspect', this, file) != false){
27380 this.prepare(file);
27385 trash : function(e)
27387 this.fireEvent('trash', this);
27390 download : function(e)
27392 this.fireEvent('download', this);
27395 loadCanvas : function(src)
27397 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27401 this.imageEl = document.createElement('img');
27405 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27407 this.imageEl.src = src;
27411 onLoadCanvas : function()
27413 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27414 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27416 this.bodyEl.un('click', this.beforeSelectFile, this);
27418 this.notifyEl.hide();
27419 this.thumbEl.show();
27420 this.footerEl.show();
27422 this.baseRotateLevel();
27424 if(this.isDocument){
27425 this.setThumbBoxSize();
27428 this.setThumbBoxPosition();
27430 this.baseScaleLevel();
27436 this.canvasLoaded = true;
27439 this.maskEl.unmask();
27444 setCanvasPosition : function()
27446 if(!this.canvasEl){
27450 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27451 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27453 this.previewEl.setLeft(pw);
27454 this.previewEl.setTop(ph);
27458 onMouseDown : function(e)
27462 this.dragable = true;
27463 this.pinching = false;
27465 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27466 this.dragable = false;
27470 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27471 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27475 onMouseMove : function(e)
27479 if(!this.canvasLoaded){
27483 if (!this.dragable){
27487 var minX = Math.ceil(this.thumbEl.getLeft(true));
27488 var minY = Math.ceil(this.thumbEl.getTop(true));
27490 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27491 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27493 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27494 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27496 x = x - this.mouseX;
27497 y = y - this.mouseY;
27499 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27500 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27502 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27503 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27505 this.previewEl.setLeft(bgX);
27506 this.previewEl.setTop(bgY);
27508 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27509 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27512 onMouseUp : function(e)
27516 this.dragable = false;
27519 onMouseWheel : function(e)
27523 this.startScale = this.scale;
27525 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27527 if(!this.zoomable()){
27528 this.scale = this.startScale;
27537 zoomable : function()
27539 var minScale = this.thumbEl.getWidth() / this.minWidth;
27541 if(this.minWidth < this.minHeight){
27542 minScale = this.thumbEl.getHeight() / this.minHeight;
27545 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27546 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27550 (this.rotate == 0 || this.rotate == 180) &&
27552 width > this.imageEl.OriginWidth ||
27553 height > this.imageEl.OriginHeight ||
27554 (width < this.minWidth && height < this.minHeight)
27562 (this.rotate == 90 || this.rotate == 270) &&
27564 width > this.imageEl.OriginWidth ||
27565 height > this.imageEl.OriginHeight ||
27566 (width < this.minHeight && height < this.minWidth)
27573 !this.isDocument &&
27574 (this.rotate == 0 || this.rotate == 180) &&
27576 width < this.minWidth ||
27577 width > this.imageEl.OriginWidth ||
27578 height < this.minHeight ||
27579 height > this.imageEl.OriginHeight
27586 !this.isDocument &&
27587 (this.rotate == 90 || this.rotate == 270) &&
27589 width < this.minHeight ||
27590 width > this.imageEl.OriginWidth ||
27591 height < this.minWidth ||
27592 height > this.imageEl.OriginHeight
27602 onRotateLeft : function(e)
27604 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27606 var minScale = this.thumbEl.getWidth() / this.minWidth;
27608 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27609 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27611 this.startScale = this.scale;
27613 while (this.getScaleLevel() < minScale){
27615 this.scale = this.scale + 1;
27617 if(!this.zoomable()){
27622 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27623 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27628 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27635 this.scale = this.startScale;
27637 this.onRotateFail();
27642 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27644 if(this.isDocument){
27645 this.setThumbBoxSize();
27646 this.setThumbBoxPosition();
27647 this.setCanvasPosition();
27652 this.fireEvent('rotate', this, 'left');
27656 onRotateRight : function(e)
27658 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27660 var minScale = this.thumbEl.getWidth() / this.minWidth;
27662 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27663 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27665 this.startScale = this.scale;
27667 while (this.getScaleLevel() < minScale){
27669 this.scale = this.scale + 1;
27671 if(!this.zoomable()){
27676 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27677 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27682 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27689 this.scale = this.startScale;
27691 this.onRotateFail();
27696 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27698 if(this.isDocument){
27699 this.setThumbBoxSize();
27700 this.setThumbBoxPosition();
27701 this.setCanvasPosition();
27706 this.fireEvent('rotate', this, 'right');
27709 onRotateFail : function()
27711 this.errorEl.show(true);
27715 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27720 this.previewEl.dom.innerHTML = '';
27722 var canvasEl = document.createElement("canvas");
27724 var contextEl = canvasEl.getContext("2d");
27726 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27727 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27728 var center = this.imageEl.OriginWidth / 2;
27730 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27731 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27732 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27733 center = this.imageEl.OriginHeight / 2;
27736 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27738 contextEl.translate(center, center);
27739 contextEl.rotate(this.rotate * Math.PI / 180);
27741 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27743 this.canvasEl = document.createElement("canvas");
27745 this.contextEl = this.canvasEl.getContext("2d");
27747 switch (this.rotate) {
27750 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27751 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27753 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27758 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27759 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27761 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27762 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);
27766 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27771 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27772 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27774 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27775 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);
27779 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);
27784 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27785 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27787 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27788 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27792 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);
27799 this.previewEl.appendChild(this.canvasEl);
27801 this.setCanvasPosition();
27806 if(!this.canvasLoaded){
27810 var imageCanvas = document.createElement("canvas");
27812 var imageContext = imageCanvas.getContext("2d");
27814 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27815 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27817 var center = imageCanvas.width / 2;
27819 imageContext.translate(center, center);
27821 imageContext.rotate(this.rotate * Math.PI / 180);
27823 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27825 var canvas = document.createElement("canvas");
27827 var context = canvas.getContext("2d");
27829 canvas.width = this.minWidth;
27830 canvas.height = this.minHeight;
27832 switch (this.rotate) {
27835 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27836 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27838 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27839 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27841 var targetWidth = this.minWidth - 2 * x;
27842 var targetHeight = this.minHeight - 2 * y;
27846 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27847 scale = targetWidth / width;
27850 if(x > 0 && y == 0){
27851 scale = targetHeight / height;
27854 if(x > 0 && y > 0){
27855 scale = targetWidth / width;
27857 if(width < height){
27858 scale = targetHeight / height;
27862 context.scale(scale, scale);
27864 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27865 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27867 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27868 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27870 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27875 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27876 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27878 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27879 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27881 var targetWidth = this.minWidth - 2 * x;
27882 var targetHeight = this.minHeight - 2 * y;
27886 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27887 scale = targetWidth / width;
27890 if(x > 0 && y == 0){
27891 scale = targetHeight / height;
27894 if(x > 0 && y > 0){
27895 scale = targetWidth / width;
27897 if(width < height){
27898 scale = targetHeight / height;
27902 context.scale(scale, scale);
27904 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27905 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27907 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27908 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27910 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27912 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27917 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27918 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27920 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27921 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27923 var targetWidth = this.minWidth - 2 * x;
27924 var targetHeight = this.minHeight - 2 * y;
27928 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27929 scale = targetWidth / width;
27932 if(x > 0 && y == 0){
27933 scale = targetHeight / height;
27936 if(x > 0 && y > 0){
27937 scale = targetWidth / width;
27939 if(width < height){
27940 scale = targetHeight / height;
27944 context.scale(scale, scale);
27946 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27947 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27949 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27950 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27952 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27953 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27955 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27960 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27961 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27963 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27964 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27966 var targetWidth = this.minWidth - 2 * x;
27967 var targetHeight = this.minHeight - 2 * y;
27971 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27972 scale = targetWidth / width;
27975 if(x > 0 && y == 0){
27976 scale = targetHeight / height;
27979 if(x > 0 && y > 0){
27980 scale = targetWidth / width;
27982 if(width < height){
27983 scale = targetHeight / height;
27987 context.scale(scale, scale);
27989 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27990 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27992 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27993 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27995 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27997 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28004 this.cropData = canvas.toDataURL(this.cropType);
28006 if(this.fireEvent('crop', this, this.cropData) !== false){
28007 this.process(this.file, this.cropData);
28014 setThumbBoxSize : function()
28018 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28019 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28020 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28022 this.minWidth = width;
28023 this.minHeight = height;
28025 if(this.rotate == 90 || this.rotate == 270){
28026 this.minWidth = height;
28027 this.minHeight = width;
28032 width = Math.ceil(this.minWidth * height / this.minHeight);
28034 if(this.minWidth > this.minHeight){
28036 height = Math.ceil(this.minHeight * width / this.minWidth);
28039 this.thumbEl.setStyle({
28040 width : width + 'px',
28041 height : height + 'px'
28048 setThumbBoxPosition : function()
28050 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28051 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28053 this.thumbEl.setLeft(x);
28054 this.thumbEl.setTop(y);
28058 baseRotateLevel : function()
28060 this.baseRotate = 1;
28063 typeof(this.exif) != 'undefined' &&
28064 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28065 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28067 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28070 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28074 baseScaleLevel : function()
28078 if(this.isDocument){
28080 if(this.baseRotate == 6 || this.baseRotate == 8){
28082 height = this.thumbEl.getHeight();
28083 this.baseScale = height / this.imageEl.OriginWidth;
28085 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28086 width = this.thumbEl.getWidth();
28087 this.baseScale = width / this.imageEl.OriginHeight;
28093 height = this.thumbEl.getHeight();
28094 this.baseScale = height / this.imageEl.OriginHeight;
28096 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28097 width = this.thumbEl.getWidth();
28098 this.baseScale = width / this.imageEl.OriginWidth;
28104 if(this.baseRotate == 6 || this.baseRotate == 8){
28106 width = this.thumbEl.getHeight();
28107 this.baseScale = width / this.imageEl.OriginHeight;
28109 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28110 height = this.thumbEl.getWidth();
28111 this.baseScale = height / this.imageEl.OriginHeight;
28114 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28115 height = this.thumbEl.getWidth();
28116 this.baseScale = height / this.imageEl.OriginHeight;
28118 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28119 width = this.thumbEl.getHeight();
28120 this.baseScale = width / this.imageEl.OriginWidth;
28127 width = this.thumbEl.getWidth();
28128 this.baseScale = width / this.imageEl.OriginWidth;
28130 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28131 height = this.thumbEl.getHeight();
28132 this.baseScale = height / this.imageEl.OriginHeight;
28135 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28137 height = this.thumbEl.getHeight();
28138 this.baseScale = height / this.imageEl.OriginHeight;
28140 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28141 width = this.thumbEl.getWidth();
28142 this.baseScale = width / this.imageEl.OriginWidth;
28150 getScaleLevel : function()
28152 return this.baseScale * Math.pow(1.1, this.scale);
28155 onTouchStart : function(e)
28157 if(!this.canvasLoaded){
28158 this.beforeSelectFile(e);
28162 var touches = e.browserEvent.touches;
28168 if(touches.length == 1){
28169 this.onMouseDown(e);
28173 if(touches.length != 2){
28179 for(var i = 0, finger; finger = touches[i]; i++){
28180 coords.push(finger.pageX, finger.pageY);
28183 var x = Math.pow(coords[0] - coords[2], 2);
28184 var y = Math.pow(coords[1] - coords[3], 2);
28186 this.startDistance = Math.sqrt(x + y);
28188 this.startScale = this.scale;
28190 this.pinching = true;
28191 this.dragable = false;
28195 onTouchMove : function(e)
28197 if(!this.pinching && !this.dragable){
28201 var touches = e.browserEvent.touches;
28208 this.onMouseMove(e);
28214 for(var i = 0, finger; finger = touches[i]; i++){
28215 coords.push(finger.pageX, finger.pageY);
28218 var x = Math.pow(coords[0] - coords[2], 2);
28219 var y = Math.pow(coords[1] - coords[3], 2);
28221 this.endDistance = Math.sqrt(x + y);
28223 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28225 if(!this.zoomable()){
28226 this.scale = this.startScale;
28234 onTouchEnd : function(e)
28236 this.pinching = false;
28237 this.dragable = false;
28241 process : function(file, crop)
28244 this.maskEl.mask(this.loadingText);
28247 this.xhr = new XMLHttpRequest();
28249 file.xhr = this.xhr;
28251 this.xhr.open(this.method, this.url, true);
28254 "Accept": "application/json",
28255 "Cache-Control": "no-cache",
28256 "X-Requested-With": "XMLHttpRequest"
28259 for (var headerName in headers) {
28260 var headerValue = headers[headerName];
28262 this.xhr.setRequestHeader(headerName, headerValue);
28268 this.xhr.onload = function()
28270 _this.xhrOnLoad(_this.xhr);
28273 this.xhr.onerror = function()
28275 _this.xhrOnError(_this.xhr);
28278 var formData = new FormData();
28280 formData.append('returnHTML', 'NO');
28283 formData.append('crop', crop);
28286 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28287 formData.append(this.paramName, file, file.name);
28290 if(typeof(file.filename) != 'undefined'){
28291 formData.append('filename', file.filename);
28294 if(typeof(file.mimetype) != 'undefined'){
28295 formData.append('mimetype', file.mimetype);
28298 if(this.fireEvent('arrange', this, formData) != false){
28299 this.xhr.send(formData);
28303 xhrOnLoad : function(xhr)
28306 this.maskEl.unmask();
28309 if (xhr.readyState !== 4) {
28310 this.fireEvent('exception', this, xhr);
28314 var response = Roo.decode(xhr.responseText);
28316 if(!response.success){
28317 this.fireEvent('exception', this, xhr);
28321 var response = Roo.decode(xhr.responseText);
28323 this.fireEvent('upload', this, response);
28327 xhrOnError : function()
28330 this.maskEl.unmask();
28333 Roo.log('xhr on error');
28335 var response = Roo.decode(xhr.responseText);
28341 prepare : function(file)
28344 this.maskEl.mask(this.loadingText);
28350 if(typeof(file) === 'string'){
28351 this.loadCanvas(file);
28355 if(!file || !this.urlAPI){
28360 this.cropType = file.type;
28364 if(this.fireEvent('prepare', this, this.file) != false){
28366 var reader = new FileReader();
28368 reader.onload = function (e) {
28369 if (e.target.error) {
28370 Roo.log(e.target.error);
28374 var buffer = e.target.result,
28375 dataView = new DataView(buffer),
28377 maxOffset = dataView.byteLength - 4,
28381 if (dataView.getUint16(0) === 0xffd8) {
28382 while (offset < maxOffset) {
28383 markerBytes = dataView.getUint16(offset);
28385 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28386 markerLength = dataView.getUint16(offset + 2) + 2;
28387 if (offset + markerLength > dataView.byteLength) {
28388 Roo.log('Invalid meta data: Invalid segment size.');
28392 if(markerBytes == 0xffe1){
28393 _this.parseExifData(
28400 offset += markerLength;
28410 var url = _this.urlAPI.createObjectURL(_this.file);
28412 _this.loadCanvas(url);
28417 reader.readAsArrayBuffer(this.file);
28423 parseExifData : function(dataView, offset, length)
28425 var tiffOffset = offset + 10,
28429 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28430 // No Exif data, might be XMP data instead
28434 // Check for the ASCII code for "Exif" (0x45786966):
28435 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28436 // No Exif data, might be XMP data instead
28439 if (tiffOffset + 8 > dataView.byteLength) {
28440 Roo.log('Invalid Exif data: Invalid segment size.');
28443 // Check for the two null bytes:
28444 if (dataView.getUint16(offset + 8) !== 0x0000) {
28445 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28448 // Check the byte alignment:
28449 switch (dataView.getUint16(tiffOffset)) {
28451 littleEndian = true;
28454 littleEndian = false;
28457 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28460 // Check for the TIFF tag marker (0x002A):
28461 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28462 Roo.log('Invalid Exif data: Missing TIFF marker.');
28465 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28466 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28468 this.parseExifTags(
28471 tiffOffset + dirOffset,
28476 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28481 if (dirOffset + 6 > dataView.byteLength) {
28482 Roo.log('Invalid Exif data: Invalid directory offset.');
28485 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28486 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28487 if (dirEndOffset + 4 > dataView.byteLength) {
28488 Roo.log('Invalid Exif data: Invalid directory size.');
28491 for (i = 0; i < tagsNumber; i += 1) {
28495 dirOffset + 2 + 12 * i, // tag offset
28499 // Return the offset to the next directory:
28500 return dataView.getUint32(dirEndOffset, littleEndian);
28503 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28505 var tag = dataView.getUint16(offset, littleEndian);
28507 this.exif[tag] = this.getExifValue(
28511 dataView.getUint16(offset + 2, littleEndian), // tag type
28512 dataView.getUint32(offset + 4, littleEndian), // tag length
28517 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28519 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28528 Roo.log('Invalid Exif data: Invalid tag type.');
28532 tagSize = tagType.size * length;
28533 // Determine if the value is contained in the dataOffset bytes,
28534 // or if the value at the dataOffset is a pointer to the actual data:
28535 dataOffset = tagSize > 4 ?
28536 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28537 if (dataOffset + tagSize > dataView.byteLength) {
28538 Roo.log('Invalid Exif data: Invalid data offset.');
28541 if (length === 1) {
28542 return tagType.getValue(dataView, dataOffset, littleEndian);
28545 for (i = 0; i < length; i += 1) {
28546 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28549 if (tagType.ascii) {
28551 // Concatenate the chars:
28552 for (i = 0; i < values.length; i += 1) {
28554 // Ignore the terminating NULL byte(s):
28555 if (c === '\u0000') {
28567 Roo.apply(Roo.bootstrap.UploadCropbox, {
28569 'Orientation': 0x0112
28573 1: 0, //'top-left',
28575 3: 180, //'bottom-right',
28576 // 4: 'bottom-left',
28578 6: 90, //'right-top',
28579 // 7: 'right-bottom',
28580 8: 270 //'left-bottom'
28584 // byte, 8-bit unsigned int:
28586 getValue: function (dataView, dataOffset) {
28587 return dataView.getUint8(dataOffset);
28591 // ascii, 8-bit byte:
28593 getValue: function (dataView, dataOffset) {
28594 return String.fromCharCode(dataView.getUint8(dataOffset));
28599 // short, 16 bit int:
28601 getValue: function (dataView, dataOffset, littleEndian) {
28602 return dataView.getUint16(dataOffset, littleEndian);
28606 // long, 32 bit int:
28608 getValue: function (dataView, dataOffset, littleEndian) {
28609 return dataView.getUint32(dataOffset, littleEndian);
28613 // rational = two long values, first is numerator, second is denominator:
28615 getValue: function (dataView, dataOffset, littleEndian) {
28616 return dataView.getUint32(dataOffset, littleEndian) /
28617 dataView.getUint32(dataOffset + 4, littleEndian);
28621 // slong, 32 bit signed int:
28623 getValue: function (dataView, dataOffset, littleEndian) {
28624 return dataView.getInt32(dataOffset, littleEndian);
28628 // srational, two slongs, first is numerator, second is denominator:
28630 getValue: function (dataView, dataOffset, littleEndian) {
28631 return dataView.getInt32(dataOffset, littleEndian) /
28632 dataView.getInt32(dataOffset + 4, littleEndian);
28642 cls : 'btn-group roo-upload-cropbox-rotate-left',
28643 action : 'rotate-left',
28647 cls : 'btn btn-default',
28648 html : '<i class="fa fa-undo"></i>'
28654 cls : 'btn-group roo-upload-cropbox-picture',
28655 action : 'picture',
28659 cls : 'btn btn-default',
28660 html : '<i class="fa fa-picture-o"></i>'
28666 cls : 'btn-group roo-upload-cropbox-rotate-right',
28667 action : 'rotate-right',
28671 cls : 'btn btn-default',
28672 html : '<i class="fa fa-repeat"></i>'
28680 cls : 'btn-group roo-upload-cropbox-rotate-left',
28681 action : 'rotate-left',
28685 cls : 'btn btn-default',
28686 html : '<i class="fa fa-undo"></i>'
28692 cls : 'btn-group roo-upload-cropbox-download',
28693 action : 'download',
28697 cls : 'btn btn-default',
28698 html : '<i class="fa fa-download"></i>'
28704 cls : 'btn-group roo-upload-cropbox-crop',
28709 cls : 'btn btn-default',
28710 html : '<i class="fa fa-crop"></i>'
28716 cls : 'btn-group roo-upload-cropbox-trash',
28721 cls : 'btn btn-default',
28722 html : '<i class="fa fa-trash"></i>'
28728 cls : 'btn-group roo-upload-cropbox-rotate-right',
28729 action : 'rotate-right',
28733 cls : 'btn btn-default',
28734 html : '<i class="fa fa-repeat"></i>'
28742 cls : 'btn-group roo-upload-cropbox-rotate-left',
28743 action : 'rotate-left',
28747 cls : 'btn btn-default',
28748 html : '<i class="fa fa-undo"></i>'
28754 cls : 'btn-group roo-upload-cropbox-rotate-right',
28755 action : 'rotate-right',
28759 cls : 'btn btn-default',
28760 html : '<i class="fa fa-repeat"></i>'
28773 * @class Roo.bootstrap.DocumentManager
28774 * @extends Roo.bootstrap.Component
28775 * Bootstrap DocumentManager class
28776 * @cfg {String} paramName default 'imageUpload'
28777 * @cfg {String} toolTipName default 'filename'
28778 * @cfg {String} method default POST
28779 * @cfg {String} url action url
28780 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28781 * @cfg {Boolean} multiple multiple upload default true
28782 * @cfg {Number} thumbSize default 300
28783 * @cfg {String} fieldLabel
28784 * @cfg {Number} labelWidth default 4
28785 * @cfg {String} labelAlign (left|top) default left
28786 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28787 * @cfg {Number} labellg set the width of label (1-12)
28788 * @cfg {Number} labelmd set the width of label (1-12)
28789 * @cfg {Number} labelsm set the width of label (1-12)
28790 * @cfg {Number} labelxs set the width of label (1-12)
28793 * Create a new DocumentManager
28794 * @param {Object} config The config object
28797 Roo.bootstrap.DocumentManager = function(config){
28798 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28801 this.delegates = [];
28806 * Fire when initial the DocumentManager
28807 * @param {Roo.bootstrap.DocumentManager} this
28812 * inspect selected file
28813 * @param {Roo.bootstrap.DocumentManager} this
28814 * @param {File} file
28819 * Fire when xhr load exception
28820 * @param {Roo.bootstrap.DocumentManager} this
28821 * @param {XMLHttpRequest} xhr
28823 "exception" : true,
28825 * @event afterupload
28826 * Fire when xhr load exception
28827 * @param {Roo.bootstrap.DocumentManager} this
28828 * @param {XMLHttpRequest} xhr
28830 "afterupload" : true,
28833 * prepare the form data
28834 * @param {Roo.bootstrap.DocumentManager} this
28835 * @param {Object} formData
28840 * Fire when remove the file
28841 * @param {Roo.bootstrap.DocumentManager} this
28842 * @param {Object} file
28847 * Fire after refresh the file
28848 * @param {Roo.bootstrap.DocumentManager} this
28853 * Fire after click the image
28854 * @param {Roo.bootstrap.DocumentManager} this
28855 * @param {Object} file
28860 * Fire when upload a image and editable set to true
28861 * @param {Roo.bootstrap.DocumentManager} this
28862 * @param {Object} file
28866 * @event beforeselectfile
28867 * Fire before select file
28868 * @param {Roo.bootstrap.DocumentManager} this
28870 "beforeselectfile" : true,
28873 * Fire before process file
28874 * @param {Roo.bootstrap.DocumentManager} this
28875 * @param {Object} file
28879 * @event previewrendered
28880 * Fire when preview rendered
28881 * @param {Roo.bootstrap.DocumentManager} this
28882 * @param {Object} file
28884 "previewrendered" : true,
28887 "previewResize" : true
28892 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28901 paramName : 'imageUpload',
28902 toolTipName : 'filename',
28905 labelAlign : 'left',
28915 getAutoCreate : function()
28917 var managerWidget = {
28919 cls : 'roo-document-manager',
28923 cls : 'roo-document-manager-selector',
28928 cls : 'roo-document-manager-uploader',
28932 cls : 'roo-document-manager-upload-btn',
28933 html : '<i class="fa fa-plus"></i>'
28944 cls : 'column col-md-12',
28949 if(this.fieldLabel.length){
28954 cls : 'column col-md-12',
28955 html : this.fieldLabel
28959 cls : 'column col-md-12',
28964 if(this.labelAlign == 'left'){
28969 html : this.fieldLabel
28978 if(this.labelWidth > 12){
28979 content[0].style = "width: " + this.labelWidth + 'px';
28982 if(this.labelWidth < 13 && this.labelmd == 0){
28983 this.labelmd = this.labelWidth;
28986 if(this.labellg > 0){
28987 content[0].cls += ' col-lg-' + this.labellg;
28988 content[1].cls += ' col-lg-' + (12 - this.labellg);
28991 if(this.labelmd > 0){
28992 content[0].cls += ' col-md-' + this.labelmd;
28993 content[1].cls += ' col-md-' + (12 - this.labelmd);
28996 if(this.labelsm > 0){
28997 content[0].cls += ' col-sm-' + this.labelsm;
28998 content[1].cls += ' col-sm-' + (12 - this.labelsm);
29001 if(this.labelxs > 0){
29002 content[0].cls += ' col-xs-' + this.labelxs;
29003 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29011 cls : 'row clearfix',
29019 initEvents : function()
29021 this.managerEl = this.el.select('.roo-document-manager', true).first();
29022 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29024 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29025 this.selectorEl.hide();
29028 this.selectorEl.attr('multiple', 'multiple');
29031 this.selectorEl.on('change', this.onFileSelected, this);
29033 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29034 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29036 this.uploader.on('click', this.onUploaderClick, this);
29038 this.renderProgressDialog();
29042 window.addEventListener("resize", function() { _this.refresh(); } );
29044 this.fireEvent('initial', this);
29047 renderProgressDialog : function()
29051 this.progressDialog = new Roo.bootstrap.Modal({
29052 cls : 'roo-document-manager-progress-dialog',
29053 allow_close : false,
29063 btnclick : function() {
29064 _this.uploadCancel();
29070 this.progressDialog.render(Roo.get(document.body));
29072 this.progress = new Roo.bootstrap.Progress({
29073 cls : 'roo-document-manager-progress',
29078 this.progress.render(this.progressDialog.getChildContainer());
29080 this.progressBar = new Roo.bootstrap.ProgressBar({
29081 cls : 'roo-document-manager-progress-bar',
29084 aria_valuemax : 12,
29088 this.progressBar.render(this.progress.getChildContainer());
29091 onUploaderClick : function(e)
29093 e.preventDefault();
29095 if(this.fireEvent('beforeselectfile', this) != false){
29096 this.selectorEl.dom.click();
29101 onFileSelected : function(e)
29103 e.preventDefault();
29105 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29109 Roo.each(this.selectorEl.dom.files, function(file){
29110 if(this.fireEvent('inspect', this, file) != false){
29111 this.files.push(file);
29121 this.selectorEl.dom.value = '';
29123 if(!this.files || !this.files.length){
29127 if(this.boxes > 0 && this.files.length > this.boxes){
29128 this.files = this.files.slice(0, this.boxes);
29131 this.uploader.show();
29133 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29134 this.uploader.hide();
29143 Roo.each(this.files, function(file){
29145 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29146 var f = this.renderPreview(file);
29151 if(file.type.indexOf('image') != -1){
29152 this.delegates.push(
29154 _this.process(file);
29155 }).createDelegate(this)
29163 _this.process(file);
29164 }).createDelegate(this)
29169 this.files = files;
29171 this.delegates = this.delegates.concat(docs);
29173 if(!this.delegates.length){
29178 this.progressBar.aria_valuemax = this.delegates.length;
29185 arrange : function()
29187 if(!this.delegates.length){
29188 this.progressDialog.hide();
29193 var delegate = this.delegates.shift();
29195 this.progressDialog.show();
29197 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29199 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29204 refresh : function()
29206 this.uploader.show();
29208 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29209 this.uploader.hide();
29212 Roo.isTouch ? this.closable(false) : this.closable(true);
29214 this.fireEvent('refresh', this);
29217 onRemove : function(e, el, o)
29219 e.preventDefault();
29221 this.fireEvent('remove', this, o);
29225 remove : function(o)
29229 Roo.each(this.files, function(file){
29230 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29239 this.files = files;
29246 Roo.each(this.files, function(file){
29251 file.target.remove();
29260 onClick : function(e, el, o)
29262 e.preventDefault();
29264 this.fireEvent('click', this, o);
29268 closable : function(closable)
29270 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29272 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29284 xhrOnLoad : function(xhr)
29286 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29290 if (xhr.readyState !== 4) {
29292 this.fireEvent('exception', this, xhr);
29296 var response = Roo.decode(xhr.responseText);
29298 if(!response.success){
29300 this.fireEvent('exception', this, xhr);
29304 var file = this.renderPreview(response.data);
29306 this.files.push(file);
29310 this.fireEvent('afterupload', this, xhr);
29314 xhrOnError : function(xhr)
29316 Roo.log('xhr on error');
29318 var response = Roo.decode(xhr.responseText);
29325 process : function(file)
29327 if(this.fireEvent('process', this, file) !== false){
29328 if(this.editable && file.type.indexOf('image') != -1){
29329 this.fireEvent('edit', this, file);
29333 this.uploadStart(file, false);
29340 uploadStart : function(file, crop)
29342 this.xhr = new XMLHttpRequest();
29344 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29349 file.xhr = this.xhr;
29351 this.managerEl.createChild({
29353 cls : 'roo-document-manager-loading',
29357 tooltip : file.name,
29358 cls : 'roo-document-manager-thumb',
29359 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29365 this.xhr.open(this.method, this.url, true);
29368 "Accept": "application/json",
29369 "Cache-Control": "no-cache",
29370 "X-Requested-With": "XMLHttpRequest"
29373 for (var headerName in headers) {
29374 var headerValue = headers[headerName];
29376 this.xhr.setRequestHeader(headerName, headerValue);
29382 this.xhr.onload = function()
29384 _this.xhrOnLoad(_this.xhr);
29387 this.xhr.onerror = function()
29389 _this.xhrOnError(_this.xhr);
29392 var formData = new FormData();
29394 formData.append('returnHTML', 'NO');
29397 formData.append('crop', crop);
29400 formData.append(this.paramName, file, file.name);
29407 if(this.fireEvent('prepare', this, formData, options) != false){
29409 if(options.manually){
29413 this.xhr.send(formData);
29417 this.uploadCancel();
29420 uploadCancel : function()
29426 this.delegates = [];
29428 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29435 renderPreview : function(file)
29437 if(typeof(file.target) != 'undefined' && file.target){
29441 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29443 var previewEl = this.managerEl.createChild({
29445 cls : 'roo-document-manager-preview',
29449 tooltip : file[this.toolTipName],
29450 cls : 'roo-document-manager-thumb',
29451 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29456 html : '<i class="fa fa-times-circle"></i>'
29461 var close = previewEl.select('button.close', true).first();
29463 close.on('click', this.onRemove, this, file);
29465 file.target = previewEl;
29467 var image = previewEl.select('img', true).first();
29471 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29473 image.on('click', this.onClick, this, file);
29475 this.fireEvent('previewrendered', this, file);
29481 onPreviewLoad : function(file, image)
29483 if(typeof(file.target) == 'undefined' || !file.target){
29487 var width = image.dom.naturalWidth || image.dom.width;
29488 var height = image.dom.naturalHeight || image.dom.height;
29490 if(!this.previewResize) {
29494 if(width > height){
29495 file.target.addClass('wide');
29499 file.target.addClass('tall');
29504 uploadFromSource : function(file, crop)
29506 this.xhr = new XMLHttpRequest();
29508 this.managerEl.createChild({
29510 cls : 'roo-document-manager-loading',
29514 tooltip : file.name,
29515 cls : 'roo-document-manager-thumb',
29516 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29522 this.xhr.open(this.method, this.url, true);
29525 "Accept": "application/json",
29526 "Cache-Control": "no-cache",
29527 "X-Requested-With": "XMLHttpRequest"
29530 for (var headerName in headers) {
29531 var headerValue = headers[headerName];
29533 this.xhr.setRequestHeader(headerName, headerValue);
29539 this.xhr.onload = function()
29541 _this.xhrOnLoad(_this.xhr);
29544 this.xhr.onerror = function()
29546 _this.xhrOnError(_this.xhr);
29549 var formData = new FormData();
29551 formData.append('returnHTML', 'NO');
29553 formData.append('crop', crop);
29555 if(typeof(file.filename) != 'undefined'){
29556 formData.append('filename', file.filename);
29559 if(typeof(file.mimetype) != 'undefined'){
29560 formData.append('mimetype', file.mimetype);
29565 if(this.fireEvent('prepare', this, formData) != false){
29566 this.xhr.send(formData);
29576 * @class Roo.bootstrap.DocumentViewer
29577 * @extends Roo.bootstrap.Component
29578 * Bootstrap DocumentViewer class
29579 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29580 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29583 * Create a new DocumentViewer
29584 * @param {Object} config The config object
29587 Roo.bootstrap.DocumentViewer = function(config){
29588 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29593 * Fire after initEvent
29594 * @param {Roo.bootstrap.DocumentViewer} this
29600 * @param {Roo.bootstrap.DocumentViewer} this
29605 * Fire after download button
29606 * @param {Roo.bootstrap.DocumentViewer} this
29611 * Fire after trash button
29612 * @param {Roo.bootstrap.DocumentViewer} this
29619 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29621 showDownload : true,
29625 getAutoCreate : function()
29629 cls : 'roo-document-viewer',
29633 cls : 'roo-document-viewer-body',
29637 cls : 'roo-document-viewer-thumb',
29641 cls : 'roo-document-viewer-image'
29649 cls : 'roo-document-viewer-footer',
29652 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29656 cls : 'btn-group roo-document-viewer-download',
29660 cls : 'btn btn-default',
29661 html : '<i class="fa fa-download"></i>'
29667 cls : 'btn-group roo-document-viewer-trash',
29671 cls : 'btn btn-default',
29672 html : '<i class="fa fa-trash"></i>'
29685 initEvents : function()
29687 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29688 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29690 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29691 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29693 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29694 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29696 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29697 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29699 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29700 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29702 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29703 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29705 this.bodyEl.on('click', this.onClick, this);
29706 this.downloadBtn.on('click', this.onDownload, this);
29707 this.trashBtn.on('click', this.onTrash, this);
29709 this.downloadBtn.hide();
29710 this.trashBtn.hide();
29712 if(this.showDownload){
29713 this.downloadBtn.show();
29716 if(this.showTrash){
29717 this.trashBtn.show();
29720 if(!this.showDownload && !this.showTrash) {
29721 this.footerEl.hide();
29726 initial : function()
29728 this.fireEvent('initial', this);
29732 onClick : function(e)
29734 e.preventDefault();
29736 this.fireEvent('click', this);
29739 onDownload : function(e)
29741 e.preventDefault();
29743 this.fireEvent('download', this);
29746 onTrash : function(e)
29748 e.preventDefault();
29750 this.fireEvent('trash', this);
29762 * @class Roo.bootstrap.NavProgressBar
29763 * @extends Roo.bootstrap.Component
29764 * Bootstrap NavProgressBar class
29767 * Create a new nav progress bar
29768 * @param {Object} config The config object
29771 Roo.bootstrap.NavProgressBar = function(config){
29772 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29774 this.bullets = this.bullets || [];
29776 // Roo.bootstrap.NavProgressBar.register(this);
29780 * Fires when the active item changes
29781 * @param {Roo.bootstrap.NavProgressBar} this
29782 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29783 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29790 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29795 getAutoCreate : function()
29797 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29801 cls : 'roo-navigation-bar-group',
29805 cls : 'roo-navigation-top-bar'
29809 cls : 'roo-navigation-bullets-bar',
29813 cls : 'roo-navigation-bar'
29820 cls : 'roo-navigation-bottom-bar'
29830 initEvents: function()
29835 onRender : function(ct, position)
29837 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29839 if(this.bullets.length){
29840 Roo.each(this.bullets, function(b){
29849 addItem : function(cfg)
29851 var item = new Roo.bootstrap.NavProgressItem(cfg);
29853 item.parentId = this.id;
29854 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29857 var top = new Roo.bootstrap.Element({
29859 cls : 'roo-navigation-bar-text'
29862 var bottom = new Roo.bootstrap.Element({
29864 cls : 'roo-navigation-bar-text'
29867 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29868 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29870 var topText = new Roo.bootstrap.Element({
29872 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29875 var bottomText = new Roo.bootstrap.Element({
29877 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29880 topText.onRender(top.el, null);
29881 bottomText.onRender(bottom.el, null);
29884 item.bottomEl = bottom;
29887 this.barItems.push(item);
29892 getActive : function()
29894 var active = false;
29896 Roo.each(this.barItems, function(v){
29898 if (!v.isActive()) {
29910 setActiveItem : function(item)
29914 Roo.each(this.barItems, function(v){
29915 if (v.rid == item.rid) {
29919 if (v.isActive()) {
29920 v.setActive(false);
29925 item.setActive(true);
29927 this.fireEvent('changed', this, item, prev);
29930 getBarItem: function(rid)
29934 Roo.each(this.barItems, function(e) {
29935 if (e.rid != rid) {
29946 indexOfItem : function(item)
29950 Roo.each(this.barItems, function(v, i){
29952 if (v.rid != item.rid) {
29963 setActiveNext : function()
29965 var i = this.indexOfItem(this.getActive());
29967 if (i > this.barItems.length) {
29971 this.setActiveItem(this.barItems[i+1]);
29974 setActivePrev : function()
29976 var i = this.indexOfItem(this.getActive());
29982 this.setActiveItem(this.barItems[i-1]);
29985 format : function()
29987 if(!this.barItems.length){
29991 var width = 100 / this.barItems.length;
29993 Roo.each(this.barItems, function(i){
29994 i.el.setStyle('width', width + '%');
29995 i.topEl.el.setStyle('width', width + '%');
29996 i.bottomEl.el.setStyle('width', width + '%');
30005 * Nav Progress Item
30010 * @class Roo.bootstrap.NavProgressItem
30011 * @extends Roo.bootstrap.Component
30012 * Bootstrap NavProgressItem class
30013 * @cfg {String} rid the reference id
30014 * @cfg {Boolean} active (true|false) Is item active default false
30015 * @cfg {Boolean} disabled (true|false) Is item active default false
30016 * @cfg {String} html
30017 * @cfg {String} position (top|bottom) text position default bottom
30018 * @cfg {String} icon show icon instead of number
30021 * Create a new NavProgressItem
30022 * @param {Object} config The config object
30024 Roo.bootstrap.NavProgressItem = function(config){
30025 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30030 * The raw click event for the entire grid.
30031 * @param {Roo.bootstrap.NavProgressItem} this
30032 * @param {Roo.EventObject} e
30039 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30045 position : 'bottom',
30048 getAutoCreate : function()
30050 var iconCls = 'roo-navigation-bar-item-icon';
30052 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30056 cls: 'roo-navigation-bar-item',
30066 cfg.cls += ' active';
30069 cfg.cls += ' disabled';
30075 disable : function()
30077 this.setDisabled(true);
30080 enable : function()
30082 this.setDisabled(false);
30085 initEvents: function()
30087 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30089 this.iconEl.on('click', this.onClick, this);
30092 onClick : function(e)
30094 e.preventDefault();
30100 if(this.fireEvent('click', this, e) === false){
30104 this.parent().setActiveItem(this);
30107 isActive: function ()
30109 return this.active;
30112 setActive : function(state)
30114 if(this.active == state){
30118 this.active = state;
30121 this.el.addClass('active');
30125 this.el.removeClass('active');
30130 setDisabled : function(state)
30132 if(this.disabled == state){
30136 this.disabled = state;
30139 this.el.addClass('disabled');
30143 this.el.removeClass('disabled');
30146 tooltipEl : function()
30148 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30161 * @class Roo.bootstrap.FieldLabel
30162 * @extends Roo.bootstrap.Component
30163 * Bootstrap FieldLabel class
30164 * @cfg {String} html contents of the element
30165 * @cfg {String} tag tag of the element default label
30166 * @cfg {String} cls class of the element
30167 * @cfg {String} target label target
30168 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30169 * @cfg {String} invalidClass default "text-warning"
30170 * @cfg {String} validClass default "text-success"
30171 * @cfg {String} iconTooltip default "This field is required"
30172 * @cfg {String} indicatorpos (left|right) default left
30175 * Create a new FieldLabel
30176 * @param {Object} config The config object
30179 Roo.bootstrap.FieldLabel = function(config){
30180 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30185 * Fires after the field has been marked as invalid.
30186 * @param {Roo.form.FieldLabel} this
30187 * @param {String} msg The validation message
30192 * Fires after the field has been validated with no errors.
30193 * @param {Roo.form.FieldLabel} this
30199 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30206 invalidClass : 'has-warning',
30207 validClass : 'has-success',
30208 iconTooltip : 'This field is required',
30209 indicatorpos : 'left',
30211 getAutoCreate : function(){
30214 if (!this.allowBlank) {
30220 cls : 'roo-bootstrap-field-label ' + this.cls,
30225 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30226 tooltip : this.iconTooltip
30235 if(this.indicatorpos == 'right'){
30238 cls : 'roo-bootstrap-field-label ' + this.cls,
30247 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30248 tooltip : this.iconTooltip
30257 initEvents: function()
30259 Roo.bootstrap.Element.superclass.initEvents.call(this);
30261 this.indicator = this.indicatorEl();
30263 if(this.indicator){
30264 this.indicator.removeClass('visible');
30265 this.indicator.addClass('invisible');
30268 Roo.bootstrap.FieldLabel.register(this);
30271 indicatorEl : function()
30273 var indicator = this.el.select('i.roo-required-indicator',true).first();
30284 * Mark this field as valid
30286 markValid : function()
30288 if(this.indicator){
30289 this.indicator.removeClass('visible');
30290 this.indicator.addClass('invisible');
30293 this.el.removeClass(this.invalidClass);
30295 this.el.addClass(this.validClass);
30297 this.fireEvent('valid', this);
30301 * Mark this field as invalid
30302 * @param {String} msg The validation message
30304 markInvalid : function(msg)
30306 if(this.indicator){
30307 this.indicator.removeClass('invisible');
30308 this.indicator.addClass('visible');
30311 this.el.removeClass(this.validClass);
30313 this.el.addClass(this.invalidClass);
30315 this.fireEvent('invalid', this, msg);
30321 Roo.apply(Roo.bootstrap.FieldLabel, {
30326 * register a FieldLabel Group
30327 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30329 register : function(label)
30331 if(this.groups.hasOwnProperty(label.target)){
30335 this.groups[label.target] = label;
30339 * fetch a FieldLabel Group based on the target
30340 * @param {string} target
30341 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30343 get: function(target) {
30344 if (typeof(this.groups[target]) == 'undefined') {
30348 return this.groups[target] ;
30357 * page DateSplitField.
30363 * @class Roo.bootstrap.DateSplitField
30364 * @extends Roo.bootstrap.Component
30365 * Bootstrap DateSplitField class
30366 * @cfg {string} fieldLabel - the label associated
30367 * @cfg {Number} labelWidth set the width of label (0-12)
30368 * @cfg {String} labelAlign (top|left)
30369 * @cfg {Boolean} dayAllowBlank (true|false) default false
30370 * @cfg {Boolean} monthAllowBlank (true|false) default false
30371 * @cfg {Boolean} yearAllowBlank (true|false) default false
30372 * @cfg {string} dayPlaceholder
30373 * @cfg {string} monthPlaceholder
30374 * @cfg {string} yearPlaceholder
30375 * @cfg {string} dayFormat default 'd'
30376 * @cfg {string} monthFormat default 'm'
30377 * @cfg {string} yearFormat default 'Y'
30378 * @cfg {Number} labellg set the width of label (1-12)
30379 * @cfg {Number} labelmd set the width of label (1-12)
30380 * @cfg {Number} labelsm set the width of label (1-12)
30381 * @cfg {Number} labelxs set the width of label (1-12)
30385 * Create a new DateSplitField
30386 * @param {Object} config The config object
30389 Roo.bootstrap.DateSplitField = function(config){
30390 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30396 * getting the data of years
30397 * @param {Roo.bootstrap.DateSplitField} this
30398 * @param {Object} years
30403 * getting the data of days
30404 * @param {Roo.bootstrap.DateSplitField} this
30405 * @param {Object} days
30410 * Fires after the field has been marked as invalid.
30411 * @param {Roo.form.Field} this
30412 * @param {String} msg The validation message
30417 * Fires after the field has been validated with no errors.
30418 * @param {Roo.form.Field} this
30424 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30427 labelAlign : 'top',
30429 dayAllowBlank : false,
30430 monthAllowBlank : false,
30431 yearAllowBlank : false,
30432 dayPlaceholder : '',
30433 monthPlaceholder : '',
30434 yearPlaceholder : '',
30438 isFormField : true,
30444 getAutoCreate : function()
30448 cls : 'row roo-date-split-field-group',
30453 cls : 'form-hidden-field roo-date-split-field-group-value',
30459 var labelCls = 'col-md-12';
30460 var contentCls = 'col-md-4';
30462 if(this.fieldLabel){
30466 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30470 html : this.fieldLabel
30475 if(this.labelAlign == 'left'){
30477 if(this.labelWidth > 12){
30478 label.style = "width: " + this.labelWidth + 'px';
30481 if(this.labelWidth < 13 && this.labelmd == 0){
30482 this.labelmd = this.labelWidth;
30485 if(this.labellg > 0){
30486 labelCls = ' col-lg-' + this.labellg;
30487 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30490 if(this.labelmd > 0){
30491 labelCls = ' col-md-' + this.labelmd;
30492 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30495 if(this.labelsm > 0){
30496 labelCls = ' col-sm-' + this.labelsm;
30497 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30500 if(this.labelxs > 0){
30501 labelCls = ' col-xs-' + this.labelxs;
30502 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30506 label.cls += ' ' + labelCls;
30508 cfg.cn.push(label);
30511 Roo.each(['day', 'month', 'year'], function(t){
30514 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30521 inputEl: function ()
30523 return this.el.select('.roo-date-split-field-group-value', true).first();
30526 onRender : function(ct, position)
30530 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30532 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30534 this.dayField = new Roo.bootstrap.ComboBox({
30535 allowBlank : this.dayAllowBlank,
30536 alwaysQuery : true,
30537 displayField : 'value',
30540 forceSelection : true,
30542 placeholder : this.dayPlaceholder,
30543 selectOnFocus : true,
30544 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30545 triggerAction : 'all',
30547 valueField : 'value',
30548 store : new Roo.data.SimpleStore({
30549 data : (function() {
30551 _this.fireEvent('days', _this, days);
30554 fields : [ 'value' ]
30557 select : function (_self, record, index)
30559 _this.setValue(_this.getValue());
30564 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30566 this.monthField = new Roo.bootstrap.MonthField({
30567 after : '<i class=\"fa fa-calendar\"></i>',
30568 allowBlank : this.monthAllowBlank,
30569 placeholder : this.monthPlaceholder,
30572 render : function (_self)
30574 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30575 e.preventDefault();
30579 select : function (_self, oldvalue, newvalue)
30581 _this.setValue(_this.getValue());
30586 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30588 this.yearField = new Roo.bootstrap.ComboBox({
30589 allowBlank : this.yearAllowBlank,
30590 alwaysQuery : true,
30591 displayField : 'value',
30594 forceSelection : true,
30596 placeholder : this.yearPlaceholder,
30597 selectOnFocus : true,
30598 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30599 triggerAction : 'all',
30601 valueField : 'value',
30602 store : new Roo.data.SimpleStore({
30603 data : (function() {
30605 _this.fireEvent('years', _this, years);
30608 fields : [ 'value' ]
30611 select : function (_self, record, index)
30613 _this.setValue(_this.getValue());
30618 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30621 setValue : function(v, format)
30623 this.inputEl.dom.value = v;
30625 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30627 var d = Date.parseDate(v, f);
30634 this.setDay(d.format(this.dayFormat));
30635 this.setMonth(d.format(this.monthFormat));
30636 this.setYear(d.format(this.yearFormat));
30643 setDay : function(v)
30645 this.dayField.setValue(v);
30646 this.inputEl.dom.value = this.getValue();
30651 setMonth : function(v)
30653 this.monthField.setValue(v, true);
30654 this.inputEl.dom.value = this.getValue();
30659 setYear : function(v)
30661 this.yearField.setValue(v);
30662 this.inputEl.dom.value = this.getValue();
30667 getDay : function()
30669 return this.dayField.getValue();
30672 getMonth : function()
30674 return this.monthField.getValue();
30677 getYear : function()
30679 return this.yearField.getValue();
30682 getValue : function()
30684 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30686 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30696 this.inputEl.dom.value = '';
30701 validate : function()
30703 var d = this.dayField.validate();
30704 var m = this.monthField.validate();
30705 var y = this.yearField.validate();
30710 (!this.dayAllowBlank && !d) ||
30711 (!this.monthAllowBlank && !m) ||
30712 (!this.yearAllowBlank && !y)
30717 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30726 this.markInvalid();
30731 markValid : function()
30734 var label = this.el.select('label', true).first();
30735 var icon = this.el.select('i.fa-star', true).first();
30741 this.fireEvent('valid', this);
30745 * Mark this field as invalid
30746 * @param {String} msg The validation message
30748 markInvalid : function(msg)
30751 var label = this.el.select('label', true).first();
30752 var icon = this.el.select('i.fa-star', true).first();
30754 if(label && !icon){
30755 this.el.select('.roo-date-split-field-label', true).createChild({
30757 cls : 'text-danger fa fa-lg fa-star',
30758 tooltip : 'This field is required',
30759 style : 'margin-right:5px;'
30763 this.fireEvent('invalid', this, msg);
30766 clearInvalid : function()
30768 var label = this.el.select('label', true).first();
30769 var icon = this.el.select('i.fa-star', true).first();
30775 this.fireEvent('valid', this);
30778 getName: function()
30788 * http://masonry.desandro.com
30790 * The idea is to render all the bricks based on vertical width...
30792 * The original code extends 'outlayer' - we might need to use that....
30798 * @class Roo.bootstrap.LayoutMasonry
30799 * @extends Roo.bootstrap.Component
30800 * Bootstrap Layout Masonry class
30803 * Create a new Element
30804 * @param {Object} config The config object
30807 Roo.bootstrap.LayoutMasonry = function(config){
30809 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30813 Roo.bootstrap.LayoutMasonry.register(this);
30819 * Fire after layout the items
30820 * @param {Roo.bootstrap.LayoutMasonry} this
30821 * @param {Roo.EventObject} e
30828 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30831 * @cfg {Boolean} isLayoutInstant = no animation?
30833 isLayoutInstant : false, // needed?
30836 * @cfg {Number} boxWidth width of the columns
30841 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30846 * @cfg {Number} padWidth padding below box..
30851 * @cfg {Number} gutter gutter width..
30856 * @cfg {Number} maxCols maximum number of columns
30862 * @cfg {Boolean} isAutoInitial defalut true
30864 isAutoInitial : true,
30869 * @cfg {Boolean} isHorizontal defalut false
30871 isHorizontal : false,
30873 currentSize : null,
30879 bricks: null, //CompositeElement
30883 _isLayoutInited : false,
30885 // isAlternative : false, // only use for vertical layout...
30888 * @cfg {Number} alternativePadWidth padding below box..
30890 alternativePadWidth : 50,
30892 selectedBrick : [],
30894 getAutoCreate : function(){
30896 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30900 cls: 'blog-masonary-wrapper ' + this.cls,
30902 cls : 'mas-boxes masonary'
30909 getChildContainer: function( )
30911 if (this.boxesEl) {
30912 return this.boxesEl;
30915 this.boxesEl = this.el.select('.mas-boxes').first();
30917 return this.boxesEl;
30921 initEvents : function()
30925 if(this.isAutoInitial){
30926 Roo.log('hook children rendered');
30927 this.on('childrenrendered', function() {
30928 Roo.log('children rendered');
30934 initial : function()
30936 this.selectedBrick = [];
30938 this.currentSize = this.el.getBox(true);
30940 Roo.EventManager.onWindowResize(this.resize, this);
30942 if(!this.isAutoInitial){
30950 //this.layout.defer(500,this);
30954 resize : function()
30956 var cs = this.el.getBox(true);
30959 this.currentSize.width == cs.width &&
30960 this.currentSize.x == cs.x &&
30961 this.currentSize.height == cs.height &&
30962 this.currentSize.y == cs.y
30964 Roo.log("no change in with or X or Y");
30968 this.currentSize = cs;
30974 layout : function()
30976 this._resetLayout();
30978 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30980 this.layoutItems( isInstant );
30982 this._isLayoutInited = true;
30984 this.fireEvent('layout', this);
30988 _resetLayout : function()
30990 if(this.isHorizontal){
30991 this.horizontalMeasureColumns();
30995 this.verticalMeasureColumns();
30999 verticalMeasureColumns : function()
31001 this.getContainerWidth();
31003 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31004 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31008 var boxWidth = this.boxWidth + this.padWidth;
31010 if(this.containerWidth < this.boxWidth){
31011 boxWidth = this.containerWidth
31014 var containerWidth = this.containerWidth;
31016 var cols = Math.floor(containerWidth / boxWidth);
31018 this.cols = Math.max( cols, 1 );
31020 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31022 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31024 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31026 this.colWidth = boxWidth + avail - this.padWidth;
31028 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31029 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31032 horizontalMeasureColumns : function()
31034 this.getContainerWidth();
31036 var boxWidth = this.boxWidth;
31038 if(this.containerWidth < boxWidth){
31039 boxWidth = this.containerWidth;
31042 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31044 this.el.setHeight(boxWidth);
31048 getContainerWidth : function()
31050 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31053 layoutItems : function( isInstant )
31055 Roo.log(this.bricks);
31057 var items = Roo.apply([], this.bricks);
31059 if(this.isHorizontal){
31060 this._horizontalLayoutItems( items , isInstant );
31064 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31065 // this._verticalAlternativeLayoutItems( items , isInstant );
31069 this._verticalLayoutItems( items , isInstant );
31073 _verticalLayoutItems : function ( items , isInstant)
31075 if ( !items || !items.length ) {
31080 ['xs', 'xs', 'xs', 'tall'],
31081 ['xs', 'xs', 'tall'],
31082 ['xs', 'xs', 'sm'],
31083 ['xs', 'xs', 'xs'],
31089 ['sm', 'xs', 'xs'],
31093 ['tall', 'xs', 'xs', 'xs'],
31094 ['tall', 'xs', 'xs'],
31106 Roo.each(items, function(item, k){
31108 switch (item.size) {
31109 // these layouts take up a full box,
31120 boxes.push([item]);
31143 var filterPattern = function(box, length)
31151 var pattern = box.slice(0, length);
31155 Roo.each(pattern, function(i){
31156 format.push(i.size);
31159 Roo.each(standard, function(s){
31161 if(String(s) != String(format)){
31170 if(!match && length == 1){
31175 filterPattern(box, length - 1);
31179 queue.push(pattern);
31181 box = box.slice(length, box.length);
31183 filterPattern(box, 4);
31189 Roo.each(boxes, function(box, k){
31195 if(box.length == 1){
31200 filterPattern(box, 4);
31204 this._processVerticalLayoutQueue( queue, isInstant );
31208 // _verticalAlternativeLayoutItems : function( items , isInstant )
31210 // if ( !items || !items.length ) {
31214 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31218 _horizontalLayoutItems : function ( items , isInstant)
31220 if ( !items || !items.length || items.length < 3) {
31226 var eItems = items.slice(0, 3);
31228 items = items.slice(3, items.length);
31231 ['xs', 'xs', 'xs', 'wide'],
31232 ['xs', 'xs', 'wide'],
31233 ['xs', 'xs', 'sm'],
31234 ['xs', 'xs', 'xs'],
31240 ['sm', 'xs', 'xs'],
31244 ['wide', 'xs', 'xs', 'xs'],
31245 ['wide', 'xs', 'xs'],
31258 Roo.each(items, function(item, k){
31260 switch (item.size) {
31271 boxes.push([item]);
31295 var filterPattern = function(box, length)
31303 var pattern = box.slice(0, length);
31307 Roo.each(pattern, function(i){
31308 format.push(i.size);
31311 Roo.each(standard, function(s){
31313 if(String(s) != String(format)){
31322 if(!match && length == 1){
31327 filterPattern(box, length - 1);
31331 queue.push(pattern);
31333 box = box.slice(length, box.length);
31335 filterPattern(box, 4);
31341 Roo.each(boxes, function(box, k){
31347 if(box.length == 1){
31352 filterPattern(box, 4);
31359 var pos = this.el.getBox(true);
31363 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31365 var hit_end = false;
31367 Roo.each(queue, function(box){
31371 Roo.each(box, function(b){
31373 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31383 Roo.each(box, function(b){
31385 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31388 mx = Math.max(mx, b.x);
31392 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31396 Roo.each(box, function(b){
31398 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31412 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31415 /** Sets position of item in DOM
31416 * @param {Element} item
31417 * @param {Number} x - horizontal position
31418 * @param {Number} y - vertical position
31419 * @param {Boolean} isInstant - disables transitions
31421 _processVerticalLayoutQueue : function( queue, isInstant )
31423 var pos = this.el.getBox(true);
31428 for (var i = 0; i < this.cols; i++){
31432 Roo.each(queue, function(box, k){
31434 var col = k % this.cols;
31436 Roo.each(box, function(b,kk){
31438 b.el.position('absolute');
31440 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31441 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31443 if(b.size == 'md-left' || b.size == 'md-right'){
31444 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31445 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31448 b.el.setWidth(width);
31449 b.el.setHeight(height);
31451 b.el.select('iframe',true).setSize(width,height);
31455 for (var i = 0; i < this.cols; i++){
31457 if(maxY[i] < maxY[col]){
31462 col = Math.min(col, i);
31466 x = pos.x + col * (this.colWidth + this.padWidth);
31470 var positions = [];
31472 switch (box.length){
31474 positions = this.getVerticalOneBoxColPositions(x, y, box);
31477 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31480 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31483 positions = this.getVerticalFourBoxColPositions(x, y, box);
31489 Roo.each(box, function(b,kk){
31491 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31493 var sz = b.el.getSize();
31495 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31503 for (var i = 0; i < this.cols; i++){
31504 mY = Math.max(mY, maxY[i]);
31507 this.el.setHeight(mY - pos.y);
31511 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31513 // var pos = this.el.getBox(true);
31516 // var maxX = pos.right;
31518 // var maxHeight = 0;
31520 // Roo.each(items, function(item, k){
31524 // item.el.position('absolute');
31526 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31528 // item.el.setWidth(width);
31530 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31532 // item.el.setHeight(height);
31535 // item.el.setXY([x, y], isInstant ? false : true);
31537 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31540 // y = y + height + this.alternativePadWidth;
31542 // maxHeight = maxHeight + height + this.alternativePadWidth;
31546 // this.el.setHeight(maxHeight);
31550 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31552 var pos = this.el.getBox(true);
31557 var maxX = pos.right;
31559 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31561 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31563 Roo.each(queue, function(box, k){
31565 Roo.each(box, function(b, kk){
31567 b.el.position('absolute');
31569 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31570 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31572 if(b.size == 'md-left' || b.size == 'md-right'){
31573 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31574 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31577 b.el.setWidth(width);
31578 b.el.setHeight(height);
31586 var positions = [];
31588 switch (box.length){
31590 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31593 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31596 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31599 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31605 Roo.each(box, function(b,kk){
31607 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31609 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31617 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31619 Roo.each(eItems, function(b,k){
31621 b.size = (k == 0) ? 'sm' : 'xs';
31622 b.x = (k == 0) ? 2 : 1;
31623 b.y = (k == 0) ? 2 : 1;
31625 b.el.position('absolute');
31627 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31629 b.el.setWidth(width);
31631 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31633 b.el.setHeight(height);
31637 var positions = [];
31640 x : maxX - this.unitWidth * 2 - this.gutter,
31645 x : maxX - this.unitWidth,
31646 y : minY + (this.unitWidth + this.gutter) * 2
31650 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31654 Roo.each(eItems, function(b,k){
31656 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31662 getVerticalOneBoxColPositions : function(x, y, box)
31666 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31668 if(box[0].size == 'md-left'){
31672 if(box[0].size == 'md-right'){
31677 x : x + (this.unitWidth + this.gutter) * rand,
31684 getVerticalTwoBoxColPositions : function(x, y, box)
31688 if(box[0].size == 'xs'){
31692 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31696 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31710 x : x + (this.unitWidth + this.gutter) * 2,
31711 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31718 getVerticalThreeBoxColPositions : function(x, y, box)
31722 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31730 x : x + (this.unitWidth + this.gutter) * 1,
31735 x : x + (this.unitWidth + this.gutter) * 2,
31743 if(box[0].size == 'xs' && box[1].size == 'xs'){
31752 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31756 x : x + (this.unitWidth + this.gutter) * 1,
31770 x : x + (this.unitWidth + this.gutter) * 2,
31775 x : x + (this.unitWidth + this.gutter) * 2,
31776 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31783 getVerticalFourBoxColPositions : function(x, y, box)
31787 if(box[0].size == 'xs'){
31796 y : y + (this.unitHeight + this.gutter) * 1
31801 y : y + (this.unitHeight + this.gutter) * 2
31805 x : x + (this.unitWidth + this.gutter) * 1,
31819 x : x + (this.unitWidth + this.gutter) * 2,
31824 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31825 y : y + (this.unitHeight + this.gutter) * 1
31829 x : x + (this.unitWidth + this.gutter) * 2,
31830 y : y + (this.unitWidth + this.gutter) * 2
31837 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31841 if(box[0].size == 'md-left'){
31843 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31850 if(box[0].size == 'md-right'){
31852 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31853 y : minY + (this.unitWidth + this.gutter) * 1
31859 var rand = Math.floor(Math.random() * (4 - box[0].y));
31862 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31863 y : minY + (this.unitWidth + this.gutter) * rand
31870 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31874 if(box[0].size == 'xs'){
31877 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31882 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31883 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31891 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31896 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31897 y : minY + (this.unitWidth + this.gutter) * 2
31904 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31908 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31911 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31916 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31917 y : minY + (this.unitWidth + this.gutter) * 1
31921 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31922 y : minY + (this.unitWidth + this.gutter) * 2
31929 if(box[0].size == 'xs' && box[1].size == 'xs'){
31932 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31937 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31942 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31943 y : minY + (this.unitWidth + this.gutter) * 1
31951 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31956 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31957 y : minY + (this.unitWidth + this.gutter) * 2
31961 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31962 y : minY + (this.unitWidth + this.gutter) * 2
31969 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31973 if(box[0].size == 'xs'){
31976 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31981 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31986 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),
31991 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31992 y : minY + (this.unitWidth + this.gutter) * 1
32000 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32005 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32006 y : minY + (this.unitWidth + this.gutter) * 2
32010 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32011 y : minY + (this.unitWidth + this.gutter) * 2
32015 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),
32016 y : minY + (this.unitWidth + this.gutter) * 2
32024 * remove a Masonry Brick
32025 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32027 removeBrick : function(brick_id)
32033 for (var i = 0; i<this.bricks.length; i++) {
32034 if (this.bricks[i].id == brick_id) {
32035 this.bricks.splice(i,1);
32036 this.el.dom.removeChild(Roo.get(brick_id).dom);
32043 * adds a Masonry Brick
32044 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32046 addBrick : function(cfg)
32048 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32049 //this.register(cn);
32050 cn.parentId = this.id;
32051 cn.render(this.el);
32056 * register a Masonry Brick
32057 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32060 register : function(brick)
32062 this.bricks.push(brick);
32063 brick.masonryId = this.id;
32067 * clear all the Masonry Brick
32069 clearAll : function()
32072 //this.getChildContainer().dom.innerHTML = "";
32073 this.el.dom.innerHTML = '';
32076 getSelected : function()
32078 if (!this.selectedBrick) {
32082 return this.selectedBrick;
32086 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32090 * register a Masonry Layout
32091 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32094 register : function(layout)
32096 this.groups[layout.id] = layout;
32099 * fetch a Masonry Layout based on the masonry layout ID
32100 * @param {string} the masonry layout to add
32101 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32104 get: function(layout_id) {
32105 if (typeof(this.groups[layout_id]) == 'undefined') {
32108 return this.groups[layout_id] ;
32120 * http://masonry.desandro.com
32122 * The idea is to render all the bricks based on vertical width...
32124 * The original code extends 'outlayer' - we might need to use that....
32130 * @class Roo.bootstrap.LayoutMasonryAuto
32131 * @extends Roo.bootstrap.Component
32132 * Bootstrap Layout Masonry class
32135 * Create a new Element
32136 * @param {Object} config The config object
32139 Roo.bootstrap.LayoutMasonryAuto = function(config){
32140 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32143 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32146 * @cfg {Boolean} isFitWidth - resize the width..
32148 isFitWidth : false, // options..
32150 * @cfg {Boolean} isOriginLeft = left align?
32152 isOriginLeft : true,
32154 * @cfg {Boolean} isOriginTop = top align?
32156 isOriginTop : false,
32158 * @cfg {Boolean} isLayoutInstant = no animation?
32160 isLayoutInstant : false, // needed?
32162 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32164 isResizingContainer : true,
32166 * @cfg {Number} columnWidth width of the columns
32172 * @cfg {Number} maxCols maximum number of columns
32177 * @cfg {Number} padHeight padding below box..
32183 * @cfg {Boolean} isAutoInitial defalut true
32186 isAutoInitial : true,
32192 initialColumnWidth : 0,
32193 currentSize : null,
32195 colYs : null, // array.
32202 bricks: null, //CompositeElement
32203 cols : 0, // array?
32204 // element : null, // wrapped now this.el
32205 _isLayoutInited : null,
32208 getAutoCreate : function(){
32212 cls: 'blog-masonary-wrapper ' + this.cls,
32214 cls : 'mas-boxes masonary'
32221 getChildContainer: function( )
32223 if (this.boxesEl) {
32224 return this.boxesEl;
32227 this.boxesEl = this.el.select('.mas-boxes').first();
32229 return this.boxesEl;
32233 initEvents : function()
32237 if(this.isAutoInitial){
32238 Roo.log('hook children rendered');
32239 this.on('childrenrendered', function() {
32240 Roo.log('children rendered');
32247 initial : function()
32249 this.reloadItems();
32251 this.currentSize = this.el.getBox(true);
32253 /// was window resize... - let's see if this works..
32254 Roo.EventManager.onWindowResize(this.resize, this);
32256 if(!this.isAutoInitial){
32261 this.layout.defer(500,this);
32264 reloadItems: function()
32266 this.bricks = this.el.select('.masonry-brick', true);
32268 this.bricks.each(function(b) {
32269 //Roo.log(b.getSize());
32270 if (!b.attr('originalwidth')) {
32271 b.attr('originalwidth', b.getSize().width);
32276 Roo.log(this.bricks.elements.length);
32279 resize : function()
32282 var cs = this.el.getBox(true);
32284 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32285 Roo.log("no change in with or X");
32288 this.currentSize = cs;
32292 layout : function()
32295 this._resetLayout();
32296 //this._manageStamps();
32298 // don't animate first layout
32299 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32300 this.layoutItems( isInstant );
32302 // flag for initalized
32303 this._isLayoutInited = true;
32306 layoutItems : function( isInstant )
32308 //var items = this._getItemsForLayout( this.items );
32309 // original code supports filtering layout items.. we just ignore it..
32311 this._layoutItems( this.bricks , isInstant );
32313 this._postLayout();
32315 _layoutItems : function ( items , isInstant)
32317 //this.fireEvent( 'layout', this, items );
32320 if ( !items || !items.elements.length ) {
32321 // no items, emit event with empty array
32326 items.each(function(item) {
32327 Roo.log("layout item");
32329 // get x/y object from method
32330 var position = this._getItemLayoutPosition( item );
32332 position.item = item;
32333 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32334 queue.push( position );
32337 this._processLayoutQueue( queue );
32339 /** Sets position of item in DOM
32340 * @param {Element} item
32341 * @param {Number} x - horizontal position
32342 * @param {Number} y - vertical position
32343 * @param {Boolean} isInstant - disables transitions
32345 _processLayoutQueue : function( queue )
32347 for ( var i=0, len = queue.length; i < len; i++ ) {
32348 var obj = queue[i];
32349 obj.item.position('absolute');
32350 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32356 * Any logic you want to do after each layout,
32357 * i.e. size the container
32359 _postLayout : function()
32361 this.resizeContainer();
32364 resizeContainer : function()
32366 if ( !this.isResizingContainer ) {
32369 var size = this._getContainerSize();
32371 this.el.setSize(size.width,size.height);
32372 this.boxesEl.setSize(size.width,size.height);
32378 _resetLayout : function()
32380 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32381 this.colWidth = this.el.getWidth();
32382 //this.gutter = this.el.getWidth();
32384 this.measureColumns();
32390 this.colYs.push( 0 );
32396 measureColumns : function()
32398 this.getContainerWidth();
32399 // if columnWidth is 0, default to outerWidth of first item
32400 if ( !this.columnWidth ) {
32401 var firstItem = this.bricks.first();
32402 Roo.log(firstItem);
32403 this.columnWidth = this.containerWidth;
32404 if (firstItem && firstItem.attr('originalwidth') ) {
32405 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32407 // columnWidth fall back to item of first element
32408 Roo.log("set column width?");
32409 this.initialColumnWidth = this.columnWidth ;
32411 // if first elem has no width, default to size of container
32416 if (this.initialColumnWidth) {
32417 this.columnWidth = this.initialColumnWidth;
32422 // column width is fixed at the top - however if container width get's smaller we should
32425 // this bit calcs how man columns..
32427 var columnWidth = this.columnWidth += this.gutter;
32429 // calculate columns
32430 var containerWidth = this.containerWidth + this.gutter;
32432 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32433 // fix rounding errors, typically with gutters
32434 var excess = columnWidth - containerWidth % columnWidth;
32437 // if overshoot is less than a pixel, round up, otherwise floor it
32438 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32439 cols = Math[ mathMethod ]( cols );
32440 this.cols = Math.max( cols, 1 );
32441 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32443 // padding positioning..
32444 var totalColWidth = this.cols * this.columnWidth;
32445 var padavail = this.containerWidth - totalColWidth;
32446 // so for 2 columns - we need 3 'pads'
32448 var padNeeded = (1+this.cols) * this.padWidth;
32450 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32452 this.columnWidth += padExtra
32453 //this.padWidth = Math.floor(padavail / ( this.cols));
32455 // adjust colum width so that padding is fixed??
32457 // we have 3 columns ... total = width * 3
32458 // we have X left over... that should be used by
32460 //if (this.expandC) {
32468 getContainerWidth : function()
32470 /* // container is parent if fit width
32471 var container = this.isFitWidth ? this.element.parentNode : this.element;
32472 // check that this.size and size are there
32473 // IE8 triggers resize on body size change, so they might not be
32475 var size = getSize( container ); //FIXME
32476 this.containerWidth = size && size.innerWidth; //FIXME
32479 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32483 _getItemLayoutPosition : function( item ) // what is item?
32485 // we resize the item to our columnWidth..
32487 item.setWidth(this.columnWidth);
32488 item.autoBoxAdjust = false;
32490 var sz = item.getSize();
32492 // how many columns does this brick span
32493 var remainder = this.containerWidth % this.columnWidth;
32495 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32496 // round if off by 1 pixel, otherwise use ceil
32497 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32498 colSpan = Math.min( colSpan, this.cols );
32500 // normally this should be '1' as we dont' currently allow multi width columns..
32502 var colGroup = this._getColGroup( colSpan );
32503 // get the minimum Y value from the columns
32504 var minimumY = Math.min.apply( Math, colGroup );
32505 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32507 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32509 // position the brick
32511 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32512 y: this.currentSize.y + minimumY + this.padHeight
32516 // apply setHeight to necessary columns
32517 var setHeight = minimumY + sz.height + this.padHeight;
32518 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32520 var setSpan = this.cols + 1 - colGroup.length;
32521 for ( var i = 0; i < setSpan; i++ ) {
32522 this.colYs[ shortColIndex + i ] = setHeight ;
32529 * @param {Number} colSpan - number of columns the element spans
32530 * @returns {Array} colGroup
32532 _getColGroup : function( colSpan )
32534 if ( colSpan < 2 ) {
32535 // if brick spans only one column, use all the column Ys
32540 // how many different places could this brick fit horizontally
32541 var groupCount = this.cols + 1 - colSpan;
32542 // for each group potential horizontal position
32543 for ( var i = 0; i < groupCount; i++ ) {
32544 // make an array of colY values for that one group
32545 var groupColYs = this.colYs.slice( i, i + colSpan );
32546 // and get the max value of the array
32547 colGroup[i] = Math.max.apply( Math, groupColYs );
32552 _manageStamp : function( stamp )
32554 var stampSize = stamp.getSize();
32555 var offset = stamp.getBox();
32556 // get the columns that this stamp affects
32557 var firstX = this.isOriginLeft ? offset.x : offset.right;
32558 var lastX = firstX + stampSize.width;
32559 var firstCol = Math.floor( firstX / this.columnWidth );
32560 firstCol = Math.max( 0, firstCol );
32562 var lastCol = Math.floor( lastX / this.columnWidth );
32563 // lastCol should not go over if multiple of columnWidth #425
32564 lastCol -= lastX % this.columnWidth ? 0 : 1;
32565 lastCol = Math.min( this.cols - 1, lastCol );
32567 // set colYs to bottom of the stamp
32568 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32571 for ( var i = firstCol; i <= lastCol; i++ ) {
32572 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32577 _getContainerSize : function()
32579 this.maxY = Math.max.apply( Math, this.colYs );
32584 if ( this.isFitWidth ) {
32585 size.width = this._getContainerFitWidth();
32591 _getContainerFitWidth : function()
32593 var unusedCols = 0;
32594 // count unused columns
32597 if ( this.colYs[i] !== 0 ) {
32602 // fit container to columns that have been used
32603 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32606 needsResizeLayout : function()
32608 var previousWidth = this.containerWidth;
32609 this.getContainerWidth();
32610 return previousWidth !== this.containerWidth;
32625 * @class Roo.bootstrap.MasonryBrick
32626 * @extends Roo.bootstrap.Component
32627 * Bootstrap MasonryBrick class
32630 * Create a new MasonryBrick
32631 * @param {Object} config The config object
32634 Roo.bootstrap.MasonryBrick = function(config){
32636 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32638 Roo.bootstrap.MasonryBrick.register(this);
32644 * When a MasonryBrick is clcik
32645 * @param {Roo.bootstrap.MasonryBrick} this
32646 * @param {Roo.EventObject} e
32652 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32655 * @cfg {String} title
32659 * @cfg {String} html
32663 * @cfg {String} bgimage
32667 * @cfg {String} videourl
32671 * @cfg {String} cls
32675 * @cfg {String} href
32679 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32684 * @cfg {String} placetitle (center|bottom)
32689 * @cfg {Boolean} isFitContainer defalut true
32691 isFitContainer : true,
32694 * @cfg {Boolean} preventDefault defalut false
32696 preventDefault : false,
32699 * @cfg {Boolean} inverse defalut false
32701 maskInverse : false,
32703 getAutoCreate : function()
32705 if(!this.isFitContainer){
32706 return this.getSplitAutoCreate();
32709 var cls = 'masonry-brick masonry-brick-full';
32711 if(this.href.length){
32712 cls += ' masonry-brick-link';
32715 if(this.bgimage.length){
32716 cls += ' masonry-brick-image';
32719 if(this.maskInverse){
32720 cls += ' mask-inverse';
32723 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32724 cls += ' enable-mask';
32728 cls += ' masonry-' + this.size + '-brick';
32731 if(this.placetitle.length){
32733 switch (this.placetitle) {
32735 cls += ' masonry-center-title';
32738 cls += ' masonry-bottom-title';
32745 if(!this.html.length && !this.bgimage.length){
32746 cls += ' masonry-center-title';
32749 if(!this.html.length && this.bgimage.length){
32750 cls += ' masonry-bottom-title';
32755 cls += ' ' + this.cls;
32759 tag: (this.href.length) ? 'a' : 'div',
32764 cls: 'masonry-brick-mask'
32768 cls: 'masonry-brick-paragraph',
32774 if(this.href.length){
32775 cfg.href = this.href;
32778 var cn = cfg.cn[1].cn;
32780 if(this.title.length){
32783 cls: 'masonry-brick-title',
32788 if(this.html.length){
32791 cls: 'masonry-brick-text',
32796 if (!this.title.length && !this.html.length) {
32797 cfg.cn[1].cls += ' hide';
32800 if(this.bgimage.length){
32803 cls: 'masonry-brick-image-view',
32808 if(this.videourl.length){
32809 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32810 // youtube support only?
32813 cls: 'masonry-brick-image-view',
32816 allowfullscreen : true
32824 getSplitAutoCreate : function()
32826 var cls = 'masonry-brick masonry-brick-split';
32828 if(this.href.length){
32829 cls += ' masonry-brick-link';
32832 if(this.bgimage.length){
32833 cls += ' masonry-brick-image';
32837 cls += ' masonry-' + this.size + '-brick';
32840 switch (this.placetitle) {
32842 cls += ' masonry-center-title';
32845 cls += ' masonry-bottom-title';
32848 if(!this.bgimage.length){
32849 cls += ' masonry-center-title';
32852 if(this.bgimage.length){
32853 cls += ' masonry-bottom-title';
32859 cls += ' ' + this.cls;
32863 tag: (this.href.length) ? 'a' : 'div',
32868 cls: 'masonry-brick-split-head',
32872 cls: 'masonry-brick-paragraph',
32879 cls: 'masonry-brick-split-body',
32885 if(this.href.length){
32886 cfg.href = this.href;
32889 if(this.title.length){
32890 cfg.cn[0].cn[0].cn.push({
32892 cls: 'masonry-brick-title',
32897 if(this.html.length){
32898 cfg.cn[1].cn.push({
32900 cls: 'masonry-brick-text',
32905 if(this.bgimage.length){
32906 cfg.cn[0].cn.push({
32908 cls: 'masonry-brick-image-view',
32913 if(this.videourl.length){
32914 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32915 // youtube support only?
32916 cfg.cn[0].cn.cn.push({
32918 cls: 'masonry-brick-image-view',
32921 allowfullscreen : true
32928 initEvents: function()
32930 switch (this.size) {
32963 this.el.on('touchstart', this.onTouchStart, this);
32964 this.el.on('touchmove', this.onTouchMove, this);
32965 this.el.on('touchend', this.onTouchEnd, this);
32966 this.el.on('contextmenu', this.onContextMenu, this);
32968 this.el.on('mouseenter' ,this.enter, this);
32969 this.el.on('mouseleave', this.leave, this);
32970 this.el.on('click', this.onClick, this);
32973 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32974 this.parent().bricks.push(this);
32979 onClick: function(e, el)
32981 var time = this.endTimer - this.startTimer;
32982 // Roo.log(e.preventDefault());
32985 e.preventDefault();
32990 if(!this.preventDefault){
32994 e.preventDefault();
32996 if (this.activeClass != '') {
32997 this.selectBrick();
33000 this.fireEvent('click', this, e);
33003 enter: function(e, el)
33005 e.preventDefault();
33007 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33011 if(this.bgimage.length && this.html.length){
33012 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33016 leave: function(e, el)
33018 e.preventDefault();
33020 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33024 if(this.bgimage.length && this.html.length){
33025 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33029 onTouchStart: function(e, el)
33031 // e.preventDefault();
33033 this.touchmoved = false;
33035 if(!this.isFitContainer){
33039 if(!this.bgimage.length || !this.html.length){
33043 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33045 this.timer = new Date().getTime();
33049 onTouchMove: function(e, el)
33051 this.touchmoved = true;
33054 onContextMenu : function(e,el)
33056 e.preventDefault();
33057 e.stopPropagation();
33061 onTouchEnd: function(e, el)
33063 // e.preventDefault();
33065 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33072 if(!this.bgimage.length || !this.html.length){
33074 if(this.href.length){
33075 window.location.href = this.href;
33081 if(!this.isFitContainer){
33085 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33087 window.location.href = this.href;
33090 //selection on single brick only
33091 selectBrick : function() {
33093 if (!this.parentId) {
33097 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33098 var index = m.selectedBrick.indexOf(this.id);
33101 m.selectedBrick.splice(index,1);
33102 this.el.removeClass(this.activeClass);
33106 for(var i = 0; i < m.selectedBrick.length; i++) {
33107 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33108 b.el.removeClass(b.activeClass);
33111 m.selectedBrick = [];
33113 m.selectedBrick.push(this.id);
33114 this.el.addClass(this.activeClass);
33118 isSelected : function(){
33119 return this.el.hasClass(this.activeClass);
33124 Roo.apply(Roo.bootstrap.MasonryBrick, {
33127 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33129 * register a Masonry Brick
33130 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33133 register : function(brick)
33135 //this.groups[brick.id] = brick;
33136 this.groups.add(brick.id, brick);
33139 * fetch a masonry brick based on the masonry brick ID
33140 * @param {string} the masonry brick to add
33141 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33144 get: function(brick_id)
33146 // if (typeof(this.groups[brick_id]) == 'undefined') {
33149 // return this.groups[brick_id] ;
33151 if(this.groups.key(brick_id)) {
33152 return this.groups.key(brick_id);
33170 * @class Roo.bootstrap.Brick
33171 * @extends Roo.bootstrap.Component
33172 * Bootstrap Brick class
33175 * Create a new Brick
33176 * @param {Object} config The config object
33179 Roo.bootstrap.Brick = function(config){
33180 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33186 * When a Brick is click
33187 * @param {Roo.bootstrap.Brick} this
33188 * @param {Roo.EventObject} e
33194 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33197 * @cfg {String} title
33201 * @cfg {String} html
33205 * @cfg {String} bgimage
33209 * @cfg {String} cls
33213 * @cfg {String} href
33217 * @cfg {String} video
33221 * @cfg {Boolean} square
33225 getAutoCreate : function()
33227 var cls = 'roo-brick';
33229 if(this.href.length){
33230 cls += ' roo-brick-link';
33233 if(this.bgimage.length){
33234 cls += ' roo-brick-image';
33237 if(!this.html.length && !this.bgimage.length){
33238 cls += ' roo-brick-center-title';
33241 if(!this.html.length && this.bgimage.length){
33242 cls += ' roo-brick-bottom-title';
33246 cls += ' ' + this.cls;
33250 tag: (this.href.length) ? 'a' : 'div',
33255 cls: 'roo-brick-paragraph',
33261 if(this.href.length){
33262 cfg.href = this.href;
33265 var cn = cfg.cn[0].cn;
33267 if(this.title.length){
33270 cls: 'roo-brick-title',
33275 if(this.html.length){
33278 cls: 'roo-brick-text',
33285 if(this.bgimage.length){
33288 cls: 'roo-brick-image-view',
33296 initEvents: function()
33298 if(this.title.length || this.html.length){
33299 this.el.on('mouseenter' ,this.enter, this);
33300 this.el.on('mouseleave', this.leave, this);
33303 Roo.EventManager.onWindowResize(this.resize, this);
33305 if(this.bgimage.length){
33306 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33307 this.imageEl.on('load', this.onImageLoad, this);
33314 onImageLoad : function()
33319 resize : function()
33321 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33323 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33325 if(this.bgimage.length){
33326 var image = this.el.select('.roo-brick-image-view', true).first();
33328 image.setWidth(paragraph.getWidth());
33331 image.setHeight(paragraph.getWidth());
33334 this.el.setHeight(image.getHeight());
33335 paragraph.setHeight(image.getHeight());
33341 enter: function(e, el)
33343 e.preventDefault();
33345 if(this.bgimage.length){
33346 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33347 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33351 leave: function(e, el)
33353 e.preventDefault();
33355 if(this.bgimage.length){
33356 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33357 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33372 * @class Roo.bootstrap.NumberField
33373 * @extends Roo.bootstrap.Input
33374 * Bootstrap NumberField class
33380 * Create a new NumberField
33381 * @param {Object} config The config object
33384 Roo.bootstrap.NumberField = function(config){
33385 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33388 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33391 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33393 allowDecimals : true,
33395 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33397 decimalSeparator : ".",
33399 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33401 decimalPrecision : 2,
33403 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33405 allowNegative : true,
33408 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33412 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33414 minValue : Number.NEGATIVE_INFINITY,
33416 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33418 maxValue : Number.MAX_VALUE,
33420 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33422 minText : "The minimum value for this field is {0}",
33424 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33426 maxText : "The maximum value for this field is {0}",
33428 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33429 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33431 nanText : "{0} is not a valid number",
33433 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33435 thousandsDelimiter : false,
33437 * @cfg {String} valueAlign alignment of value
33439 valueAlign : "left",
33441 getAutoCreate : function()
33443 var hiddenInput = {
33447 cls: 'hidden-number-input'
33451 hiddenInput.name = this.name;
33456 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33458 this.name = hiddenInput.name;
33460 if(cfg.cn.length > 0) {
33461 cfg.cn.push(hiddenInput);
33468 initEvents : function()
33470 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33472 var allowed = "0123456789";
33474 if(this.allowDecimals){
33475 allowed += this.decimalSeparator;
33478 if(this.allowNegative){
33482 if(this.thousandsDelimiter) {
33486 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33488 var keyPress = function(e){
33490 var k = e.getKey();
33492 var c = e.getCharCode();
33495 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33496 allowed.indexOf(String.fromCharCode(c)) === -1
33502 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33506 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33511 this.el.on("keypress", keyPress, this);
33514 validateValue : function(value)
33517 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33521 var num = this.parseValue(value);
33524 this.markInvalid(String.format(this.nanText, value));
33528 if(num < this.minValue){
33529 this.markInvalid(String.format(this.minText, this.minValue));
33533 if(num > this.maxValue){
33534 this.markInvalid(String.format(this.maxText, this.maxValue));
33541 getValue : function()
33543 var v = this.hiddenEl().getValue();
33545 return this.fixPrecision(this.parseValue(v));
33548 parseValue : function(value)
33550 if(this.thousandsDelimiter) {
33552 r = new RegExp(",", "g");
33553 value = value.replace(r, "");
33556 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33557 return isNaN(value) ? '' : value;
33560 fixPrecision : function(value)
33562 if(this.thousandsDelimiter) {
33564 r = new RegExp(",", "g");
33565 value = value.replace(r, "");
33568 var nan = isNaN(value);
33570 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33571 return nan ? '' : value;
33573 return parseFloat(value).toFixed(this.decimalPrecision);
33576 setValue : function(v)
33578 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33584 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33586 this.inputEl().dom.value = (v == '') ? '' :
33587 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33589 if(!this.allowZero && v === '0') {
33590 this.hiddenEl().dom.value = '';
33591 this.inputEl().dom.value = '';
33598 decimalPrecisionFcn : function(v)
33600 return Math.floor(v);
33603 beforeBlur : function()
33605 var v = this.parseValue(this.getRawValue());
33607 if(v || v === 0 || v === ''){
33612 hiddenEl : function()
33614 return this.el.select('input.hidden-number-input',true).first();
33626 * @class Roo.bootstrap.DocumentSlider
33627 * @extends Roo.bootstrap.Component
33628 * Bootstrap DocumentSlider class
33631 * Create a new DocumentViewer
33632 * @param {Object} config The config object
33635 Roo.bootstrap.DocumentSlider = function(config){
33636 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33643 * Fire after initEvent
33644 * @param {Roo.bootstrap.DocumentSlider} this
33649 * Fire after update
33650 * @param {Roo.bootstrap.DocumentSlider} this
33656 * @param {Roo.bootstrap.DocumentSlider} this
33662 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33668 getAutoCreate : function()
33672 cls : 'roo-document-slider',
33676 cls : 'roo-document-slider-header',
33680 cls : 'roo-document-slider-header-title'
33686 cls : 'roo-document-slider-body',
33690 cls : 'roo-document-slider-prev',
33694 cls : 'fa fa-chevron-left'
33700 cls : 'roo-document-slider-thumb',
33704 cls : 'roo-document-slider-image'
33710 cls : 'roo-document-slider-next',
33714 cls : 'fa fa-chevron-right'
33726 initEvents : function()
33728 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33729 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33731 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33732 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33734 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33735 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33737 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33738 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33740 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33741 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33743 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33744 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33746 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33747 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33749 this.thumbEl.on('click', this.onClick, this);
33751 this.prevIndicator.on('click', this.prev, this);
33753 this.nextIndicator.on('click', this.next, this);
33757 initial : function()
33759 if(this.files.length){
33760 this.indicator = 1;
33764 this.fireEvent('initial', this);
33767 update : function()
33769 this.imageEl.attr('src', this.files[this.indicator - 1]);
33771 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33773 this.prevIndicator.show();
33775 if(this.indicator == 1){
33776 this.prevIndicator.hide();
33779 this.nextIndicator.show();
33781 if(this.indicator == this.files.length){
33782 this.nextIndicator.hide();
33785 this.thumbEl.scrollTo('top');
33787 this.fireEvent('update', this);
33790 onClick : function(e)
33792 e.preventDefault();
33794 this.fireEvent('click', this);
33799 e.preventDefault();
33801 this.indicator = Math.max(1, this.indicator - 1);
33808 e.preventDefault();
33810 this.indicator = Math.min(this.files.length, this.indicator + 1);
33824 * @class Roo.bootstrap.RadioSet
33825 * @extends Roo.bootstrap.Input
33826 * Bootstrap RadioSet class
33827 * @cfg {String} indicatorpos (left|right) default left
33828 * @cfg {Boolean} inline (true|false) inline the element (default true)
33829 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33831 * Create a new RadioSet
33832 * @param {Object} config The config object
33835 Roo.bootstrap.RadioSet = function(config){
33837 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33841 Roo.bootstrap.RadioSet.register(this);
33846 * Fires when the element is checked or unchecked.
33847 * @param {Roo.bootstrap.RadioSet} this This radio
33848 * @param {Roo.bootstrap.Radio} item The checked item
33853 * Fires when the element is click.
33854 * @param {Roo.bootstrap.RadioSet} this This radio set
33855 * @param {Roo.bootstrap.Radio} item The checked item
33856 * @param {Roo.EventObject} e The event object
33863 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33871 indicatorpos : 'left',
33873 getAutoCreate : function()
33877 cls : 'roo-radio-set-label',
33881 html : this.fieldLabel
33886 if(this.indicatorpos == 'left'){
33889 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33890 tooltip : 'This field is required'
33895 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33896 tooltip : 'This field is required'
33902 cls : 'roo-radio-set-items'
33905 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33907 if (align === 'left' && this.fieldLabel.length) {
33910 cls : "roo-radio-set-right",
33916 if(this.labelWidth > 12){
33917 label.style = "width: " + this.labelWidth + 'px';
33920 if(this.labelWidth < 13 && this.labelmd == 0){
33921 this.labelmd = this.labelWidth;
33924 if(this.labellg > 0){
33925 label.cls += ' col-lg-' + this.labellg;
33926 items.cls += ' col-lg-' + (12 - this.labellg);
33929 if(this.labelmd > 0){
33930 label.cls += ' col-md-' + this.labelmd;
33931 items.cls += ' col-md-' + (12 - this.labelmd);
33934 if(this.labelsm > 0){
33935 label.cls += ' col-sm-' + this.labelsm;
33936 items.cls += ' col-sm-' + (12 - this.labelsm);
33939 if(this.labelxs > 0){
33940 label.cls += ' col-xs-' + this.labelxs;
33941 items.cls += ' col-xs-' + (12 - this.labelxs);
33947 cls : 'roo-radio-set',
33951 cls : 'roo-radio-set-input',
33954 value : this.value ? this.value : ''
33961 if(this.weight.length){
33962 cfg.cls += ' roo-radio-' + this.weight;
33966 cfg.cls += ' roo-radio-set-inline';
33970 ['xs','sm','md','lg'].map(function(size){
33971 if (settings[size]) {
33972 cfg.cls += ' col-' + size + '-' + settings[size];
33980 initEvents : function()
33982 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33983 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33985 if(!this.fieldLabel.length){
33986 this.labelEl.hide();
33989 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33990 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33992 this.indicator = this.indicatorEl();
33994 if(this.indicator){
33995 this.indicator.addClass('invisible');
33998 this.originalValue = this.getValue();
34002 inputEl: function ()
34004 return this.el.select('.roo-radio-set-input', true).first();
34007 getChildContainer : function()
34009 return this.itemsEl;
34012 register : function(item)
34014 this.radioes.push(item);
34018 validate : function()
34020 if(this.getVisibilityEl().hasClass('hidden')){
34026 Roo.each(this.radioes, function(i){
34035 if(this.allowBlank) {
34039 if(this.disabled || valid){
34044 this.markInvalid();
34049 markValid : function()
34051 if(this.labelEl.isVisible(true)){
34052 this.indicatorEl().removeClass('visible');
34053 this.indicatorEl().addClass('invisible');
34056 this.el.removeClass([this.invalidClass, this.validClass]);
34057 this.el.addClass(this.validClass);
34059 this.fireEvent('valid', this);
34062 markInvalid : function(msg)
34064 if(this.allowBlank || this.disabled){
34068 if(this.labelEl.isVisible(true)){
34069 this.indicatorEl().removeClass('invisible');
34070 this.indicatorEl().addClass('visible');
34073 this.el.removeClass([this.invalidClass, this.validClass]);
34074 this.el.addClass(this.invalidClass);
34076 this.fireEvent('invalid', this, msg);
34080 setValue : function(v, suppressEvent)
34082 if(this.value === v){
34089 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34092 Roo.each(this.radioes, function(i){
34094 i.el.removeClass('checked');
34097 Roo.each(this.radioes, function(i){
34099 if(i.value === v || i.value.toString() === v.toString()){
34101 i.el.addClass('checked');
34103 if(suppressEvent !== true){
34104 this.fireEvent('check', this, i);
34115 clearInvalid : function(){
34117 if(!this.el || this.preventMark){
34121 this.el.removeClass([this.invalidClass]);
34123 this.fireEvent('valid', this);
34128 Roo.apply(Roo.bootstrap.RadioSet, {
34132 register : function(set)
34134 this.groups[set.name] = set;
34137 get: function(name)
34139 if (typeof(this.groups[name]) == 'undefined') {
34143 return this.groups[name] ;
34149 * Ext JS Library 1.1.1
34150 * Copyright(c) 2006-2007, Ext JS, LLC.
34152 * Originally Released Under LGPL - original licence link has changed is not relivant.
34155 * <script type="text/javascript">
34160 * @class Roo.bootstrap.SplitBar
34161 * @extends Roo.util.Observable
34162 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34166 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34167 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34168 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34169 split.minSize = 100;
34170 split.maxSize = 600;
34171 split.animate = true;
34172 split.on('moved', splitterMoved);
34175 * Create a new SplitBar
34176 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34177 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34178 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34179 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34180 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34181 position of the SplitBar).
34183 Roo.bootstrap.SplitBar = function(cfg){
34188 // dragElement : elm
34189 // resizingElement: el,
34191 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34192 // placement : Roo.bootstrap.SplitBar.LEFT ,
34193 // existingProxy ???
34196 this.el = Roo.get(cfg.dragElement, true);
34197 this.el.dom.unselectable = "on";
34199 this.resizingEl = Roo.get(cfg.resizingElement, true);
34203 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34204 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34207 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34210 * The minimum size of the resizing element. (Defaults to 0)
34216 * The maximum size of the resizing element. (Defaults to 2000)
34219 this.maxSize = 2000;
34222 * Whether to animate the transition to the new size
34225 this.animate = false;
34228 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34231 this.useShim = false;
34236 if(!cfg.existingProxy){
34238 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34240 this.proxy = Roo.get(cfg.existingProxy).dom;
34243 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34246 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34249 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34252 this.dragSpecs = {};
34255 * @private The adapter to use to positon and resize elements
34257 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34258 this.adapter.init(this);
34260 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34262 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34263 this.el.addClass("roo-splitbar-h");
34266 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34267 this.el.addClass("roo-splitbar-v");
34273 * Fires when the splitter is moved (alias for {@link #event-moved})
34274 * @param {Roo.bootstrap.SplitBar} this
34275 * @param {Number} newSize the new width or height
34280 * Fires when the splitter is moved
34281 * @param {Roo.bootstrap.SplitBar} this
34282 * @param {Number} newSize the new width or height
34286 * @event beforeresize
34287 * Fires before the splitter is dragged
34288 * @param {Roo.bootstrap.SplitBar} this
34290 "beforeresize" : true,
34292 "beforeapply" : true
34295 Roo.util.Observable.call(this);
34298 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34299 onStartProxyDrag : function(x, y){
34300 this.fireEvent("beforeresize", this);
34302 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34304 o.enableDisplayMode("block");
34305 // all splitbars share the same overlay
34306 Roo.bootstrap.SplitBar.prototype.overlay = o;
34308 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34309 this.overlay.show();
34310 Roo.get(this.proxy).setDisplayed("block");
34311 var size = this.adapter.getElementSize(this);
34312 this.activeMinSize = this.getMinimumSize();;
34313 this.activeMaxSize = this.getMaximumSize();;
34314 var c1 = size - this.activeMinSize;
34315 var c2 = Math.max(this.activeMaxSize - size, 0);
34316 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34317 this.dd.resetConstraints();
34318 this.dd.setXConstraint(
34319 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34320 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34322 this.dd.setYConstraint(0, 0);
34324 this.dd.resetConstraints();
34325 this.dd.setXConstraint(0, 0);
34326 this.dd.setYConstraint(
34327 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34328 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34331 this.dragSpecs.startSize = size;
34332 this.dragSpecs.startPoint = [x, y];
34333 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34337 * @private Called after the drag operation by the DDProxy
34339 onEndProxyDrag : function(e){
34340 Roo.get(this.proxy).setDisplayed(false);
34341 var endPoint = Roo.lib.Event.getXY(e);
34343 this.overlay.hide();
34346 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34347 newSize = this.dragSpecs.startSize +
34348 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34349 endPoint[0] - this.dragSpecs.startPoint[0] :
34350 this.dragSpecs.startPoint[0] - endPoint[0]
34353 newSize = this.dragSpecs.startSize +
34354 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34355 endPoint[1] - this.dragSpecs.startPoint[1] :
34356 this.dragSpecs.startPoint[1] - endPoint[1]
34359 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34360 if(newSize != this.dragSpecs.startSize){
34361 if(this.fireEvent('beforeapply', this, newSize) !== false){
34362 this.adapter.setElementSize(this, newSize);
34363 this.fireEvent("moved", this, newSize);
34364 this.fireEvent("resize", this, newSize);
34370 * Get the adapter this SplitBar uses
34371 * @return The adapter object
34373 getAdapter : function(){
34374 return this.adapter;
34378 * Set the adapter this SplitBar uses
34379 * @param {Object} adapter A SplitBar adapter object
34381 setAdapter : function(adapter){
34382 this.adapter = adapter;
34383 this.adapter.init(this);
34387 * Gets the minimum size for the resizing element
34388 * @return {Number} The minimum size
34390 getMinimumSize : function(){
34391 return this.minSize;
34395 * Sets the minimum size for the resizing element
34396 * @param {Number} minSize The minimum size
34398 setMinimumSize : function(minSize){
34399 this.minSize = minSize;
34403 * Gets the maximum size for the resizing element
34404 * @return {Number} The maximum size
34406 getMaximumSize : function(){
34407 return this.maxSize;
34411 * Sets the maximum size for the resizing element
34412 * @param {Number} maxSize The maximum size
34414 setMaximumSize : function(maxSize){
34415 this.maxSize = maxSize;
34419 * Sets the initialize size for the resizing element
34420 * @param {Number} size The initial size
34422 setCurrentSize : function(size){
34423 var oldAnimate = this.animate;
34424 this.animate = false;
34425 this.adapter.setElementSize(this, size);
34426 this.animate = oldAnimate;
34430 * Destroy this splitbar.
34431 * @param {Boolean} removeEl True to remove the element
34433 destroy : function(removeEl){
34435 this.shim.remove();
34438 this.proxy.parentNode.removeChild(this.proxy);
34446 * @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.
34448 Roo.bootstrap.SplitBar.createProxy = function(dir){
34449 var proxy = new Roo.Element(document.createElement("div"));
34450 proxy.unselectable();
34451 var cls = 'roo-splitbar-proxy';
34452 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34453 document.body.appendChild(proxy.dom);
34458 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34459 * Default Adapter. It assumes the splitter and resizing element are not positioned
34460 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34462 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34465 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34466 // do nothing for now
34467 init : function(s){
34471 * Called before drag operations to get the current size of the resizing element.
34472 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34474 getElementSize : function(s){
34475 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34476 return s.resizingEl.getWidth();
34478 return s.resizingEl.getHeight();
34483 * Called after drag operations to set the size of the resizing element.
34484 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34485 * @param {Number} newSize The new size to set
34486 * @param {Function} onComplete A function to be invoked when resizing is complete
34488 setElementSize : function(s, newSize, onComplete){
34489 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34491 s.resizingEl.setWidth(newSize);
34493 onComplete(s, newSize);
34496 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34501 s.resizingEl.setHeight(newSize);
34503 onComplete(s, newSize);
34506 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34513 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34514 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34515 * Adapter that moves the splitter element to align with the resized sizing element.
34516 * Used with an absolute positioned SplitBar.
34517 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34518 * document.body, make sure you assign an id to the body element.
34520 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34521 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34522 this.container = Roo.get(container);
34525 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34526 init : function(s){
34527 this.basic.init(s);
34530 getElementSize : function(s){
34531 return this.basic.getElementSize(s);
34534 setElementSize : function(s, newSize, onComplete){
34535 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34538 moveSplitter : function(s){
34539 var yes = Roo.bootstrap.SplitBar;
34540 switch(s.placement){
34542 s.el.setX(s.resizingEl.getRight());
34545 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34548 s.el.setY(s.resizingEl.getBottom());
34551 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34558 * Orientation constant - Create a vertical SplitBar
34562 Roo.bootstrap.SplitBar.VERTICAL = 1;
34565 * Orientation constant - Create a horizontal SplitBar
34569 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34572 * Placement constant - The resizing element is to the left of the splitter element
34576 Roo.bootstrap.SplitBar.LEFT = 1;
34579 * Placement constant - The resizing element is to the right of the splitter element
34583 Roo.bootstrap.SplitBar.RIGHT = 2;
34586 * Placement constant - The resizing element is positioned above the splitter element
34590 Roo.bootstrap.SplitBar.TOP = 3;
34593 * Placement constant - The resizing element is positioned under splitter element
34597 Roo.bootstrap.SplitBar.BOTTOM = 4;
34598 Roo.namespace("Roo.bootstrap.layout");/*
34600 * Ext JS Library 1.1.1
34601 * Copyright(c) 2006-2007, Ext JS, LLC.
34603 * Originally Released Under LGPL - original licence link has changed is not relivant.
34606 * <script type="text/javascript">
34610 * @class Roo.bootstrap.layout.Manager
34611 * @extends Roo.bootstrap.Component
34612 * Base class for layout managers.
34614 Roo.bootstrap.layout.Manager = function(config)
34616 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34622 /** false to disable window resize monitoring @type Boolean */
34623 this.monitorWindowResize = true;
34628 * Fires when a layout is performed.
34629 * @param {Roo.LayoutManager} this
34633 * @event regionresized
34634 * Fires when the user resizes a region.
34635 * @param {Roo.LayoutRegion} region The resized region
34636 * @param {Number} newSize The new size (width for east/west, height for north/south)
34638 "regionresized" : true,
34640 * @event regioncollapsed
34641 * Fires when a region is collapsed.
34642 * @param {Roo.LayoutRegion} region The collapsed region
34644 "regioncollapsed" : true,
34646 * @event regionexpanded
34647 * Fires when a region is expanded.
34648 * @param {Roo.LayoutRegion} region The expanded region
34650 "regionexpanded" : true
34652 this.updating = false;
34655 this.el = Roo.get(config.el);
34661 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34666 monitorWindowResize : true,
34672 onRender : function(ct, position)
34675 this.el = Roo.get(ct);
34678 //this.fireEvent('render',this);
34682 initEvents: function()
34686 // ie scrollbar fix
34687 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34688 document.body.scroll = "no";
34689 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34690 this.el.position('relative');
34692 this.id = this.el.id;
34693 this.el.addClass("roo-layout-container");
34694 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34695 if(this.el.dom != document.body ) {
34696 this.el.on('resize', this.layout,this);
34697 this.el.on('show', this.layout,this);
34703 * Returns true if this layout is currently being updated
34704 * @return {Boolean}
34706 isUpdating : function(){
34707 return this.updating;
34711 * Suspend the LayoutManager from doing auto-layouts while
34712 * making multiple add or remove calls
34714 beginUpdate : function(){
34715 this.updating = true;
34719 * Restore auto-layouts and optionally disable the manager from performing a layout
34720 * @param {Boolean} noLayout true to disable a layout update
34722 endUpdate : function(noLayout){
34723 this.updating = false;
34729 layout: function(){
34733 onRegionResized : function(region, newSize){
34734 this.fireEvent("regionresized", region, newSize);
34738 onRegionCollapsed : function(region){
34739 this.fireEvent("regioncollapsed", region);
34742 onRegionExpanded : function(region){
34743 this.fireEvent("regionexpanded", region);
34747 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34748 * performs box-model adjustments.
34749 * @return {Object} The size as an object {width: (the width), height: (the height)}
34751 getViewSize : function()
34754 if(this.el.dom != document.body){
34755 size = this.el.getSize();
34757 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34759 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34760 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34765 * Returns the Element this layout is bound to.
34766 * @return {Roo.Element}
34768 getEl : function(){
34773 * Returns the specified region.
34774 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34775 * @return {Roo.LayoutRegion}
34777 getRegion : function(target){
34778 return this.regions[target.toLowerCase()];
34781 onWindowResize : function(){
34782 if(this.monitorWindowResize){
34789 * Ext JS Library 1.1.1
34790 * Copyright(c) 2006-2007, Ext JS, LLC.
34792 * Originally Released Under LGPL - original licence link has changed is not relivant.
34795 * <script type="text/javascript">
34798 * @class Roo.bootstrap.layout.Border
34799 * @extends Roo.bootstrap.layout.Manager
34800 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34801 * please see: examples/bootstrap/nested.html<br><br>
34803 <b>The container the layout is rendered into can be either the body element or any other element.
34804 If it is not the body element, the container needs to either be an absolute positioned element,
34805 or you will need to add "position:relative" to the css of the container. You will also need to specify
34806 the container size if it is not the body element.</b>
34809 * Create a new Border
34810 * @param {Object} config Configuration options
34812 Roo.bootstrap.layout.Border = function(config){
34813 config = config || {};
34814 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34818 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34819 if(config[region]){
34820 config[region].region = region;
34821 this.addRegion(config[region]);
34827 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34829 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34831 * Creates and adds a new region if it doesn't already exist.
34832 * @param {String} target The target region key (north, south, east, west or center).
34833 * @param {Object} config The regions config object
34834 * @return {BorderLayoutRegion} The new region
34836 addRegion : function(config)
34838 if(!this.regions[config.region]){
34839 var r = this.factory(config);
34840 this.bindRegion(r);
34842 return this.regions[config.region];
34846 bindRegion : function(r){
34847 this.regions[r.config.region] = r;
34849 r.on("visibilitychange", this.layout, this);
34850 r.on("paneladded", this.layout, this);
34851 r.on("panelremoved", this.layout, this);
34852 r.on("invalidated", this.layout, this);
34853 r.on("resized", this.onRegionResized, this);
34854 r.on("collapsed", this.onRegionCollapsed, this);
34855 r.on("expanded", this.onRegionExpanded, this);
34859 * Performs a layout update.
34861 layout : function()
34863 if(this.updating) {
34867 // render all the rebions if they have not been done alreayd?
34868 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34869 if(this.regions[region] && !this.regions[region].bodyEl){
34870 this.regions[region].onRender(this.el)
34874 var size = this.getViewSize();
34875 var w = size.width;
34876 var h = size.height;
34881 //var x = 0, y = 0;
34883 var rs = this.regions;
34884 var north = rs["north"];
34885 var south = rs["south"];
34886 var west = rs["west"];
34887 var east = rs["east"];
34888 var center = rs["center"];
34889 //if(this.hideOnLayout){ // not supported anymore
34890 //c.el.setStyle("display", "none");
34892 if(north && north.isVisible()){
34893 var b = north.getBox();
34894 var m = north.getMargins();
34895 b.width = w - (m.left+m.right);
34898 centerY = b.height + b.y + m.bottom;
34899 centerH -= centerY;
34900 north.updateBox(this.safeBox(b));
34902 if(south && south.isVisible()){
34903 var b = south.getBox();
34904 var m = south.getMargins();
34905 b.width = w - (m.left+m.right);
34907 var totalHeight = (b.height + m.top + m.bottom);
34908 b.y = h - totalHeight + m.top;
34909 centerH -= totalHeight;
34910 south.updateBox(this.safeBox(b));
34912 if(west && west.isVisible()){
34913 var b = west.getBox();
34914 var m = west.getMargins();
34915 b.height = centerH - (m.top+m.bottom);
34917 b.y = centerY + m.top;
34918 var totalWidth = (b.width + m.left + m.right);
34919 centerX += totalWidth;
34920 centerW -= totalWidth;
34921 west.updateBox(this.safeBox(b));
34923 if(east && east.isVisible()){
34924 var b = east.getBox();
34925 var m = east.getMargins();
34926 b.height = centerH - (m.top+m.bottom);
34927 var totalWidth = (b.width + m.left + m.right);
34928 b.x = w - totalWidth + m.left;
34929 b.y = centerY + m.top;
34930 centerW -= totalWidth;
34931 east.updateBox(this.safeBox(b));
34934 var m = center.getMargins();
34936 x: centerX + m.left,
34937 y: centerY + m.top,
34938 width: centerW - (m.left+m.right),
34939 height: centerH - (m.top+m.bottom)
34941 //if(this.hideOnLayout){
34942 //center.el.setStyle("display", "block");
34944 center.updateBox(this.safeBox(centerBox));
34947 this.fireEvent("layout", this);
34951 safeBox : function(box){
34952 box.width = Math.max(0, box.width);
34953 box.height = Math.max(0, box.height);
34958 * Adds a ContentPanel (or subclass) to this layout.
34959 * @param {String} target The target region key (north, south, east, west or center).
34960 * @param {Roo.ContentPanel} panel The panel to add
34961 * @return {Roo.ContentPanel} The added panel
34963 add : function(target, panel){
34965 target = target.toLowerCase();
34966 return this.regions[target].add(panel);
34970 * Remove a ContentPanel (or subclass) to this layout.
34971 * @param {String} target The target region key (north, south, east, west or center).
34972 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34973 * @return {Roo.ContentPanel} The removed panel
34975 remove : function(target, panel){
34976 target = target.toLowerCase();
34977 return this.regions[target].remove(panel);
34981 * Searches all regions for a panel with the specified id
34982 * @param {String} panelId
34983 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34985 findPanel : function(panelId){
34986 var rs = this.regions;
34987 for(var target in rs){
34988 if(typeof rs[target] != "function"){
34989 var p = rs[target].getPanel(panelId);
34999 * Searches all regions for a panel with the specified id and activates (shows) it.
35000 * @param {String/ContentPanel} panelId The panels id or the panel itself
35001 * @return {Roo.ContentPanel} The shown panel or null
35003 showPanel : function(panelId) {
35004 var rs = this.regions;
35005 for(var target in rs){
35006 var r = rs[target];
35007 if(typeof r != "function"){
35008 if(r.hasPanel(panelId)){
35009 return r.showPanel(panelId);
35017 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35018 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35021 restoreState : function(provider){
35023 provider = Roo.state.Manager;
35025 var sm = new Roo.LayoutStateManager();
35026 sm.init(this, provider);
35032 * Adds a xtype elements to the layout.
35036 xtype : 'ContentPanel',
35043 xtype : 'NestedLayoutPanel',
35049 items : [ ... list of content panels or nested layout panels.. ]
35053 * @param {Object} cfg Xtype definition of item to add.
35055 addxtype : function(cfg)
35057 // basically accepts a pannel...
35058 // can accept a layout region..!?!?
35059 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35062 // theory? children can only be panels??
35064 //if (!cfg.xtype.match(/Panel$/)) {
35069 if (typeof(cfg.region) == 'undefined') {
35070 Roo.log("Failed to add Panel, region was not set");
35074 var region = cfg.region;
35080 xitems = cfg.items;
35087 case 'Content': // ContentPanel (el, cfg)
35088 case 'Scroll': // ContentPanel (el, cfg)
35090 cfg.autoCreate = true;
35091 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35093 // var el = this.el.createChild();
35094 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35097 this.add(region, ret);
35101 case 'TreePanel': // our new panel!
35102 cfg.el = this.el.createChild();
35103 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35104 this.add(region, ret);
35109 // create a new Layout (which is a Border Layout...
35111 var clayout = cfg.layout;
35112 clayout.el = this.el.createChild();
35113 clayout.items = clayout.items || [];
35117 // replace this exitems with the clayout ones..
35118 xitems = clayout.items;
35120 // force background off if it's in center...
35121 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35122 cfg.background = false;
35124 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35127 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35128 //console.log('adding nested layout panel ' + cfg.toSource());
35129 this.add(region, ret);
35130 nb = {}; /// find first...
35135 // needs grid and region
35137 //var el = this.getRegion(region).el.createChild();
35139 *var el = this.el.createChild();
35140 // create the grid first...
35141 cfg.grid.container = el;
35142 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35145 if (region == 'center' && this.active ) {
35146 cfg.background = false;
35149 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35151 this.add(region, ret);
35153 if (cfg.background) {
35154 // render grid on panel activation (if panel background)
35155 ret.on('activate', function(gp) {
35156 if (!gp.grid.rendered) {
35157 // gp.grid.render(el);
35161 // cfg.grid.render(el);
35167 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35168 // it was the old xcomponent building that caused this before.
35169 // espeically if border is the top element in the tree.
35179 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35181 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35182 this.add(region, ret);
35186 throw "Can not add '" + cfg.xtype + "' to Border";
35192 this.beginUpdate();
35196 Roo.each(xitems, function(i) {
35197 region = nb && i.region ? i.region : false;
35199 var add = ret.addxtype(i);
35202 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35203 if (!i.background) {
35204 abn[region] = nb[region] ;
35211 // make the last non-background panel active..
35212 //if (nb) { Roo.log(abn); }
35215 for(var r in abn) {
35216 region = this.getRegion(r);
35218 // tried using nb[r], but it does not work..
35220 region.showPanel(abn[r]);
35231 factory : function(cfg)
35234 var validRegions = Roo.bootstrap.layout.Border.regions;
35236 var target = cfg.region;
35239 var r = Roo.bootstrap.layout;
35243 return new r.North(cfg);
35245 return new r.South(cfg);
35247 return new r.East(cfg);
35249 return new r.West(cfg);
35251 return new r.Center(cfg);
35253 throw 'Layout region "'+target+'" not supported.';
35260 * Ext JS Library 1.1.1
35261 * Copyright(c) 2006-2007, Ext JS, LLC.
35263 * Originally Released Under LGPL - original licence link has changed is not relivant.
35266 * <script type="text/javascript">
35270 * @class Roo.bootstrap.layout.Basic
35271 * @extends Roo.util.Observable
35272 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35273 * and does not have a titlebar, tabs or any other features. All it does is size and position
35274 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35275 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35276 * @cfg {string} region the region that it inhabits..
35277 * @cfg {bool} skipConfig skip config?
35281 Roo.bootstrap.layout.Basic = function(config){
35283 this.mgr = config.mgr;
35285 this.position = config.region;
35287 var skipConfig = config.skipConfig;
35291 * @scope Roo.BasicLayoutRegion
35295 * @event beforeremove
35296 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35297 * @param {Roo.LayoutRegion} this
35298 * @param {Roo.ContentPanel} panel The panel
35299 * @param {Object} e The cancel event object
35301 "beforeremove" : true,
35303 * @event invalidated
35304 * Fires when the layout for this region is changed.
35305 * @param {Roo.LayoutRegion} this
35307 "invalidated" : true,
35309 * @event visibilitychange
35310 * Fires when this region is shown or hidden
35311 * @param {Roo.LayoutRegion} this
35312 * @param {Boolean} visibility true or false
35314 "visibilitychange" : true,
35316 * @event paneladded
35317 * Fires when a panel is added.
35318 * @param {Roo.LayoutRegion} this
35319 * @param {Roo.ContentPanel} panel The panel
35321 "paneladded" : true,
35323 * @event panelremoved
35324 * Fires when a panel is removed.
35325 * @param {Roo.LayoutRegion} this
35326 * @param {Roo.ContentPanel} panel The panel
35328 "panelremoved" : true,
35330 * @event beforecollapse
35331 * Fires when this region before collapse.
35332 * @param {Roo.LayoutRegion} this
35334 "beforecollapse" : true,
35337 * Fires when this region is collapsed.
35338 * @param {Roo.LayoutRegion} this
35340 "collapsed" : true,
35343 * Fires when this region is expanded.
35344 * @param {Roo.LayoutRegion} this
35349 * Fires when this region is slid into view.
35350 * @param {Roo.LayoutRegion} this
35352 "slideshow" : true,
35355 * Fires when this region slides out of view.
35356 * @param {Roo.LayoutRegion} this
35358 "slidehide" : true,
35360 * @event panelactivated
35361 * Fires when a panel is activated.
35362 * @param {Roo.LayoutRegion} this
35363 * @param {Roo.ContentPanel} panel The activated panel
35365 "panelactivated" : true,
35368 * Fires when the user resizes this region.
35369 * @param {Roo.LayoutRegion} this
35370 * @param {Number} newSize The new size (width for east/west, height for north/south)
35374 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35375 this.panels = new Roo.util.MixedCollection();
35376 this.panels.getKey = this.getPanelId.createDelegate(this);
35378 this.activePanel = null;
35379 // ensure listeners are added...
35381 if (config.listeners || config.events) {
35382 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35383 listeners : config.listeners || {},
35384 events : config.events || {}
35388 if(skipConfig !== true){
35389 this.applyConfig(config);
35393 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35395 getPanelId : function(p){
35399 applyConfig : function(config){
35400 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35401 this.config = config;
35406 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35407 * the width, for horizontal (north, south) the height.
35408 * @param {Number} newSize The new width or height
35410 resizeTo : function(newSize){
35411 var el = this.el ? this.el :
35412 (this.activePanel ? this.activePanel.getEl() : null);
35414 switch(this.position){
35417 el.setWidth(newSize);
35418 this.fireEvent("resized", this, newSize);
35422 el.setHeight(newSize);
35423 this.fireEvent("resized", this, newSize);
35429 getBox : function(){
35430 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35433 getMargins : function(){
35434 return this.margins;
35437 updateBox : function(box){
35439 var el = this.activePanel.getEl();
35440 el.dom.style.left = box.x + "px";
35441 el.dom.style.top = box.y + "px";
35442 this.activePanel.setSize(box.width, box.height);
35446 * Returns the container element for this region.
35447 * @return {Roo.Element}
35449 getEl : function(){
35450 return this.activePanel;
35454 * Returns true if this region is currently visible.
35455 * @return {Boolean}
35457 isVisible : function(){
35458 return this.activePanel ? true : false;
35461 setActivePanel : function(panel){
35462 panel = this.getPanel(panel);
35463 if(this.activePanel && this.activePanel != panel){
35464 this.activePanel.setActiveState(false);
35465 this.activePanel.getEl().setLeftTop(-10000,-10000);
35467 this.activePanel = panel;
35468 panel.setActiveState(true);
35470 panel.setSize(this.box.width, this.box.height);
35472 this.fireEvent("panelactivated", this, panel);
35473 this.fireEvent("invalidated");
35477 * Show the specified panel.
35478 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35479 * @return {Roo.ContentPanel} The shown panel or null
35481 showPanel : function(panel){
35482 panel = this.getPanel(panel);
35484 this.setActivePanel(panel);
35490 * Get the active panel for this region.
35491 * @return {Roo.ContentPanel} The active panel or null
35493 getActivePanel : function(){
35494 return this.activePanel;
35498 * Add the passed ContentPanel(s)
35499 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35500 * @return {Roo.ContentPanel} The panel added (if only one was added)
35502 add : function(panel){
35503 if(arguments.length > 1){
35504 for(var i = 0, len = arguments.length; i < len; i++) {
35505 this.add(arguments[i]);
35509 if(this.hasPanel(panel)){
35510 this.showPanel(panel);
35513 var el = panel.getEl();
35514 if(el.dom.parentNode != this.mgr.el.dom){
35515 this.mgr.el.dom.appendChild(el.dom);
35517 if(panel.setRegion){
35518 panel.setRegion(this);
35520 this.panels.add(panel);
35521 el.setStyle("position", "absolute");
35522 if(!panel.background){
35523 this.setActivePanel(panel);
35524 if(this.config.initialSize && this.panels.getCount()==1){
35525 this.resizeTo(this.config.initialSize);
35528 this.fireEvent("paneladded", this, panel);
35533 * Returns true if the panel is in this region.
35534 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35535 * @return {Boolean}
35537 hasPanel : function(panel){
35538 if(typeof panel == "object"){ // must be panel obj
35539 panel = panel.getId();
35541 return this.getPanel(panel) ? true : false;
35545 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35546 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35547 * @param {Boolean} preservePanel Overrides the config preservePanel option
35548 * @return {Roo.ContentPanel} The panel that was removed
35550 remove : function(panel, preservePanel){
35551 panel = this.getPanel(panel);
35556 this.fireEvent("beforeremove", this, panel, e);
35557 if(e.cancel === true){
35560 var panelId = panel.getId();
35561 this.panels.removeKey(panelId);
35566 * Returns the panel specified or null if it's not in this region.
35567 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35568 * @return {Roo.ContentPanel}
35570 getPanel : function(id){
35571 if(typeof id == "object"){ // must be panel obj
35574 return this.panels.get(id);
35578 * Returns this regions position (north/south/east/west/center).
35581 getPosition: function(){
35582 return this.position;
35586 * Ext JS Library 1.1.1
35587 * Copyright(c) 2006-2007, Ext JS, LLC.
35589 * Originally Released Under LGPL - original licence link has changed is not relivant.
35592 * <script type="text/javascript">
35596 * @class Roo.bootstrap.layout.Region
35597 * @extends Roo.bootstrap.layout.Basic
35598 * This class represents a region in a layout manager.
35600 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35601 * @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})
35602 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35603 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35604 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35605 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35606 * @cfg {String} title The title for the region (overrides panel titles)
35607 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35608 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35609 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35610 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35611 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35612 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35613 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35614 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35615 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35616 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35618 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35619 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35620 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35621 * @cfg {Number} width For East/West panels
35622 * @cfg {Number} height For North/South panels
35623 * @cfg {Boolean} split To show the splitter
35624 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35626 * @cfg {string} cls Extra CSS classes to add to region
35628 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35629 * @cfg {string} region the region that it inhabits..
35632 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35633 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35635 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35636 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35637 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35639 Roo.bootstrap.layout.Region = function(config)
35641 this.applyConfig(config);
35643 var mgr = config.mgr;
35644 var pos = config.region;
35645 config.skipConfig = true;
35646 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35649 this.onRender(mgr.el);
35652 this.visible = true;
35653 this.collapsed = false;
35654 this.unrendered_panels = [];
35657 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35659 position: '', // set by wrapper (eg. north/south etc..)
35660 unrendered_panels : null, // unrendered panels.
35661 createBody : function(){
35662 /** This region's body element
35663 * @type Roo.Element */
35664 this.bodyEl = this.el.createChild({
35666 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35670 onRender: function(ctr, pos)
35672 var dh = Roo.DomHelper;
35673 /** This region's container element
35674 * @type Roo.Element */
35675 this.el = dh.append(ctr.dom, {
35677 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35679 /** This region's title element
35680 * @type Roo.Element */
35682 this.titleEl = dh.append(this.el.dom,
35685 unselectable: "on",
35686 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35688 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35689 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35692 this.titleEl.enableDisplayMode();
35693 /** This region's title text element
35694 * @type HTMLElement */
35695 this.titleTextEl = this.titleEl.dom.firstChild;
35696 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35698 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35699 this.closeBtn.enableDisplayMode();
35700 this.closeBtn.on("click", this.closeClicked, this);
35701 this.closeBtn.hide();
35703 this.createBody(this.config);
35704 if(this.config.hideWhenEmpty){
35706 this.on("paneladded", this.validateVisibility, this);
35707 this.on("panelremoved", this.validateVisibility, this);
35709 if(this.autoScroll){
35710 this.bodyEl.setStyle("overflow", "auto");
35712 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35714 //if(c.titlebar !== false){
35715 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35716 this.titleEl.hide();
35718 this.titleEl.show();
35719 if(this.config.title){
35720 this.titleTextEl.innerHTML = this.config.title;
35724 if(this.config.collapsed){
35725 this.collapse(true);
35727 if(this.config.hidden){
35731 if (this.unrendered_panels && this.unrendered_panels.length) {
35732 for (var i =0;i< this.unrendered_panels.length; i++) {
35733 this.add(this.unrendered_panels[i]);
35735 this.unrendered_panels = null;
35741 applyConfig : function(c)
35744 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35745 var dh = Roo.DomHelper;
35746 if(c.titlebar !== false){
35747 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35748 this.collapseBtn.on("click", this.collapse, this);
35749 this.collapseBtn.enableDisplayMode();
35751 if(c.showPin === true || this.showPin){
35752 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35753 this.stickBtn.enableDisplayMode();
35754 this.stickBtn.on("click", this.expand, this);
35755 this.stickBtn.hide();
35760 /** This region's collapsed element
35761 * @type Roo.Element */
35764 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35765 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35768 if(c.floatable !== false){
35769 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35770 this.collapsedEl.on("click", this.collapseClick, this);
35773 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35774 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35775 id: "message", unselectable: "on", style:{"float":"left"}});
35776 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35778 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35779 this.expandBtn.on("click", this.expand, this);
35783 if(this.collapseBtn){
35784 this.collapseBtn.setVisible(c.collapsible == true);
35787 this.cmargins = c.cmargins || this.cmargins ||
35788 (this.position == "west" || this.position == "east" ?
35789 {top: 0, left: 2, right:2, bottom: 0} :
35790 {top: 2, left: 0, right:0, bottom: 2});
35792 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35795 this.bottomTabs = c.tabPosition != "top";
35797 this.autoScroll = c.autoScroll || false;
35802 this.duration = c.duration || .30;
35803 this.slideDuration = c.slideDuration || .45;
35808 * Returns true if this region is currently visible.
35809 * @return {Boolean}
35811 isVisible : function(){
35812 return this.visible;
35816 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35817 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35819 //setCollapsedTitle : function(title){
35820 // title = title || " ";
35821 // if(this.collapsedTitleTextEl){
35822 // this.collapsedTitleTextEl.innerHTML = title;
35826 getBox : function(){
35828 // if(!this.collapsed){
35829 b = this.el.getBox(false, true);
35831 // b = this.collapsedEl.getBox(false, true);
35836 getMargins : function(){
35837 return this.margins;
35838 //return this.collapsed ? this.cmargins : this.margins;
35841 highlight : function(){
35842 this.el.addClass("x-layout-panel-dragover");
35845 unhighlight : function(){
35846 this.el.removeClass("x-layout-panel-dragover");
35849 updateBox : function(box)
35851 if (!this.bodyEl) {
35852 return; // not rendered yet..
35856 if(!this.collapsed){
35857 this.el.dom.style.left = box.x + "px";
35858 this.el.dom.style.top = box.y + "px";
35859 this.updateBody(box.width, box.height);
35861 this.collapsedEl.dom.style.left = box.x + "px";
35862 this.collapsedEl.dom.style.top = box.y + "px";
35863 this.collapsedEl.setSize(box.width, box.height);
35866 this.tabs.autoSizeTabs();
35870 updateBody : function(w, h)
35873 this.el.setWidth(w);
35874 w -= this.el.getBorderWidth("rl");
35875 if(this.config.adjustments){
35876 w += this.config.adjustments[0];
35879 if(h !== null && h > 0){
35880 this.el.setHeight(h);
35881 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35882 h -= this.el.getBorderWidth("tb");
35883 if(this.config.adjustments){
35884 h += this.config.adjustments[1];
35886 this.bodyEl.setHeight(h);
35888 h = this.tabs.syncHeight(h);
35891 if(this.panelSize){
35892 w = w !== null ? w : this.panelSize.width;
35893 h = h !== null ? h : this.panelSize.height;
35895 if(this.activePanel){
35896 var el = this.activePanel.getEl();
35897 w = w !== null ? w : el.getWidth();
35898 h = h !== null ? h : el.getHeight();
35899 this.panelSize = {width: w, height: h};
35900 this.activePanel.setSize(w, h);
35902 if(Roo.isIE && this.tabs){
35903 this.tabs.el.repaint();
35908 * Returns the container element for this region.
35909 * @return {Roo.Element}
35911 getEl : function(){
35916 * Hides this region.
35919 //if(!this.collapsed){
35920 this.el.dom.style.left = "-2000px";
35923 // this.collapsedEl.dom.style.left = "-2000px";
35924 // this.collapsedEl.hide();
35926 this.visible = false;
35927 this.fireEvent("visibilitychange", this, false);
35931 * Shows this region if it was previously hidden.
35934 //if(!this.collapsed){
35937 // this.collapsedEl.show();
35939 this.visible = true;
35940 this.fireEvent("visibilitychange", this, true);
35943 closeClicked : function(){
35944 if(this.activePanel){
35945 this.remove(this.activePanel);
35949 collapseClick : function(e){
35951 e.stopPropagation();
35954 e.stopPropagation();
35960 * Collapses this region.
35961 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35964 collapse : function(skipAnim, skipCheck = false){
35965 if(this.collapsed) {
35969 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35971 this.collapsed = true;
35973 this.split.el.hide();
35975 if(this.config.animate && skipAnim !== true){
35976 this.fireEvent("invalidated", this);
35977 this.animateCollapse();
35979 this.el.setLocation(-20000,-20000);
35981 this.collapsedEl.show();
35982 this.fireEvent("collapsed", this);
35983 this.fireEvent("invalidated", this);
35989 animateCollapse : function(){
35994 * Expands this region if it was previously collapsed.
35995 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35996 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35999 expand : function(e, skipAnim){
36001 e.stopPropagation();
36003 if(!this.collapsed || this.el.hasActiveFx()) {
36007 this.afterSlideIn();
36010 this.collapsed = false;
36011 if(this.config.animate && skipAnim !== true){
36012 this.animateExpand();
36016 this.split.el.show();
36018 this.collapsedEl.setLocation(-2000,-2000);
36019 this.collapsedEl.hide();
36020 this.fireEvent("invalidated", this);
36021 this.fireEvent("expanded", this);
36025 animateExpand : function(){
36029 initTabs : function()
36031 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36033 var ts = new Roo.bootstrap.panel.Tabs({
36034 el: this.bodyEl.dom,
36035 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36036 disableTooltips: this.config.disableTabTips,
36037 toolbar : this.config.toolbar
36040 if(this.config.hideTabs){
36041 ts.stripWrap.setDisplayed(false);
36044 ts.resizeTabs = this.config.resizeTabs === true;
36045 ts.minTabWidth = this.config.minTabWidth || 40;
36046 ts.maxTabWidth = this.config.maxTabWidth || 250;
36047 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36048 ts.monitorResize = false;
36049 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36050 ts.bodyEl.addClass('roo-layout-tabs-body');
36051 this.panels.each(this.initPanelAsTab, this);
36054 initPanelAsTab : function(panel){
36055 var ti = this.tabs.addTab(
36059 this.config.closeOnTab && panel.isClosable(),
36062 if(panel.tabTip !== undefined){
36063 ti.setTooltip(panel.tabTip);
36065 ti.on("activate", function(){
36066 this.setActivePanel(panel);
36069 if(this.config.closeOnTab){
36070 ti.on("beforeclose", function(t, e){
36072 this.remove(panel);
36076 panel.tabItem = ti;
36081 updatePanelTitle : function(panel, title)
36083 if(this.activePanel == panel){
36084 this.updateTitle(title);
36087 var ti = this.tabs.getTab(panel.getEl().id);
36089 if(panel.tabTip !== undefined){
36090 ti.setTooltip(panel.tabTip);
36095 updateTitle : function(title){
36096 if(this.titleTextEl && !this.config.title){
36097 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36101 setActivePanel : function(panel)
36103 panel = this.getPanel(panel);
36104 if(this.activePanel && this.activePanel != panel){
36105 if(this.activePanel.setActiveState(false) === false){
36109 this.activePanel = panel;
36110 panel.setActiveState(true);
36111 if(this.panelSize){
36112 panel.setSize(this.panelSize.width, this.panelSize.height);
36115 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36117 this.updateTitle(panel.getTitle());
36119 this.fireEvent("invalidated", this);
36121 this.fireEvent("panelactivated", this, panel);
36125 * Shows the specified panel.
36126 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36127 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36129 showPanel : function(panel)
36131 panel = this.getPanel(panel);
36134 var tab = this.tabs.getTab(panel.getEl().id);
36135 if(tab.isHidden()){
36136 this.tabs.unhideTab(tab.id);
36140 this.setActivePanel(panel);
36147 * Get the active panel for this region.
36148 * @return {Roo.ContentPanel} The active panel or null
36150 getActivePanel : function(){
36151 return this.activePanel;
36154 validateVisibility : function(){
36155 if(this.panels.getCount() < 1){
36156 this.updateTitle(" ");
36157 this.closeBtn.hide();
36160 if(!this.isVisible()){
36167 * Adds the passed ContentPanel(s) to this region.
36168 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36169 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36171 add : function(panel)
36173 if(arguments.length > 1){
36174 for(var i = 0, len = arguments.length; i < len; i++) {
36175 this.add(arguments[i]);
36180 // if we have not been rendered yet, then we can not really do much of this..
36181 if (!this.bodyEl) {
36182 this.unrendered_panels.push(panel);
36189 if(this.hasPanel(panel)){
36190 this.showPanel(panel);
36193 panel.setRegion(this);
36194 this.panels.add(panel);
36195 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36196 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36197 // and hide them... ???
36198 this.bodyEl.dom.appendChild(panel.getEl().dom);
36199 if(panel.background !== true){
36200 this.setActivePanel(panel);
36202 this.fireEvent("paneladded", this, panel);
36209 this.initPanelAsTab(panel);
36213 if(panel.background !== true){
36214 this.tabs.activate(panel.getEl().id);
36216 this.fireEvent("paneladded", this, panel);
36221 * Hides the tab for the specified panel.
36222 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36224 hidePanel : function(panel){
36225 if(this.tabs && (panel = this.getPanel(panel))){
36226 this.tabs.hideTab(panel.getEl().id);
36231 * Unhides the tab for a previously hidden panel.
36232 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36234 unhidePanel : function(panel){
36235 if(this.tabs && (panel = this.getPanel(panel))){
36236 this.tabs.unhideTab(panel.getEl().id);
36240 clearPanels : function(){
36241 while(this.panels.getCount() > 0){
36242 this.remove(this.panels.first());
36247 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36248 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36249 * @param {Boolean} preservePanel Overrides the config preservePanel option
36250 * @return {Roo.ContentPanel} The panel that was removed
36252 remove : function(panel, preservePanel)
36254 panel = this.getPanel(panel);
36259 this.fireEvent("beforeremove", this, panel, e);
36260 if(e.cancel === true){
36263 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36264 var panelId = panel.getId();
36265 this.panels.removeKey(panelId);
36267 document.body.appendChild(panel.getEl().dom);
36270 this.tabs.removeTab(panel.getEl().id);
36271 }else if (!preservePanel){
36272 this.bodyEl.dom.removeChild(panel.getEl().dom);
36274 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36275 var p = this.panels.first();
36276 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36277 tempEl.appendChild(p.getEl().dom);
36278 this.bodyEl.update("");
36279 this.bodyEl.dom.appendChild(p.getEl().dom);
36281 this.updateTitle(p.getTitle());
36283 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36284 this.setActivePanel(p);
36286 panel.setRegion(null);
36287 if(this.activePanel == panel){
36288 this.activePanel = null;
36290 if(this.config.autoDestroy !== false && preservePanel !== true){
36291 try{panel.destroy();}catch(e){}
36293 this.fireEvent("panelremoved", this, panel);
36298 * Returns the TabPanel component used by this region
36299 * @return {Roo.TabPanel}
36301 getTabs : function(){
36305 createTool : function(parentEl, className){
36306 var btn = Roo.DomHelper.append(parentEl, {
36308 cls: "x-layout-tools-button",
36311 cls: "roo-layout-tools-button-inner " + className,
36315 btn.addClassOnOver("roo-layout-tools-button-over");
36320 * Ext JS Library 1.1.1
36321 * Copyright(c) 2006-2007, Ext JS, LLC.
36323 * Originally Released Under LGPL - original licence link has changed is not relivant.
36326 * <script type="text/javascript">
36332 * @class Roo.SplitLayoutRegion
36333 * @extends Roo.LayoutRegion
36334 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36336 Roo.bootstrap.layout.Split = function(config){
36337 this.cursor = config.cursor;
36338 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36341 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36343 splitTip : "Drag to resize.",
36344 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36345 useSplitTips : false,
36347 applyConfig : function(config){
36348 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36351 onRender : function(ctr,pos) {
36353 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36354 if(!this.config.split){
36359 var splitEl = Roo.DomHelper.append(ctr.dom, {
36361 id: this.el.id + "-split",
36362 cls: "roo-layout-split roo-layout-split-"+this.position,
36365 /** The SplitBar for this region
36366 * @type Roo.SplitBar */
36367 // does not exist yet...
36368 Roo.log([this.position, this.orientation]);
36370 this.split = new Roo.bootstrap.SplitBar({
36371 dragElement : splitEl,
36372 resizingElement: this.el,
36373 orientation : this.orientation
36376 this.split.on("moved", this.onSplitMove, this);
36377 this.split.useShim = this.config.useShim === true;
36378 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36379 if(this.useSplitTips){
36380 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36382 //if(config.collapsible){
36383 // this.split.el.on("dblclick", this.collapse, this);
36386 if(typeof this.config.minSize != "undefined"){
36387 this.split.minSize = this.config.minSize;
36389 if(typeof this.config.maxSize != "undefined"){
36390 this.split.maxSize = this.config.maxSize;
36392 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36393 this.hideSplitter();
36398 getHMaxSize : function(){
36399 var cmax = this.config.maxSize || 10000;
36400 var center = this.mgr.getRegion("center");
36401 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36404 getVMaxSize : function(){
36405 var cmax = this.config.maxSize || 10000;
36406 var center = this.mgr.getRegion("center");
36407 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36410 onSplitMove : function(split, newSize){
36411 this.fireEvent("resized", this, newSize);
36415 * Returns the {@link Roo.SplitBar} for this region.
36416 * @return {Roo.SplitBar}
36418 getSplitBar : function(){
36423 this.hideSplitter();
36424 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36427 hideSplitter : function(){
36429 this.split.el.setLocation(-2000,-2000);
36430 this.split.el.hide();
36436 this.split.el.show();
36438 Roo.bootstrap.layout.Split.superclass.show.call(this);
36441 beforeSlide: function(){
36442 if(Roo.isGecko){// firefox overflow auto bug workaround
36443 this.bodyEl.clip();
36445 this.tabs.bodyEl.clip();
36447 if(this.activePanel){
36448 this.activePanel.getEl().clip();
36450 if(this.activePanel.beforeSlide){
36451 this.activePanel.beforeSlide();
36457 afterSlide : function(){
36458 if(Roo.isGecko){// firefox overflow auto bug workaround
36459 this.bodyEl.unclip();
36461 this.tabs.bodyEl.unclip();
36463 if(this.activePanel){
36464 this.activePanel.getEl().unclip();
36465 if(this.activePanel.afterSlide){
36466 this.activePanel.afterSlide();
36472 initAutoHide : function(){
36473 if(this.autoHide !== false){
36474 if(!this.autoHideHd){
36475 var st = new Roo.util.DelayedTask(this.slideIn, this);
36476 this.autoHideHd = {
36477 "mouseout": function(e){
36478 if(!e.within(this.el, true)){
36482 "mouseover" : function(e){
36488 this.el.on(this.autoHideHd);
36492 clearAutoHide : function(){
36493 if(this.autoHide !== false){
36494 this.el.un("mouseout", this.autoHideHd.mouseout);
36495 this.el.un("mouseover", this.autoHideHd.mouseover);
36499 clearMonitor : function(){
36500 Roo.get(document).un("click", this.slideInIf, this);
36503 // these names are backwards but not changed for compat
36504 slideOut : function(){
36505 if(this.isSlid || this.el.hasActiveFx()){
36508 this.isSlid = true;
36509 if(this.collapseBtn){
36510 this.collapseBtn.hide();
36512 this.closeBtnState = this.closeBtn.getStyle('display');
36513 this.closeBtn.hide();
36515 this.stickBtn.show();
36518 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36519 this.beforeSlide();
36520 this.el.setStyle("z-index", 10001);
36521 this.el.slideIn(this.getSlideAnchor(), {
36522 callback: function(){
36524 this.initAutoHide();
36525 Roo.get(document).on("click", this.slideInIf, this);
36526 this.fireEvent("slideshow", this);
36533 afterSlideIn : function(){
36534 this.clearAutoHide();
36535 this.isSlid = false;
36536 this.clearMonitor();
36537 this.el.setStyle("z-index", "");
36538 if(this.collapseBtn){
36539 this.collapseBtn.show();
36541 this.closeBtn.setStyle('display', this.closeBtnState);
36543 this.stickBtn.hide();
36545 this.fireEvent("slidehide", this);
36548 slideIn : function(cb){
36549 if(!this.isSlid || this.el.hasActiveFx()){
36553 this.isSlid = false;
36554 this.beforeSlide();
36555 this.el.slideOut(this.getSlideAnchor(), {
36556 callback: function(){
36557 this.el.setLeftTop(-10000, -10000);
36559 this.afterSlideIn();
36567 slideInIf : function(e){
36568 if(!e.within(this.el)){
36573 animateCollapse : function(){
36574 this.beforeSlide();
36575 this.el.setStyle("z-index", 20000);
36576 var anchor = this.getSlideAnchor();
36577 this.el.slideOut(anchor, {
36578 callback : function(){
36579 this.el.setStyle("z-index", "");
36580 this.collapsedEl.slideIn(anchor, {duration:.3});
36582 this.el.setLocation(-10000,-10000);
36584 this.fireEvent("collapsed", this);
36591 animateExpand : function(){
36592 this.beforeSlide();
36593 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36594 this.el.setStyle("z-index", 20000);
36595 this.collapsedEl.hide({
36598 this.el.slideIn(this.getSlideAnchor(), {
36599 callback : function(){
36600 this.el.setStyle("z-index", "");
36603 this.split.el.show();
36605 this.fireEvent("invalidated", this);
36606 this.fireEvent("expanded", this);
36634 getAnchor : function(){
36635 return this.anchors[this.position];
36638 getCollapseAnchor : function(){
36639 return this.canchors[this.position];
36642 getSlideAnchor : function(){
36643 return this.sanchors[this.position];
36646 getAlignAdj : function(){
36647 var cm = this.cmargins;
36648 switch(this.position){
36664 getExpandAdj : function(){
36665 var c = this.collapsedEl, cm = this.cmargins;
36666 switch(this.position){
36668 return [-(cm.right+c.getWidth()+cm.left), 0];
36671 return [cm.right+c.getWidth()+cm.left, 0];
36674 return [0, -(cm.top+cm.bottom+c.getHeight())];
36677 return [0, cm.top+cm.bottom+c.getHeight()];
36683 * Ext JS Library 1.1.1
36684 * Copyright(c) 2006-2007, Ext JS, LLC.
36686 * Originally Released Under LGPL - original licence link has changed is not relivant.
36689 * <script type="text/javascript">
36692 * These classes are private internal classes
36694 Roo.bootstrap.layout.Center = function(config){
36695 config.region = "center";
36696 Roo.bootstrap.layout.Region.call(this, config);
36697 this.visible = true;
36698 this.minWidth = config.minWidth || 20;
36699 this.minHeight = config.minHeight || 20;
36702 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36704 // center panel can't be hidden
36708 // center panel can't be hidden
36711 getMinWidth: function(){
36712 return this.minWidth;
36715 getMinHeight: function(){
36716 return this.minHeight;
36729 Roo.bootstrap.layout.North = function(config)
36731 config.region = 'north';
36732 config.cursor = 'n-resize';
36734 Roo.bootstrap.layout.Split.call(this, config);
36738 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36739 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36740 this.split.el.addClass("roo-layout-split-v");
36742 var size = config.initialSize || config.height;
36743 if(typeof size != "undefined"){
36744 this.el.setHeight(size);
36747 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36749 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36753 getBox : function(){
36754 if(this.collapsed){
36755 return this.collapsedEl.getBox();
36757 var box = this.el.getBox();
36759 box.height += this.split.el.getHeight();
36764 updateBox : function(box){
36765 if(this.split && !this.collapsed){
36766 box.height -= this.split.el.getHeight();
36767 this.split.el.setLeft(box.x);
36768 this.split.el.setTop(box.y+box.height);
36769 this.split.el.setWidth(box.width);
36771 if(this.collapsed){
36772 this.updateBody(box.width, null);
36774 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36782 Roo.bootstrap.layout.South = function(config){
36783 config.region = 'south';
36784 config.cursor = 's-resize';
36785 Roo.bootstrap.layout.Split.call(this, config);
36787 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36788 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36789 this.split.el.addClass("roo-layout-split-v");
36791 var size = config.initialSize || config.height;
36792 if(typeof size != "undefined"){
36793 this.el.setHeight(size);
36797 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36798 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36799 getBox : function(){
36800 if(this.collapsed){
36801 return this.collapsedEl.getBox();
36803 var box = this.el.getBox();
36805 var sh = this.split.el.getHeight();
36812 updateBox : function(box){
36813 if(this.split && !this.collapsed){
36814 var sh = this.split.el.getHeight();
36817 this.split.el.setLeft(box.x);
36818 this.split.el.setTop(box.y-sh);
36819 this.split.el.setWidth(box.width);
36821 if(this.collapsed){
36822 this.updateBody(box.width, null);
36824 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36828 Roo.bootstrap.layout.East = function(config){
36829 config.region = "east";
36830 config.cursor = "e-resize";
36831 Roo.bootstrap.layout.Split.call(this, config);
36833 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36834 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36835 this.split.el.addClass("roo-layout-split-h");
36837 var size = config.initialSize || config.width;
36838 if(typeof size != "undefined"){
36839 this.el.setWidth(size);
36842 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36843 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36844 getBox : function(){
36845 if(this.collapsed){
36846 return this.collapsedEl.getBox();
36848 var box = this.el.getBox();
36850 var sw = this.split.el.getWidth();
36857 updateBox : function(box){
36858 if(this.split && !this.collapsed){
36859 var sw = this.split.el.getWidth();
36861 this.split.el.setLeft(box.x);
36862 this.split.el.setTop(box.y);
36863 this.split.el.setHeight(box.height);
36866 if(this.collapsed){
36867 this.updateBody(null, box.height);
36869 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36873 Roo.bootstrap.layout.West = function(config){
36874 config.region = "west";
36875 config.cursor = "w-resize";
36877 Roo.bootstrap.layout.Split.call(this, config);
36879 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36880 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36881 this.split.el.addClass("roo-layout-split-h");
36885 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36886 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36888 onRender: function(ctr, pos)
36890 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36891 var size = this.config.initialSize || this.config.width;
36892 if(typeof size != "undefined"){
36893 this.el.setWidth(size);
36897 getBox : function(){
36898 if(this.collapsed){
36899 return this.collapsedEl.getBox();
36901 var box = this.el.getBox();
36903 box.width += this.split.el.getWidth();
36908 updateBox : function(box){
36909 if(this.split && !this.collapsed){
36910 var sw = this.split.el.getWidth();
36912 this.split.el.setLeft(box.x+box.width);
36913 this.split.el.setTop(box.y);
36914 this.split.el.setHeight(box.height);
36916 if(this.collapsed){
36917 this.updateBody(null, box.height);
36919 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36922 Roo.namespace("Roo.bootstrap.panel");/*
36924 * Ext JS Library 1.1.1
36925 * Copyright(c) 2006-2007, Ext JS, LLC.
36927 * Originally Released Under LGPL - original licence link has changed is not relivant.
36930 * <script type="text/javascript">
36933 * @class Roo.ContentPanel
36934 * @extends Roo.util.Observable
36935 * A basic ContentPanel element.
36936 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36937 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36938 * @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
36939 * @cfg {Boolean} closable True if the panel can be closed/removed
36940 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36941 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36942 * @cfg {Toolbar} toolbar A toolbar for this panel
36943 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36944 * @cfg {String} title The title for this panel
36945 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36946 * @cfg {String} url Calls {@link #setUrl} with this value
36947 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36948 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36949 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36950 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36951 * @cfg {Boolean} badges render the badges
36954 * Create a new ContentPanel.
36955 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36956 * @param {String/Object} config A string to set only the title or a config object
36957 * @param {String} content (optional) Set the HTML content for this panel
36958 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36960 Roo.bootstrap.panel.Content = function( config){
36962 this.tpl = config.tpl || false;
36964 var el = config.el;
36965 var content = config.content;
36967 if(config.autoCreate){ // xtype is available if this is called from factory
36970 this.el = Roo.get(el);
36971 if(!this.el && config && config.autoCreate){
36972 if(typeof config.autoCreate == "object"){
36973 if(!config.autoCreate.id){
36974 config.autoCreate.id = config.id||el;
36976 this.el = Roo.DomHelper.append(document.body,
36977 config.autoCreate, true);
36979 var elcfg = { tag: "div",
36980 cls: "roo-layout-inactive-content",
36984 elcfg.html = config.html;
36988 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36991 this.closable = false;
36992 this.loaded = false;
36993 this.active = false;
36996 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36998 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37000 this.wrapEl = this.el; //this.el.wrap();
37002 if (config.toolbar.items) {
37003 ti = config.toolbar.items ;
37004 delete config.toolbar.items ;
37008 this.toolbar.render(this.wrapEl, 'before');
37009 for(var i =0;i < ti.length;i++) {
37010 // Roo.log(['add child', items[i]]);
37011 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37013 this.toolbar.items = nitems;
37014 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37015 delete config.toolbar;
37019 // xtype created footer. - not sure if will work as we normally have to render first..
37020 if (this.footer && !this.footer.el && this.footer.xtype) {
37021 if (!this.wrapEl) {
37022 this.wrapEl = this.el.wrap();
37025 this.footer.container = this.wrapEl.createChild();
37027 this.footer = Roo.factory(this.footer, Roo);
37032 if(typeof config == "string"){
37033 this.title = config;
37035 Roo.apply(this, config);
37039 this.resizeEl = Roo.get(this.resizeEl, true);
37041 this.resizeEl = this.el;
37043 // handle view.xtype
37051 * Fires when this panel is activated.
37052 * @param {Roo.ContentPanel} this
37056 * @event deactivate
37057 * Fires when this panel is activated.
37058 * @param {Roo.ContentPanel} this
37060 "deactivate" : true,
37064 * Fires when this panel is resized if fitToFrame is true.
37065 * @param {Roo.ContentPanel} this
37066 * @param {Number} width The width after any component adjustments
37067 * @param {Number} height The height after any component adjustments
37073 * Fires when this tab is created
37074 * @param {Roo.ContentPanel} this
37085 if(this.autoScroll){
37086 this.resizeEl.setStyle("overflow", "auto");
37088 // fix randome scrolling
37089 //this.el.on('scroll', function() {
37090 // Roo.log('fix random scolling');
37091 // this.scrollTo('top',0);
37094 content = content || this.content;
37096 this.setContent(content);
37098 if(config && config.url){
37099 this.setUrl(this.url, this.params, this.loadOnce);
37104 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37106 if (this.view && typeof(this.view.xtype) != 'undefined') {
37107 this.view.el = this.el.appendChild(document.createElement("div"));
37108 this.view = Roo.factory(this.view);
37109 this.view.render && this.view.render(false, '');
37113 this.fireEvent('render', this);
37116 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37120 setRegion : function(region){
37121 this.region = region;
37122 this.setActiveClass(region && !this.background);
37126 setActiveClass: function(state)
37129 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37130 this.el.setStyle('position','relative');
37132 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37133 this.el.setStyle('position', 'absolute');
37138 * Returns the toolbar for this Panel if one was configured.
37139 * @return {Roo.Toolbar}
37141 getToolbar : function(){
37142 return this.toolbar;
37145 setActiveState : function(active)
37147 this.active = active;
37148 this.setActiveClass(active);
37150 if(this.fireEvent("deactivate", this) === false){
37155 this.fireEvent("activate", this);
37159 * Updates this panel's element
37160 * @param {String} content The new content
37161 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37163 setContent : function(content, loadScripts){
37164 this.el.update(content, loadScripts);
37167 ignoreResize : function(w, h){
37168 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37171 this.lastSize = {width: w, height: h};
37176 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37177 * @return {Roo.UpdateManager} The UpdateManager
37179 getUpdateManager : function(){
37180 return this.el.getUpdateManager();
37183 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37184 * @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:
37187 url: "your-url.php",
37188 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37189 callback: yourFunction,
37190 scope: yourObject, //(optional scope)
37193 text: "Loading...",
37198 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37199 * 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.
37200 * @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}
37201 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37202 * @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.
37203 * @return {Roo.ContentPanel} this
37206 var um = this.el.getUpdateManager();
37207 um.update.apply(um, arguments);
37213 * 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.
37214 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37215 * @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)
37216 * @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)
37217 * @return {Roo.UpdateManager} The UpdateManager
37219 setUrl : function(url, params, loadOnce){
37220 if(this.refreshDelegate){
37221 this.removeListener("activate", this.refreshDelegate);
37223 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37224 this.on("activate", this.refreshDelegate);
37225 return this.el.getUpdateManager();
37228 _handleRefresh : function(url, params, loadOnce){
37229 if(!loadOnce || !this.loaded){
37230 var updater = this.el.getUpdateManager();
37231 updater.update(url, params, this._setLoaded.createDelegate(this));
37235 _setLoaded : function(){
37236 this.loaded = true;
37240 * Returns this panel's id
37243 getId : function(){
37248 * Returns this panel's element - used by regiosn to add.
37249 * @return {Roo.Element}
37251 getEl : function(){
37252 return this.wrapEl || this.el;
37257 adjustForComponents : function(width, height)
37259 //Roo.log('adjustForComponents ');
37260 if(this.resizeEl != this.el){
37261 width -= this.el.getFrameWidth('lr');
37262 height -= this.el.getFrameWidth('tb');
37265 var te = this.toolbar.getEl();
37266 te.setWidth(width);
37267 height -= te.getHeight();
37270 var te = this.footer.getEl();
37271 te.setWidth(width);
37272 height -= te.getHeight();
37276 if(this.adjustments){
37277 width += this.adjustments[0];
37278 height += this.adjustments[1];
37280 return {"width": width, "height": height};
37283 setSize : function(width, height){
37284 if(this.fitToFrame && !this.ignoreResize(width, height)){
37285 if(this.fitContainer && this.resizeEl != this.el){
37286 this.el.setSize(width, height);
37288 var size = this.adjustForComponents(width, height);
37289 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37290 this.fireEvent('resize', this, size.width, size.height);
37295 * Returns this panel's title
37298 getTitle : function(){
37300 if (typeof(this.title) != 'object') {
37305 for (var k in this.title) {
37306 if (!this.title.hasOwnProperty(k)) {
37310 if (k.indexOf('-') >= 0) {
37311 var s = k.split('-');
37312 for (var i = 0; i<s.length; i++) {
37313 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37316 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37323 * Set this panel's title
37324 * @param {String} title
37326 setTitle : function(title){
37327 this.title = title;
37329 this.region.updatePanelTitle(this, title);
37334 * Returns true is this panel was configured to be closable
37335 * @return {Boolean}
37337 isClosable : function(){
37338 return this.closable;
37341 beforeSlide : function(){
37343 this.resizeEl.clip();
37346 afterSlide : function(){
37348 this.resizeEl.unclip();
37352 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37353 * Will fail silently if the {@link #setUrl} method has not been called.
37354 * This does not activate the panel, just updates its content.
37356 refresh : function(){
37357 if(this.refreshDelegate){
37358 this.loaded = false;
37359 this.refreshDelegate();
37364 * Destroys this panel
37366 destroy : function(){
37367 this.el.removeAllListeners();
37368 var tempEl = document.createElement("span");
37369 tempEl.appendChild(this.el.dom);
37370 tempEl.innerHTML = "";
37376 * form - if the content panel contains a form - this is a reference to it.
37377 * @type {Roo.form.Form}
37381 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37382 * This contains a reference to it.
37388 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37398 * @param {Object} cfg Xtype definition of item to add.
37402 getChildContainer: function () {
37403 return this.getEl();
37408 var ret = new Roo.factory(cfg);
37413 if (cfg.xtype.match(/^Form$/)) {
37416 //if (this.footer) {
37417 // el = this.footer.container.insertSibling(false, 'before');
37419 el = this.el.createChild();
37422 this.form = new Roo.form.Form(cfg);
37425 if ( this.form.allItems.length) {
37426 this.form.render(el.dom);
37430 // should only have one of theses..
37431 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37432 // views.. should not be just added - used named prop 'view''
37434 cfg.el = this.el.appendChild(document.createElement("div"));
37437 var ret = new Roo.factory(cfg);
37439 ret.render && ret.render(false, ''); // render blank..
37449 * @class Roo.bootstrap.panel.Grid
37450 * @extends Roo.bootstrap.panel.Content
37452 * Create a new GridPanel.
37453 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37454 * @param {Object} config A the config object
37460 Roo.bootstrap.panel.Grid = function(config)
37464 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37465 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37467 config.el = this.wrapper;
37468 //this.el = this.wrapper;
37470 if (config.container) {
37471 // ctor'ed from a Border/panel.grid
37474 this.wrapper.setStyle("overflow", "hidden");
37475 this.wrapper.addClass('roo-grid-container');
37480 if(config.toolbar){
37481 var tool_el = this.wrapper.createChild();
37482 this.toolbar = Roo.factory(config.toolbar);
37484 if (config.toolbar.items) {
37485 ti = config.toolbar.items ;
37486 delete config.toolbar.items ;
37490 this.toolbar.render(tool_el);
37491 for(var i =0;i < ti.length;i++) {
37492 // Roo.log(['add child', items[i]]);
37493 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37495 this.toolbar.items = nitems;
37497 delete config.toolbar;
37500 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37501 config.grid.scrollBody = true;;
37502 config.grid.monitorWindowResize = false; // turn off autosizing
37503 config.grid.autoHeight = false;
37504 config.grid.autoWidth = false;
37506 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37508 if (config.background) {
37509 // render grid on panel activation (if panel background)
37510 this.on('activate', function(gp) {
37511 if (!gp.grid.rendered) {
37512 gp.grid.render(this.wrapper);
37513 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37518 this.grid.render(this.wrapper);
37519 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37522 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37523 // ??? needed ??? config.el = this.wrapper;
37528 // xtype created footer. - not sure if will work as we normally have to render first..
37529 if (this.footer && !this.footer.el && this.footer.xtype) {
37531 var ctr = this.grid.getView().getFooterPanel(true);
37532 this.footer.dataSource = this.grid.dataSource;
37533 this.footer = Roo.factory(this.footer, Roo);
37534 this.footer.render(ctr);
37544 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37545 getId : function(){
37546 return this.grid.id;
37550 * Returns the grid for this panel
37551 * @return {Roo.bootstrap.Table}
37553 getGrid : function(){
37557 setSize : function(width, height){
37558 if(!this.ignoreResize(width, height)){
37559 var grid = this.grid;
37560 var size = this.adjustForComponents(width, height);
37561 var gridel = grid.getGridEl();
37562 gridel.setSize(size.width, size.height);
37564 var thd = grid.getGridEl().select('thead',true).first();
37565 var tbd = grid.getGridEl().select('tbody', true).first();
37567 tbd.setSize(width, height - thd.getHeight());
37576 beforeSlide : function(){
37577 this.grid.getView().scroller.clip();
37580 afterSlide : function(){
37581 this.grid.getView().scroller.unclip();
37584 destroy : function(){
37585 this.grid.destroy();
37587 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37592 * @class Roo.bootstrap.panel.Nest
37593 * @extends Roo.bootstrap.panel.Content
37595 * Create a new Panel, that can contain a layout.Border.
37598 * @param {Roo.BorderLayout} layout The layout for this panel
37599 * @param {String/Object} config A string to set only the title or a config object
37601 Roo.bootstrap.panel.Nest = function(config)
37603 // construct with only one argument..
37604 /* FIXME - implement nicer consturctors
37605 if (layout.layout) {
37607 layout = config.layout;
37608 delete config.layout;
37610 if (layout.xtype && !layout.getEl) {
37611 // then layout needs constructing..
37612 layout = Roo.factory(layout, Roo);
37616 config.el = config.layout.getEl();
37618 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37620 config.layout.monitorWindowResize = false; // turn off autosizing
37621 this.layout = config.layout;
37622 this.layout.getEl().addClass("roo-layout-nested-layout");
37629 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37631 setSize : function(width, height){
37632 if(!this.ignoreResize(width, height)){
37633 var size = this.adjustForComponents(width, height);
37634 var el = this.layout.getEl();
37635 if (size.height < 1) {
37636 el.setWidth(size.width);
37638 el.setSize(size.width, size.height);
37640 var touch = el.dom.offsetWidth;
37641 this.layout.layout();
37642 // ie requires a double layout on the first pass
37643 if(Roo.isIE && !this.initialized){
37644 this.initialized = true;
37645 this.layout.layout();
37650 // activate all subpanels if not currently active..
37652 setActiveState : function(active){
37653 this.active = active;
37654 this.setActiveClass(active);
37657 this.fireEvent("deactivate", this);
37661 this.fireEvent("activate", this);
37662 // not sure if this should happen before or after..
37663 if (!this.layout) {
37664 return; // should not happen..
37667 for (var r in this.layout.regions) {
37668 reg = this.layout.getRegion(r);
37669 if (reg.getActivePanel()) {
37670 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37671 reg.setActivePanel(reg.getActivePanel());
37674 if (!reg.panels.length) {
37677 reg.showPanel(reg.getPanel(0));
37686 * Returns the nested BorderLayout for this panel
37687 * @return {Roo.BorderLayout}
37689 getLayout : function(){
37690 return this.layout;
37694 * Adds a xtype elements to the layout of the nested panel
37698 xtype : 'ContentPanel',
37705 xtype : 'NestedLayoutPanel',
37711 items : [ ... list of content panels or nested layout panels.. ]
37715 * @param {Object} cfg Xtype definition of item to add.
37717 addxtype : function(cfg) {
37718 return this.layout.addxtype(cfg);
37723 * Ext JS Library 1.1.1
37724 * Copyright(c) 2006-2007, Ext JS, LLC.
37726 * Originally Released Under LGPL - original licence link has changed is not relivant.
37729 * <script type="text/javascript">
37732 * @class Roo.TabPanel
37733 * @extends Roo.util.Observable
37734 * A lightweight tab container.
37738 // basic tabs 1, built from existing content
37739 var tabs = new Roo.TabPanel("tabs1");
37740 tabs.addTab("script", "View Script");
37741 tabs.addTab("markup", "View Markup");
37742 tabs.activate("script");
37744 // more advanced tabs, built from javascript
37745 var jtabs = new Roo.TabPanel("jtabs");
37746 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37748 // set up the UpdateManager
37749 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37750 var updater = tab2.getUpdateManager();
37751 updater.setDefaultUrl("ajax1.htm");
37752 tab2.on('activate', updater.refresh, updater, true);
37754 // Use setUrl for Ajax loading
37755 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37756 tab3.setUrl("ajax2.htm", null, true);
37759 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37762 jtabs.activate("jtabs-1");
37765 * Create a new TabPanel.
37766 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37767 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37769 Roo.bootstrap.panel.Tabs = function(config){
37771 * The container element for this TabPanel.
37772 * @type Roo.Element
37774 this.el = Roo.get(config.el);
37777 if(typeof config == "boolean"){
37778 this.tabPosition = config ? "bottom" : "top";
37780 Roo.apply(this, config);
37784 if(this.tabPosition == "bottom"){
37785 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37786 this.el.addClass("roo-tabs-bottom");
37788 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37789 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37790 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37792 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37794 if(this.tabPosition != "bottom"){
37795 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37796 * @type Roo.Element
37798 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37799 this.el.addClass("roo-tabs-top");
37803 this.bodyEl.setStyle("position", "relative");
37805 this.active = null;
37806 this.activateDelegate = this.activate.createDelegate(this);
37811 * Fires when the active tab changes
37812 * @param {Roo.TabPanel} this
37813 * @param {Roo.TabPanelItem} activePanel The new active tab
37817 * @event beforetabchange
37818 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37819 * @param {Roo.TabPanel} this
37820 * @param {Object} e Set cancel to true on this object to cancel the tab change
37821 * @param {Roo.TabPanelItem} tab The tab being changed to
37823 "beforetabchange" : true
37826 Roo.EventManager.onWindowResize(this.onResize, this);
37827 this.cpad = this.el.getPadding("lr");
37828 this.hiddenCount = 0;
37831 // toolbar on the tabbar support...
37832 if (this.toolbar) {
37833 alert("no toolbar support yet");
37834 this.toolbar = false;
37836 var tcfg = this.toolbar;
37837 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37838 this.toolbar = new Roo.Toolbar(tcfg);
37839 if (Roo.isSafari) {
37840 var tbl = tcfg.container.child('table', true);
37841 tbl.setAttribute('width', '100%');
37849 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37852 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37854 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37856 tabPosition : "top",
37858 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37860 currentTabWidth : 0,
37862 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37866 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37870 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37872 preferredTabWidth : 175,
37874 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37876 resizeTabs : false,
37878 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37880 monitorResize : true,
37882 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37887 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37888 * @param {String} id The id of the div to use <b>or create</b>
37889 * @param {String} text The text for the tab
37890 * @param {String} content (optional) Content to put in the TabPanelItem body
37891 * @param {Boolean} closable (optional) True to create a close icon on the tab
37892 * @return {Roo.TabPanelItem} The created TabPanelItem
37894 addTab : function(id, text, content, closable, tpl)
37896 var item = new Roo.bootstrap.panel.TabItem({
37900 closable : closable,
37903 this.addTabItem(item);
37905 item.setContent(content);
37911 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37912 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37913 * @return {Roo.TabPanelItem}
37915 getTab : function(id){
37916 return this.items[id];
37920 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37921 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37923 hideTab : function(id){
37924 var t = this.items[id];
37927 this.hiddenCount++;
37928 this.autoSizeTabs();
37933 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37934 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37936 unhideTab : function(id){
37937 var t = this.items[id];
37939 t.setHidden(false);
37940 this.hiddenCount--;
37941 this.autoSizeTabs();
37946 * Adds an existing {@link Roo.TabPanelItem}.
37947 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37949 addTabItem : function(item){
37950 this.items[item.id] = item;
37951 this.items.push(item);
37952 // if(this.resizeTabs){
37953 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37954 // this.autoSizeTabs();
37956 // item.autoSize();
37961 * Removes a {@link Roo.TabPanelItem}.
37962 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37964 removeTab : function(id){
37965 var items = this.items;
37966 var tab = items[id];
37967 if(!tab) { return; }
37968 var index = items.indexOf(tab);
37969 if(this.active == tab && items.length > 1){
37970 var newTab = this.getNextAvailable(index);
37975 this.stripEl.dom.removeChild(tab.pnode.dom);
37976 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37977 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37979 items.splice(index, 1);
37980 delete this.items[tab.id];
37981 tab.fireEvent("close", tab);
37982 tab.purgeListeners();
37983 this.autoSizeTabs();
37986 getNextAvailable : function(start){
37987 var items = this.items;
37989 // look for a next tab that will slide over to
37990 // replace the one being removed
37991 while(index < items.length){
37992 var item = items[++index];
37993 if(item && !item.isHidden()){
37997 // if one isn't found select the previous tab (on the left)
38000 var item = items[--index];
38001 if(item && !item.isHidden()){
38009 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38010 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38012 disableTab : function(id){
38013 var tab = this.items[id];
38014 if(tab && this.active != tab){
38020 * Enables a {@link Roo.TabPanelItem} that is disabled.
38021 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38023 enableTab : function(id){
38024 var tab = this.items[id];
38029 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38030 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38031 * @return {Roo.TabPanelItem} The TabPanelItem.
38033 activate : function(id){
38034 var tab = this.items[id];
38038 if(tab == this.active || tab.disabled){
38042 this.fireEvent("beforetabchange", this, e, tab);
38043 if(e.cancel !== true && !tab.disabled){
38045 this.active.hide();
38047 this.active = this.items[id];
38048 this.active.show();
38049 this.fireEvent("tabchange", this, this.active);
38055 * Gets the active {@link Roo.TabPanelItem}.
38056 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38058 getActiveTab : function(){
38059 return this.active;
38063 * Updates the tab body element to fit the height of the container element
38064 * for overflow scrolling
38065 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38067 syncHeight : function(targetHeight){
38068 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38069 var bm = this.bodyEl.getMargins();
38070 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38071 this.bodyEl.setHeight(newHeight);
38075 onResize : function(){
38076 if(this.monitorResize){
38077 this.autoSizeTabs();
38082 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38084 beginUpdate : function(){
38085 this.updating = true;
38089 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38091 endUpdate : function(){
38092 this.updating = false;
38093 this.autoSizeTabs();
38097 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38099 autoSizeTabs : function(){
38100 var count = this.items.length;
38101 var vcount = count - this.hiddenCount;
38102 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38105 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38106 var availWidth = Math.floor(w / vcount);
38107 var b = this.stripBody;
38108 if(b.getWidth() > w){
38109 var tabs = this.items;
38110 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38111 if(availWidth < this.minTabWidth){
38112 /*if(!this.sleft){ // incomplete scrolling code
38113 this.createScrollButtons();
38116 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38119 if(this.currentTabWidth < this.preferredTabWidth){
38120 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38126 * Returns the number of tabs in this TabPanel.
38129 getCount : function(){
38130 return this.items.length;
38134 * Resizes all the tabs to the passed width
38135 * @param {Number} The new width
38137 setTabWidth : function(width){
38138 this.currentTabWidth = width;
38139 for(var i = 0, len = this.items.length; i < len; i++) {
38140 if(!this.items[i].isHidden()) {
38141 this.items[i].setWidth(width);
38147 * Destroys this TabPanel
38148 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38150 destroy : function(removeEl){
38151 Roo.EventManager.removeResizeListener(this.onResize, this);
38152 for(var i = 0, len = this.items.length; i < len; i++){
38153 this.items[i].purgeListeners();
38155 if(removeEl === true){
38156 this.el.update("");
38161 createStrip : function(container)
38163 var strip = document.createElement("nav");
38164 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38165 container.appendChild(strip);
38169 createStripList : function(strip)
38171 // div wrapper for retard IE
38172 // returns the "tr" element.
38173 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38174 //'<div class="x-tabs-strip-wrap">'+
38175 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38176 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38177 return strip.firstChild; //.firstChild.firstChild.firstChild;
38179 createBody : function(container)
38181 var body = document.createElement("div");
38182 Roo.id(body, "tab-body");
38183 //Roo.fly(body).addClass("x-tabs-body");
38184 Roo.fly(body).addClass("tab-content");
38185 container.appendChild(body);
38188 createItemBody :function(bodyEl, id){
38189 var body = Roo.getDom(id);
38191 body = document.createElement("div");
38194 //Roo.fly(body).addClass("x-tabs-item-body");
38195 Roo.fly(body).addClass("tab-pane");
38196 bodyEl.insertBefore(body, bodyEl.firstChild);
38200 createStripElements : function(stripEl, text, closable, tpl)
38202 var td = document.createElement("li"); // was td..
38205 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38208 stripEl.appendChild(td);
38210 td.className = "x-tabs-closable";
38211 if(!this.closeTpl){
38212 this.closeTpl = new Roo.Template(
38213 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38214 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38215 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38218 var el = this.closeTpl.overwrite(td, {"text": text});
38219 var close = el.getElementsByTagName("div")[0];
38220 var inner = el.getElementsByTagName("em")[0];
38221 return {"el": el, "close": close, "inner": inner};
38224 // not sure what this is..
38225 // if(!this.tabTpl){
38226 //this.tabTpl = new Roo.Template(
38227 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38228 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38230 // this.tabTpl = new Roo.Template(
38231 // '<a href="#">' +
38232 // '<span unselectable="on"' +
38233 // (this.disableTooltips ? '' : ' title="{text}"') +
38234 // ' >{text}</span></a>'
38240 var template = tpl || this.tabTpl || false;
38244 template = new Roo.Template(
38246 '<span unselectable="on"' +
38247 (this.disableTooltips ? '' : ' title="{text}"') +
38248 ' >{text}</span></a>'
38252 switch (typeof(template)) {
38256 template = new Roo.Template(template);
38262 var el = template.overwrite(td, {"text": text});
38264 var inner = el.getElementsByTagName("span")[0];
38266 return {"el": el, "inner": inner};
38274 * @class Roo.TabPanelItem
38275 * @extends Roo.util.Observable
38276 * Represents an individual item (tab plus body) in a TabPanel.
38277 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38278 * @param {String} id The id of this TabPanelItem
38279 * @param {String} text The text for the tab of this TabPanelItem
38280 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38282 Roo.bootstrap.panel.TabItem = function(config){
38284 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38285 * @type Roo.TabPanel
38287 this.tabPanel = config.panel;
38289 * The id for this TabPanelItem
38292 this.id = config.id;
38294 this.disabled = false;
38296 this.text = config.text;
38298 this.loaded = false;
38299 this.closable = config.closable;
38302 * The body element for this TabPanelItem.
38303 * @type Roo.Element
38305 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38306 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38307 this.bodyEl.setStyle("display", "block");
38308 this.bodyEl.setStyle("zoom", "1");
38309 //this.hideAction();
38311 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38313 this.el = Roo.get(els.el);
38314 this.inner = Roo.get(els.inner, true);
38315 this.textEl = Roo.get(this.el.dom.firstChild, true);
38316 this.pnode = Roo.get(els.el.parentNode, true);
38317 // this.el.on("mousedown", this.onTabMouseDown, this);
38318 this.el.on("click", this.onTabClick, this);
38320 if(config.closable){
38321 var c = Roo.get(els.close, true);
38322 c.dom.title = this.closeText;
38323 c.addClassOnOver("close-over");
38324 c.on("click", this.closeClick, this);
38330 * Fires when this tab becomes the active tab.
38331 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38332 * @param {Roo.TabPanelItem} this
38336 * @event beforeclose
38337 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38338 * @param {Roo.TabPanelItem} this
38339 * @param {Object} e Set cancel to true on this object to cancel the close.
38341 "beforeclose": true,
38344 * Fires when this tab is closed.
38345 * @param {Roo.TabPanelItem} this
38349 * @event deactivate
38350 * Fires when this tab is no longer the active tab.
38351 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38352 * @param {Roo.TabPanelItem} this
38354 "deactivate" : true
38356 this.hidden = false;
38358 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38361 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38363 purgeListeners : function(){
38364 Roo.util.Observable.prototype.purgeListeners.call(this);
38365 this.el.removeAllListeners();
38368 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38371 this.pnode.addClass("active");
38374 this.tabPanel.stripWrap.repaint();
38376 this.fireEvent("activate", this.tabPanel, this);
38380 * Returns true if this tab is the active tab.
38381 * @return {Boolean}
38383 isActive : function(){
38384 return this.tabPanel.getActiveTab() == this;
38388 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38391 this.pnode.removeClass("active");
38393 this.fireEvent("deactivate", this.tabPanel, this);
38396 hideAction : function(){
38397 this.bodyEl.hide();
38398 this.bodyEl.setStyle("position", "absolute");
38399 this.bodyEl.setLeft("-20000px");
38400 this.bodyEl.setTop("-20000px");
38403 showAction : function(){
38404 this.bodyEl.setStyle("position", "relative");
38405 this.bodyEl.setTop("");
38406 this.bodyEl.setLeft("");
38407 this.bodyEl.show();
38411 * Set the tooltip for the tab.
38412 * @param {String} tooltip The tab's tooltip
38414 setTooltip : function(text){
38415 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38416 this.textEl.dom.qtip = text;
38417 this.textEl.dom.removeAttribute('title');
38419 this.textEl.dom.title = text;
38423 onTabClick : function(e){
38424 e.preventDefault();
38425 this.tabPanel.activate(this.id);
38428 onTabMouseDown : function(e){
38429 e.preventDefault();
38430 this.tabPanel.activate(this.id);
38433 getWidth : function(){
38434 return this.inner.getWidth();
38437 setWidth : function(width){
38438 var iwidth = width - this.pnode.getPadding("lr");
38439 this.inner.setWidth(iwidth);
38440 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38441 this.pnode.setWidth(width);
38445 * Show or hide the tab
38446 * @param {Boolean} hidden True to hide or false to show.
38448 setHidden : function(hidden){
38449 this.hidden = hidden;
38450 this.pnode.setStyle("display", hidden ? "none" : "");
38454 * Returns true if this tab is "hidden"
38455 * @return {Boolean}
38457 isHidden : function(){
38458 return this.hidden;
38462 * Returns the text for this tab
38465 getText : function(){
38469 autoSize : function(){
38470 //this.el.beginMeasure();
38471 this.textEl.setWidth(1);
38473 * #2804 [new] Tabs in Roojs
38474 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38476 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38477 //this.el.endMeasure();
38481 * Sets the text for the tab (Note: this also sets the tooltip text)
38482 * @param {String} text The tab's text and tooltip
38484 setText : function(text){
38486 this.textEl.update(text);
38487 this.setTooltip(text);
38488 //if(!this.tabPanel.resizeTabs){
38489 // this.autoSize();
38493 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38495 activate : function(){
38496 this.tabPanel.activate(this.id);
38500 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38502 disable : function(){
38503 if(this.tabPanel.active != this){
38504 this.disabled = true;
38505 this.pnode.addClass("disabled");
38510 * Enables this TabPanelItem if it was previously disabled.
38512 enable : function(){
38513 this.disabled = false;
38514 this.pnode.removeClass("disabled");
38518 * Sets the content for this TabPanelItem.
38519 * @param {String} content The content
38520 * @param {Boolean} loadScripts true to look for and load scripts
38522 setContent : function(content, loadScripts){
38523 this.bodyEl.update(content, loadScripts);
38527 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38528 * @return {Roo.UpdateManager} The UpdateManager
38530 getUpdateManager : function(){
38531 return this.bodyEl.getUpdateManager();
38535 * Set a URL to be used to load the content for this TabPanelItem.
38536 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38537 * @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)
38538 * @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)
38539 * @return {Roo.UpdateManager} The UpdateManager
38541 setUrl : function(url, params, loadOnce){
38542 if(this.refreshDelegate){
38543 this.un('activate', this.refreshDelegate);
38545 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38546 this.on("activate", this.refreshDelegate);
38547 return this.bodyEl.getUpdateManager();
38551 _handleRefresh : function(url, params, loadOnce){
38552 if(!loadOnce || !this.loaded){
38553 var updater = this.bodyEl.getUpdateManager();
38554 updater.update(url, params, this._setLoaded.createDelegate(this));
38559 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38560 * Will fail silently if the setUrl method has not been called.
38561 * This does not activate the panel, just updates its content.
38563 refresh : function(){
38564 if(this.refreshDelegate){
38565 this.loaded = false;
38566 this.refreshDelegate();
38571 _setLoaded : function(){
38572 this.loaded = true;
38576 closeClick : function(e){
38579 this.fireEvent("beforeclose", this, o);
38580 if(o.cancel !== true){
38581 this.tabPanel.removeTab(this.id);
38585 * The text displayed in the tooltip for the close icon.
38588 closeText : "Close this tab"
38591 * This script refer to:
38592 * Title: International Telephone Input
38593 * Author: Jack O'Connor
38594 * Code version: v12.1.12
38595 * Availability: https://github.com/jackocnr/intl-tel-input.git
38598 Roo.bootstrap.PhoneInputData = function() {
38601 "Afghanistan (افغانستان)",
38606 "Albania (Shqipëri)",
38611 "Algeria (الجزائر)",
38636 "Antigua and Barbuda",
38646 "Armenia (Հայաստան)",
38662 "Austria (Österreich)",
38667 "Azerbaijan (Azərbaycan)",
38677 "Bahrain (البحرين)",
38682 "Bangladesh (বাংলাদেশ)",
38692 "Belarus (Беларусь)",
38697 "Belgium (België)",
38727 "Bosnia and Herzegovina (Босна и Херцеговина)",
38742 "British Indian Ocean Territory",
38747 "British Virgin Islands",
38757 "Bulgaria (България)",
38767 "Burundi (Uburundi)",
38772 "Cambodia (កម្ពុជា)",
38777 "Cameroon (Cameroun)",
38786 ["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"]
38789 "Cape Verde (Kabu Verdi)",
38794 "Caribbean Netherlands",
38805 "Central African Republic (République centrafricaine)",
38825 "Christmas Island",
38831 "Cocos (Keeling) Islands",
38842 "Comoros (جزر القمر)",
38847 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38852 "Congo (Republic) (Congo-Brazzaville)",
38872 "Croatia (Hrvatska)",
38893 "Czech Republic (Česká republika)",
38898 "Denmark (Danmark)",
38913 "Dominican Republic (República Dominicana)",
38917 ["809", "829", "849"]
38935 "Equatorial Guinea (Guinea Ecuatorial)",
38955 "Falkland Islands (Islas Malvinas)",
38960 "Faroe Islands (Føroyar)",
38981 "French Guiana (Guyane française)",
38986 "French Polynesia (Polynésie française)",
39001 "Georgia (საქართველო)",
39006 "Germany (Deutschland)",
39026 "Greenland (Kalaallit Nunaat)",
39063 "Guinea-Bissau (Guiné Bissau)",
39088 "Hungary (Magyarország)",
39093 "Iceland (Ísland)",
39113 "Iraq (العراق)",
39129 "Israel (ישראל)",
39156 "Jordan (الأردن)",
39161 "Kazakhstan (Казахстан)",
39182 "Kuwait (الكويت)",
39187 "Kyrgyzstan (Кыргызстан)",
39197 "Latvia (Latvija)",
39202 "Lebanon (لبنان)",
39217 "Libya (ليبيا)",
39227 "Lithuania (Lietuva)",
39242 "Macedonia (FYROM) (Македонија)",
39247 "Madagascar (Madagasikara)",
39277 "Marshall Islands",
39287 "Mauritania (موريتانيا)",
39292 "Mauritius (Moris)",
39313 "Moldova (Republica Moldova)",
39323 "Mongolia (Монгол)",
39328 "Montenegro (Crna Gora)",
39338 "Morocco (المغرب)",
39344 "Mozambique (Moçambique)",
39349 "Myanmar (Burma) (မြန်မာ)",
39354 "Namibia (Namibië)",
39369 "Netherlands (Nederland)",
39374 "New Caledonia (Nouvelle-Calédonie)",
39409 "North Korea (조선 민주주의 인민 공화국)",
39414 "Northern Mariana Islands",
39430 "Pakistan (پاکستان)",
39440 "Palestine (فلسطين)",
39450 "Papua New Guinea",
39492 "Réunion (La Réunion)",
39498 "Romania (România)",
39514 "Saint Barthélemy",
39525 "Saint Kitts and Nevis",
39535 "Saint Martin (Saint-Martin (partie française))",
39541 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39546 "Saint Vincent and the Grenadines",
39561 "São Tomé and Príncipe (São Tomé e Príncipe)",
39566 "Saudi Arabia (المملكة العربية السعودية)",
39571 "Senegal (Sénégal)",
39601 "Slovakia (Slovensko)",
39606 "Slovenia (Slovenija)",
39616 "Somalia (Soomaaliya)",
39626 "South Korea (대한민국)",
39631 "South Sudan (جنوب السودان)",
39641 "Sri Lanka (ශ්රී ලංකාව)",
39646 "Sudan (السودان)",
39656 "Svalbard and Jan Mayen",
39667 "Sweden (Sverige)",
39672 "Switzerland (Schweiz)",
39677 "Syria (سوريا)",
39722 "Trinidad and Tobago",
39727 "Tunisia (تونس)",
39732 "Turkey (Türkiye)",
39742 "Turks and Caicos Islands",
39752 "U.S. Virgin Islands",
39762 "Ukraine (Україна)",
39767 "United Arab Emirates (الإمارات العربية المتحدة)",
39789 "Uzbekistan (Oʻzbekiston)",
39799 "Vatican City (Città del Vaticano)",
39810 "Vietnam (Việt Nam)",
39815 "Wallis and Futuna (Wallis-et-Futuna)",
39820 "Western Sahara (الصحراء الغربية)",
39826 "Yemen (اليمن)",
39850 * This script refer to:
39851 * Title: International Telephone Input
39852 * Author: Jack O'Connor
39853 * Code version: v12.1.12
39854 * Availability: https://github.com/jackocnr/intl-tel-input.git
39858 * @class Roo.bootstrap.PhoneInput
39859 * @extends Roo.bootstrap.TriggerField
39860 * An input with International dial-code selection
39862 * @cfg {String} defaultDialCode default '+852'
39863 * @cfg {Array} preferedCountries default []
39866 * Create a new PhoneInput.
39867 * @param {Object} config Configuration options
39870 Roo.bootstrap.PhoneInput = function(config) {
39871 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39874 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39876 listWidth: undefined,
39878 selectedClass: 'active',
39880 invalidClass : "has-warning",
39882 validClass: 'has-success',
39884 allowed: '0123456789',
39887 * @cfg {String} defaultDialCode The default dial code when initializing the input
39889 defaultDialCode: '+852',
39892 * @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
39894 preferedCountries: false,
39896 getAutoCreate : function()
39898 var data = Roo.bootstrap.PhoneInputData();
39899 var align = this.labelAlign || this.parentLabelAlign();
39902 this.allCountries = [];
39903 this.dialCodeMapping = [];
39905 for (var i = 0; i < data.length; i++) {
39907 this.allCountries[i] = {
39911 priority: c[3] || 0,
39912 areaCodes: c[4] || null
39914 this.dialCodeMapping[c[2]] = {
39917 priority: c[3] || 0,
39918 areaCodes: c[4] || null
39930 cls : 'form-control tel-input',
39931 autocomplete: 'new-password'
39934 var hiddenInput = {
39937 cls: 'hidden-tel-input'
39941 hiddenInput.name = this.name;
39944 if (this.disabled) {
39945 input.disabled = true;
39948 var flag_container = {
39965 cls: this.hasFeedback ? 'has-feedback' : '',
39971 cls: 'dial-code-holder',
39978 cls: 'roo-select2-container input-group',
39985 if (this.fieldLabel.length) {
39988 tooltip: 'This field is required'
39994 cls: 'control-label',
40000 html: this.fieldLabel
40003 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40009 if(this.indicatorpos == 'right') {
40010 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40017 if(align == 'left') {
40025 if(this.labelWidth > 12){
40026 label.style = "width: " + this.labelWidth + 'px';
40028 if(this.labelWidth < 13 && this.labelmd == 0){
40029 this.labelmd = this.labelWidth;
40031 if(this.labellg > 0){
40032 label.cls += ' col-lg-' + this.labellg;
40033 input.cls += ' col-lg-' + (12 - this.labellg);
40035 if(this.labelmd > 0){
40036 label.cls += ' col-md-' + this.labelmd;
40037 container.cls += ' col-md-' + (12 - this.labelmd);
40039 if(this.labelsm > 0){
40040 label.cls += ' col-sm-' + this.labelsm;
40041 container.cls += ' col-sm-' + (12 - this.labelsm);
40043 if(this.labelxs > 0){
40044 label.cls += ' col-xs-' + this.labelxs;
40045 container.cls += ' col-xs-' + (12 - this.labelxs);
40055 var settings = this;
40057 ['xs','sm','md','lg'].map(function(size){
40058 if (settings[size]) {
40059 cfg.cls += ' col-' + size + '-' + settings[size];
40063 this.store = new Roo.data.Store({
40064 proxy : new Roo.data.MemoryProxy({}),
40065 reader : new Roo.data.JsonReader({
40076 'name' : 'dialCode',
40080 'name' : 'priority',
40084 'name' : 'areaCodes',
40091 if(!this.preferedCountries) {
40092 this.preferedCountries = [
40099 var p = this.preferedCountries.reverse();
40102 for (var i = 0; i < p.length; i++) {
40103 for (var j = 0; j < this.allCountries.length; j++) {
40104 if(this.allCountries[j].iso2 == p[i]) {
40105 var t = this.allCountries[j];
40106 this.allCountries.splice(j,1);
40107 this.allCountries.unshift(t);
40113 this.store.proxy.data = {
40115 data: this.allCountries
40121 initEvents : function()
40124 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40126 this.indicator = this.indicatorEl();
40127 this.flag = this.flagEl();
40128 this.dialCodeHolder = this.dialCodeHolderEl();
40130 this.trigger = this.el.select('div.flag-box',true).first();
40131 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40136 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40137 _this.list.setWidth(lw);
40140 this.list.on('mouseover', this.onViewOver, this);
40141 this.list.on('mousemove', this.onViewMove, this);
40142 this.inputEl().on("keyup", this.onKeyUp, this);
40144 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40146 this.view = new Roo.View(this.list, this.tpl, {
40147 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40150 this.view.on('click', this.onViewClick, this);
40151 this.setValue(this.defaultDialCode);
40154 onTriggerClick : function(e)
40156 Roo.log('trigger click');
40161 if(this.isExpanded()){
40163 this.hasFocus = false;
40165 this.store.load({});
40166 this.hasFocus = true;
40171 isExpanded : function()
40173 return this.list.isVisible();
40176 collapse : function()
40178 if(!this.isExpanded()){
40182 Roo.get(document).un('mousedown', this.collapseIf, this);
40183 Roo.get(document).un('mousewheel', this.collapseIf, this);
40184 this.fireEvent('collapse', this);
40188 expand : function()
40192 if(this.isExpanded() || !this.hasFocus){
40196 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40197 this.list.setWidth(lw);
40200 this.restrictHeight();
40202 Roo.get(document).on('mousedown', this.collapseIf, this);
40203 Roo.get(document).on('mousewheel', this.collapseIf, this);
40205 this.fireEvent('expand', this);
40208 restrictHeight : function()
40210 this.list.alignTo(this.inputEl(), this.listAlign);
40211 this.list.alignTo(this.inputEl(), this.listAlign);
40214 onViewOver : function(e, t)
40216 if(this.inKeyMode){
40219 var item = this.view.findItemFromChild(t);
40222 var index = this.view.indexOf(item);
40223 this.select(index, false);
40228 onViewClick : function(view, doFocus, el, e)
40230 var index = this.view.getSelectedIndexes()[0];
40232 var r = this.store.getAt(index);
40235 this.onSelect(r, index);
40237 if(doFocus !== false && !this.blockFocus){
40238 this.inputEl().focus();
40242 onViewMove : function(e, t)
40244 this.inKeyMode = false;
40247 select : function(index, scrollIntoView)
40249 this.selectedIndex = index;
40250 this.view.select(index);
40251 if(scrollIntoView !== false){
40252 var el = this.view.getNode(index);
40254 this.list.scrollChildIntoView(el, false);
40259 createList : function()
40261 this.list = Roo.get(document.body).createChild({
40263 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40264 style: 'display:none'
40267 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40270 collapseIf : function(e)
40272 var in_combo = e.within(this.el);
40273 var in_list = e.within(this.list);
40274 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40276 if (in_combo || in_list || is_list) {
40282 onSelect : function(record, index)
40284 if(this.fireEvent('beforeselect', this, record, index) !== false){
40286 this.setFlagClass(record.data.iso2);
40287 this.setDialCode(record.data.dialCode);
40288 this.hasFocus = false;
40290 this.fireEvent('select', this, record, index);
40294 flagEl : function()
40296 var flag = this.el.select('div.flag',true).first();
40303 dialCodeHolderEl : function()
40305 var d = this.el.select('input.dial-code-holder',true).first();
40312 setDialCode : function(v)
40314 this.dialCodeHolder.dom.value = '+'+v;
40317 setFlagClass : function(n)
40319 this.flag.dom.className = 'flag '+n;
40322 getValue : function()
40324 var v = this.inputEl().getValue();
40325 if(this.dialCodeHolder) {
40326 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40331 setValue : function(v)
40333 var d = this.getDialCode(v);
40335 //invalid dial code
40336 if(v.length == 0 || !d || d.length == 0) {
40338 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40339 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40345 this.setFlagClass(this.dialCodeMapping[d].iso2);
40346 this.setDialCode(d);
40347 this.inputEl().dom.value = v.replace('+'+d,'');
40348 this.hiddenEl().dom.value = this.getValue();
40353 getDialCode : function(v)
40357 if (v.length == 0) {
40358 return this.dialCodeHolder.dom.value;
40362 if (v.charAt(0) != "+") {
40365 var numericChars = "";
40366 for (var i = 1; i < v.length; i++) {
40367 var c = v.charAt(i);
40370 if (this.dialCodeMapping[numericChars]) {
40371 dialCode = v.substr(1, i);
40373 if (numericChars.length == 4) {
40383 this.setValue(this.defaultDialCode);
40387 hiddenEl : function()
40389 return this.el.select('input.hidden-tel-input',true).first();
40392 onKeyUp : function(e){
40394 var k = e.getKey();
40395 var c = e.getCharCode();
40398 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40399 this.allowed.indexOf(String.fromCharCode(c)) === -1
40404 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40407 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40411 this.setValue(this.getValue());
40416 * @class Roo.bootstrap.MoneyField
40417 * @extends Roo.bootstrap.ComboBox
40418 * Bootstrap MoneyField class
40421 * Create a new MoneyField.
40422 * @param {Object} config Configuration options
40425 Roo.bootstrap.MoneyField = function(config) {
40427 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40431 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40434 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40436 allowDecimals : true,
40438 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40440 decimalSeparator : ".",
40442 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40444 decimalPrecision : 0,
40446 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40448 allowNegative : true,
40450 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40454 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40456 minValue : Number.NEGATIVE_INFINITY,
40458 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40460 maxValue : Number.MAX_VALUE,
40462 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40464 minText : "The minimum value for this field is {0}",
40466 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40468 maxText : "The maximum value for this field is {0}",
40470 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40471 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40473 nanText : "{0} is not a valid number",
40475 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40479 * @cfg {String} defaults currency of the MoneyField
40480 * value should be in lkey
40482 defaultCurrency : false,
40484 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40486 thousandsDelimiter : false,
40496 getAutoCreate : function()
40498 var align = this.labelAlign || this.parentLabelAlign();
40510 cls : 'form-control roo-money-amount-input',
40511 autocomplete: 'new-password'
40514 var hiddenInput = {
40518 cls: 'hidden-number-input'
40522 hiddenInput.name = this.name;
40525 if (this.disabled) {
40526 input.disabled = true;
40529 var clg = 12 - this.inputlg;
40530 var cmd = 12 - this.inputmd;
40531 var csm = 12 - this.inputsm;
40532 var cxs = 12 - this.inputxs;
40536 cls : 'row roo-money-field',
40540 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40544 cls: 'roo-select2-container input-group',
40548 cls : 'form-control roo-money-currency-input',
40549 autocomplete: 'new-password',
40551 name : this.currencyName
40555 cls : 'input-group-addon',
40569 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40573 cls: this.hasFeedback ? 'has-feedback' : '',
40584 if (this.fieldLabel.length) {
40587 tooltip: 'This field is required'
40593 cls: 'control-label',
40599 html: this.fieldLabel
40602 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40608 if(this.indicatorpos == 'right') {
40609 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40616 if(align == 'left') {
40624 if(this.labelWidth > 12){
40625 label.style = "width: " + this.labelWidth + 'px';
40627 if(this.labelWidth < 13 && this.labelmd == 0){
40628 this.labelmd = this.labelWidth;
40630 if(this.labellg > 0){
40631 label.cls += ' col-lg-' + this.labellg;
40632 input.cls += ' col-lg-' + (12 - this.labellg);
40634 if(this.labelmd > 0){
40635 label.cls += ' col-md-' + this.labelmd;
40636 container.cls += ' col-md-' + (12 - this.labelmd);
40638 if(this.labelsm > 0){
40639 label.cls += ' col-sm-' + this.labelsm;
40640 container.cls += ' col-sm-' + (12 - this.labelsm);
40642 if(this.labelxs > 0){
40643 label.cls += ' col-xs-' + this.labelxs;
40644 container.cls += ' col-xs-' + (12 - this.labelxs);
40655 var settings = this;
40657 ['xs','sm','md','lg'].map(function(size){
40658 if (settings[size]) {
40659 cfg.cls += ' col-' + size + '-' + settings[size];
40666 initEvents : function()
40668 this.indicator = this.indicatorEl();
40670 this.initCurrencyEvent();
40672 this.initNumberEvent();
40675 initCurrencyEvent : function()
40678 throw "can not find store for combo";
40681 this.store = Roo.factory(this.store, Roo.data);
40682 this.store.parent = this;
40686 this.triggerEl = this.el.select('.input-group-addon', true).first();
40688 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40693 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40694 _this.list.setWidth(lw);
40697 this.list.on('mouseover', this.onViewOver, this);
40698 this.list.on('mousemove', this.onViewMove, this);
40699 this.list.on('scroll', this.onViewScroll, this);
40702 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40705 this.view = new Roo.View(this.list, this.tpl, {
40706 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40709 this.view.on('click', this.onViewClick, this);
40711 this.store.on('beforeload', this.onBeforeLoad, this);
40712 this.store.on('load', this.onLoad, this);
40713 this.store.on('loadexception', this.onLoadException, this);
40715 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40716 "up" : function(e){
40717 this.inKeyMode = true;
40721 "down" : function(e){
40722 if(!this.isExpanded()){
40723 this.onTriggerClick();
40725 this.inKeyMode = true;
40730 "enter" : function(e){
40733 if(this.fireEvent("specialkey", this, e)){
40734 this.onViewClick(false);
40740 "esc" : function(e){
40744 "tab" : function(e){
40747 if(this.fireEvent("specialkey", this, e)){
40748 this.onViewClick(false);
40756 doRelay : function(foo, bar, hname){
40757 if(hname == 'down' || this.scope.isExpanded()){
40758 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40766 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40770 initNumberEvent : function(e)
40772 this.inputEl().on("keydown" , this.fireKey, this);
40773 this.inputEl().on("focus", this.onFocus, this);
40774 this.inputEl().on("blur", this.onBlur, this);
40776 this.inputEl().relayEvent('keyup', this);
40778 if(this.indicator){
40779 this.indicator.addClass('invisible');
40782 this.originalValue = this.getValue();
40784 if(this.validationEvent == 'keyup'){
40785 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40786 this.inputEl().on('keyup', this.filterValidation, this);
40788 else if(this.validationEvent !== false){
40789 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40792 if(this.selectOnFocus){
40793 this.on("focus", this.preFocus, this);
40796 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40797 this.inputEl().on("keypress", this.filterKeys, this);
40799 this.inputEl().relayEvent('keypress', this);
40802 var allowed = "0123456789";
40804 if(this.allowDecimals){
40805 allowed += this.decimalSeparator;
40808 if(this.allowNegative){
40812 if(this.thousandsDelimiter) {
40816 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40818 var keyPress = function(e){
40820 var k = e.getKey();
40822 var c = e.getCharCode();
40825 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40826 allowed.indexOf(String.fromCharCode(c)) === -1
40832 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40836 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40841 this.inputEl().on("keypress", keyPress, this);
40845 onTriggerClick : function(e)
40852 this.loadNext = false;
40854 if(this.isExpanded()){
40859 this.hasFocus = true;
40861 if(this.triggerAction == 'all') {
40862 this.doQuery(this.allQuery, true);
40866 this.doQuery(this.getRawValue());
40869 getCurrency : function()
40871 var v = this.currencyEl().getValue();
40876 restrictHeight : function()
40878 this.list.alignTo(this.currencyEl(), this.listAlign);
40879 this.list.alignTo(this.currencyEl(), this.listAlign);
40882 onViewClick : function(view, doFocus, el, e)
40884 var index = this.view.getSelectedIndexes()[0];
40886 var r = this.store.getAt(index);
40889 this.onSelect(r, index);
40893 onSelect : function(record, index){
40895 if(this.fireEvent('beforeselect', this, record, index) !== false){
40897 this.setFromCurrencyData(index > -1 ? record.data : false);
40901 this.fireEvent('select', this, record, index);
40905 setFromCurrencyData : function(o)
40909 this.lastCurrency = o;
40911 if (this.currencyField) {
40912 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40914 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40917 this.lastSelectionText = currency;
40919 //setting default currency
40920 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40921 this.setCurrency(this.defaultCurrency);
40925 this.setCurrency(currency);
40928 setFromData : function(o)
40932 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40934 this.setFromCurrencyData(c);
40939 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40941 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40944 this.setValue(value);
40948 setCurrency : function(v)
40950 this.currencyValue = v;
40953 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40958 setValue : function(v)
40960 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40966 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40968 this.inputEl().dom.value = (v == '') ? '' :
40969 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40971 if(!this.allowZero && v === '0') {
40972 this.hiddenEl().dom.value = '';
40973 this.inputEl().dom.value = '';
40980 getRawValue : function()
40982 var v = this.inputEl().getValue();
40987 getValue : function()
40989 return this.fixPrecision(this.parseValue(this.getRawValue()));
40992 parseValue : function(value)
40994 if(this.thousandsDelimiter) {
40996 r = new RegExp(",", "g");
40997 value = value.replace(r, "");
41000 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41001 return isNaN(value) ? '' : value;
41005 fixPrecision : function(value)
41007 if(this.thousandsDelimiter) {
41009 r = new RegExp(",", "g");
41010 value = value.replace(r, "");
41013 var nan = isNaN(value);
41015 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41016 return nan ? '' : value;
41018 return parseFloat(value).toFixed(this.decimalPrecision);
41021 decimalPrecisionFcn : function(v)
41023 return Math.floor(v);
41026 validateValue : function(value)
41028 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41032 var num = this.parseValue(value);
41035 this.markInvalid(String.format(this.nanText, value));
41039 if(num < this.minValue){
41040 this.markInvalid(String.format(this.minText, this.minValue));
41044 if(num > this.maxValue){
41045 this.markInvalid(String.format(this.maxText, this.maxValue));
41052 validate : function()
41054 if(this.disabled || this.allowBlank){
41059 var currency = this.getCurrency();
41061 if(this.validateValue(this.getRawValue()) && currency.length){
41066 this.markInvalid();
41070 getName: function()
41075 beforeBlur : function()
41081 var v = this.parseValue(this.getRawValue());
41088 onBlur : function()
41092 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41093 //this.el.removeClass(this.focusClass);
41096 this.hasFocus = false;
41098 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41102 var v = this.getValue();
41104 if(String(v) !== String(this.startValue)){
41105 this.fireEvent('change', this, v, this.startValue);
41108 this.fireEvent("blur", this);
41111 inputEl : function()
41113 return this.el.select('.roo-money-amount-input', true).first();
41116 currencyEl : function()
41118 return this.el.select('.roo-money-currency-input', true).first();
41121 hiddenEl : function()
41123 return this.el.select('input.hidden-number-input',true).first();