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';
3934 * navbar-expand-md fixed-top
3938 * @class Roo.bootstrap.NavHeaderbar
3939 * @extends Roo.bootstrap.NavSimplebar
3940 * Bootstrap Sidebar class
3942 * @cfg {String} brand what is brand
3943 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3944 * @cfg {String} brand_href href of the brand
3945 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3946 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3947 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3948 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3951 * Create a new Sidebar
3952 * @param {Object} config The config object
3956 Roo.bootstrap.NavHeaderbar = function(config){
3957 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3961 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3968 desktopCenter : false,
3971 getAutoCreate : function(){
3974 tag: this.nav || 'nav',
3975 cls: 'navbar navbar-expand-md',
3981 if (this.desktopCenter) {
3982 cn.push({cls : 'container', cn : []});
3989 cls: 'navbar-header',
3994 cls: 'navbar-toggle navbar-toggler',
3995 'data-toggle': 'collapse',
4000 html: 'Toggle navigation'
4004 cls: 'icon-bar navbar-toggler-icon'
4022 cls: 'collapse navbar-collapse',
4026 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4028 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4029 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4031 // tag can override this..
4033 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4036 if (this.brand !== '') {
4039 href: this.brand_href ? this.brand_href : '#',
4040 cls: 'navbar-brand',
4048 cfg.cls += ' main-nav';
4056 getHeaderChildContainer : function()
4058 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4059 return this.el.select('.navbar-header',true).first();
4062 return this.getChildContainer();
4066 initEvents : function()
4068 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4070 if (this.autohide) {
4075 Roo.get(document).on('scroll',function(e) {
4076 var ns = Roo.get(document).getScroll().top;
4077 var os = prevScroll;
4081 ft.removeClass('slideDown');
4082 ft.addClass('slideUp');
4085 ft.removeClass('slideUp');
4086 ft.addClass('slideDown');
4107 * @class Roo.bootstrap.NavSidebar
4108 * @extends Roo.bootstrap.Navbar
4109 * Bootstrap Sidebar class
4112 * Create a new Sidebar
4113 * @param {Object} config The config object
4117 Roo.bootstrap.NavSidebar = function(config){
4118 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4121 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4123 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4125 getAutoCreate : function(){
4130 cls: 'sidebar sidebar-nav'
4152 * @class Roo.bootstrap.NavGroup
4153 * @extends Roo.bootstrap.Component
4154 * Bootstrap NavGroup class
4155 * @cfg {String} align (left|right)
4156 * @cfg {Boolean} inverse
4157 * @cfg {String} type (nav|pills|tab) default nav
4158 * @cfg {String} navId - reference Id for navbar.
4162 * Create a new nav group
4163 * @param {Object} config The config object
4166 Roo.bootstrap.NavGroup = function(config){
4167 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4170 Roo.bootstrap.NavGroup.register(this);
4174 * Fires when the active item changes
4175 * @param {Roo.bootstrap.NavGroup} this
4176 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4177 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4184 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4195 getAutoCreate : function()
4197 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4204 if (['tabs','pills'].indexOf(this.type)!==-1) {
4205 cfg.cls += ' nav-' + this.type
4207 if (this.type!=='nav') {
4208 Roo.log('nav type must be nav/tabs/pills')
4210 cfg.cls += ' navbar-nav mr-auto'
4213 if (this.parent() && this.parent().sidebar) {
4216 cls: 'dashboard-menu sidebar-menu'
4222 if (this.form === true) {
4228 if (this.align === 'right') {
4229 cfg.cls += ' navbar-right';
4231 cfg.cls += ' navbar-left';
4235 if (this.align === 'right') {
4236 cfg.cls += ' navbar-right';
4240 cfg.cls += ' navbar-inverse';
4248 * sets the active Navigation item
4249 * @param {Roo.bootstrap.NavItem} the new current navitem
4251 setActiveItem : function(item)
4254 Roo.each(this.navItems, function(v){
4259 v.setActive(false, true);
4266 item.setActive(true, true);
4267 this.fireEvent('changed', this, item, prev);
4272 * gets the active Navigation item
4273 * @return {Roo.bootstrap.NavItem} the current navitem
4275 getActive : function()
4279 Roo.each(this.navItems, function(v){
4290 indexOfNav : function()
4294 Roo.each(this.navItems, function(v,i){
4305 * adds a Navigation item
4306 * @param {Roo.bootstrap.NavItem} the navitem to add
4308 addItem : function(cfg)
4310 var cn = new Roo.bootstrap.NavItem(cfg);
4312 cn.parentId = this.id;
4313 cn.onRender(this.el, null);
4317 * register a Navigation item
4318 * @param {Roo.bootstrap.NavItem} the navitem to add
4320 register : function(item)
4322 this.navItems.push( item);
4323 item.navId = this.navId;
4328 * clear all the Navigation item
4331 clearAll : function()
4334 this.el.dom.innerHTML = '';
4337 getNavItem: function(tabId)
4340 Roo.each(this.navItems, function(e) {
4341 if (e.tabId == tabId) {
4351 setActiveNext : function()
4353 var i = this.indexOfNav(this.getActive());
4354 if (i > this.navItems.length) {
4357 this.setActiveItem(this.navItems[i+1]);
4359 setActivePrev : function()
4361 var i = this.indexOfNav(this.getActive());
4365 this.setActiveItem(this.navItems[i-1]);
4367 clearWasActive : function(except) {
4368 Roo.each(this.navItems, function(e) {
4369 if (e.tabId != except.tabId && e.was_active) {
4370 e.was_active = false;
4377 getWasActive : function ()
4380 Roo.each(this.navItems, function(e) {
4395 Roo.apply(Roo.bootstrap.NavGroup, {
4399 * register a Navigation Group
4400 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4402 register : function(navgrp)
4404 this.groups[navgrp.navId] = navgrp;
4408 * fetch a Navigation Group based on the navigation ID
4409 * @param {string} the navgroup to add
4410 * @returns {Roo.bootstrap.NavGroup} the navgroup
4412 get: function(navId) {
4413 if (typeof(this.groups[navId]) == 'undefined') {
4415 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4417 return this.groups[navId] ;
4432 * @class Roo.bootstrap.NavItem
4433 * @extends Roo.bootstrap.Component
4434 * Bootstrap Navbar.NavItem class
4435 * @cfg {String} href link to
4436 * @cfg {String} html content of button
4437 * @cfg {String} badge text inside badge
4438 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4439 * @cfg {String} glyphicon name of glyphicon
4440 * @cfg {String} icon name of font awesome icon
4441 * @cfg {Boolean} active Is item active
4442 * @cfg {Boolean} disabled Is item disabled
4444 * @cfg {Boolean} preventDefault (true | false) default false
4445 * @cfg {String} tabId the tab that this item activates.
4446 * @cfg {String} tagtype (a|span) render as a href or span?
4447 * @cfg {Boolean} animateRef (true|false) link to element default false
4450 * Create a new Navbar Item
4451 * @param {Object} config The config object
4453 Roo.bootstrap.NavItem = function(config){
4454 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4459 * The raw click event for the entire grid.
4460 * @param {Roo.EventObject} e
4465 * Fires when the active item active state changes
4466 * @param {Roo.bootstrap.NavItem} this
4467 * @param {boolean} state the new state
4473 * Fires when scroll to element
4474 * @param {Roo.bootstrap.NavItem} this
4475 * @param {Object} options
4476 * @param {Roo.EventObject} e
4484 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4492 preventDefault : false,
4499 getAutoCreate : function(){
4508 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4510 if (this.disabled) {
4511 cfg.cls += ' disabled';
4514 if (this.href || this.html || this.glyphicon || this.icon) {
4518 href : this.href || "#",
4519 html: this.html || ''
4522 if (this.tagtype == 'a') {
4523 cfg.cn[0].cls = 'nav-link';
4526 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4529 if(this.glyphicon) {
4530 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4535 cfg.cn[0].html += " <span class='caret'></span>";
4539 if (this.badge !== '') {
4541 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4549 initEvents: function()
4551 if (typeof (this.menu) != 'undefined') {
4552 this.menu.parentType = this.xtype;
4553 this.menu.triggerEl = this.el;
4554 this.menu = this.addxtype(Roo.apply({}, this.menu));
4557 this.el.select('a',true).on('click', this.onClick, this);
4559 if(this.tagtype == 'span'){
4560 this.el.select('span',true).on('click', this.onClick, this);
4563 // at this point parent should be available..
4564 this.parent().register(this);
4567 onClick : function(e)
4569 if (e.getTarget('.dropdown-menu-item')) {
4570 // did you click on a menu itemm.... - then don't trigger onclick..
4575 this.preventDefault ||
4578 Roo.log("NavItem - prevent Default?");
4582 if (this.disabled) {
4586 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4587 if (tg && tg.transition) {
4588 Roo.log("waiting for the transitionend");
4594 //Roo.log("fire event clicked");
4595 if(this.fireEvent('click', this, e) === false){
4599 if(this.tagtype == 'span'){
4603 //Roo.log(this.href);
4604 var ael = this.el.select('a',true).first();
4607 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4608 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4609 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4610 return; // ignore... - it's a 'hash' to another page.
4612 Roo.log("NavItem - prevent Default?");
4614 this.scrollToElement(e);
4618 var p = this.parent();
4620 if (['tabs','pills'].indexOf(p.type)!==-1) {
4621 if (typeof(p.setActiveItem) !== 'undefined') {
4622 p.setActiveItem(this);
4626 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4627 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4628 // remove the collapsed menu expand...
4629 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4633 isActive: function () {
4636 setActive : function(state, fire, is_was_active)
4638 if (this.active && !state && this.navId) {
4639 this.was_active = true;
4640 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4642 nv.clearWasActive(this);
4646 this.active = state;
4649 this.el.removeClass('active');
4650 } else if (!this.el.hasClass('active')) {
4651 this.el.addClass('active');
4654 this.fireEvent('changed', this, state);
4657 // show a panel if it's registered and related..
4659 if (!this.navId || !this.tabId || !state || is_was_active) {
4663 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4667 var pan = tg.getPanelByName(this.tabId);
4671 // if we can not flip to new panel - go back to old nav highlight..
4672 if (false == tg.showPanel(pan)) {
4673 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4675 var onav = nv.getWasActive();
4677 onav.setActive(true, false, true);
4686 // this should not be here...
4687 setDisabled : function(state)
4689 this.disabled = state;
4691 this.el.removeClass('disabled');
4692 } else if (!this.el.hasClass('disabled')) {
4693 this.el.addClass('disabled');
4699 * Fetch the element to display the tooltip on.
4700 * @return {Roo.Element} defaults to this.el
4702 tooltipEl : function()
4704 return this.el.select('' + this.tagtype + '', true).first();
4707 scrollToElement : function(e)
4709 var c = document.body;
4712 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4714 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4715 c = document.documentElement;
4718 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4724 var o = target.calcOffsetsTo(c);
4731 this.fireEvent('scrollto', this, options, e);
4733 Roo.get(c).scrollTo('top', options.value, true);
4746 * <span> icon </span>
4747 * <span> text </span>
4748 * <span>badge </span>
4752 * @class Roo.bootstrap.NavSidebarItem
4753 * @extends Roo.bootstrap.NavItem
4754 * Bootstrap Navbar.NavSidebarItem class
4755 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4756 * {Boolean} open is the menu open
4757 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4758 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4759 * {String} buttonSize (sm|md|lg)the extra classes for the button
4760 * {Boolean} showArrow show arrow next to the text (default true)
4762 * Create a new Navbar Button
4763 * @param {Object} config The config object
4765 Roo.bootstrap.NavSidebarItem = function(config){
4766 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4771 * The raw click event for the entire grid.
4772 * @param {Roo.EventObject} e
4777 * Fires when the active item active state changes
4778 * @param {Roo.bootstrap.NavSidebarItem} this
4779 * @param {boolean} state the new state
4787 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4789 badgeWeight : 'default',
4795 buttonWeight : 'default',
4801 getAutoCreate : function(){
4806 href : this.href || '#',
4812 if(this.buttonView){
4815 href : this.href || '#',
4816 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4829 cfg.cls += ' active';
4832 if (this.disabled) {
4833 cfg.cls += ' disabled';
4836 cfg.cls += ' open x-open';
4839 if (this.glyphicon || this.icon) {
4840 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4841 a.cn.push({ tag : 'i', cls : c }) ;
4844 if(!this.buttonView){
4847 html : this.html || ''
4854 if (this.badge !== '') {
4855 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4861 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4864 a.cls += ' dropdown-toggle treeview' ;
4870 initEvents : function()
4872 if (typeof (this.menu) != 'undefined') {
4873 this.menu.parentType = this.xtype;
4874 this.menu.triggerEl = this.el;
4875 this.menu = this.addxtype(Roo.apply({}, this.menu));
4878 this.el.on('click', this.onClick, this);
4880 if(this.badge !== ''){
4881 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4886 onClick : function(e)
4893 if(this.preventDefault){
4897 this.fireEvent('click', this);
4900 disable : function()
4902 this.setDisabled(true);
4907 this.setDisabled(false);
4910 setDisabled : function(state)
4912 if(this.disabled == state){
4916 this.disabled = state;
4919 this.el.addClass('disabled');
4923 this.el.removeClass('disabled');
4928 setActive : function(state)
4930 if(this.active == state){
4934 this.active = state;
4937 this.el.addClass('active');
4941 this.el.removeClass('active');
4946 isActive: function ()
4951 setBadge : function(str)
4957 this.badgeEl.dom.innerHTML = str;
4974 * @class Roo.bootstrap.Row
4975 * @extends Roo.bootstrap.Component
4976 * Bootstrap Row class (contains columns...)
4980 * @param {Object} config The config object
4983 Roo.bootstrap.Row = function(config){
4984 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4987 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4989 getAutoCreate : function(){
5008 * @class Roo.bootstrap.Element
5009 * @extends Roo.bootstrap.Component
5010 * Bootstrap Element class
5011 * @cfg {String} html contents of the element
5012 * @cfg {String} tag tag of the element
5013 * @cfg {String} cls class of the element
5014 * @cfg {Boolean} preventDefault (true|false) default false
5015 * @cfg {Boolean} clickable (true|false) default false
5018 * Create a new Element
5019 * @param {Object} config The config object
5022 Roo.bootstrap.Element = function(config){
5023 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5029 * When a element is chick
5030 * @param {Roo.bootstrap.Element} this
5031 * @param {Roo.EventObject} e
5037 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5042 preventDefault: false,
5045 getAutoCreate : function(){
5049 // cls: this.cls, double assign in parent class Component.js :: onRender
5056 initEvents: function()
5058 Roo.bootstrap.Element.superclass.initEvents.call(this);
5061 this.el.on('click', this.onClick, this);
5066 onClick : function(e)
5068 if(this.preventDefault){
5072 this.fireEvent('click', this, e);
5075 getValue : function()
5077 return this.el.dom.innerHTML;
5080 setValue : function(value)
5082 this.el.dom.innerHTML = value;
5097 * @class Roo.bootstrap.Pagination
5098 * @extends Roo.bootstrap.Component
5099 * Bootstrap Pagination class
5100 * @cfg {String} size xs | sm | md | lg
5101 * @cfg {Boolean} inverse false | true
5104 * Create a new Pagination
5105 * @param {Object} config The config object
5108 Roo.bootstrap.Pagination = function(config){
5109 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5112 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5118 getAutoCreate : function(){
5124 cfg.cls += ' inverse';
5130 cfg.cls += " " + this.cls;
5148 * @class Roo.bootstrap.PaginationItem
5149 * @extends Roo.bootstrap.Component
5150 * Bootstrap PaginationItem class
5151 * @cfg {String} html text
5152 * @cfg {String} href the link
5153 * @cfg {Boolean} preventDefault (true | false) default true
5154 * @cfg {Boolean} active (true | false) default false
5155 * @cfg {Boolean} disabled default false
5159 * Create a new PaginationItem
5160 * @param {Object} config The config object
5164 Roo.bootstrap.PaginationItem = function(config){
5165 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5170 * The raw click event for the entire grid.
5171 * @param {Roo.EventObject} e
5177 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5181 preventDefault: true,
5186 getAutoCreate : function(){
5192 href : this.href ? this.href : '#',
5193 html : this.html ? this.html : ''
5203 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5207 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5213 initEvents: function() {
5215 this.el.on('click', this.onClick, this);
5218 onClick : function(e)
5220 Roo.log('PaginationItem on click ');
5221 if(this.preventDefault){
5229 this.fireEvent('click', this, e);
5245 * @class Roo.bootstrap.Slider
5246 * @extends Roo.bootstrap.Component
5247 * Bootstrap Slider class
5250 * Create a new Slider
5251 * @param {Object} config The config object
5254 Roo.bootstrap.Slider = function(config){
5255 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5258 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5260 getAutoCreate : function(){
5264 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5268 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5280 * Ext JS Library 1.1.1
5281 * Copyright(c) 2006-2007, Ext JS, LLC.
5283 * Originally Released Under LGPL - original licence link has changed is not relivant.
5286 * <script type="text/javascript">
5291 * @class Roo.grid.ColumnModel
5292 * @extends Roo.util.Observable
5293 * This is the default implementation of a ColumnModel used by the Grid. It defines
5294 * the columns in the grid.
5297 var colModel = new Roo.grid.ColumnModel([
5298 {header: "Ticker", width: 60, sortable: true, locked: true},
5299 {header: "Company Name", width: 150, sortable: true},
5300 {header: "Market Cap.", width: 100, sortable: true},
5301 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5302 {header: "Employees", width: 100, sortable: true, resizable: false}
5307 * The config options listed for this class are options which may appear in each
5308 * individual column definition.
5309 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5311 * @param {Object} config An Array of column config objects. See this class's
5312 * config objects for details.
5314 Roo.grid.ColumnModel = function(config){
5316 * The config passed into the constructor
5318 this.config = config;
5321 // if no id, create one
5322 // if the column does not have a dataIndex mapping,
5323 // map it to the order it is in the config
5324 for(var i = 0, len = config.length; i < len; i++){
5326 if(typeof c.dataIndex == "undefined"){
5329 if(typeof c.renderer == "string"){
5330 c.renderer = Roo.util.Format[c.renderer];
5332 if(typeof c.id == "undefined"){
5335 if(c.editor && c.editor.xtype){
5336 c.editor = Roo.factory(c.editor, Roo.grid);
5338 if(c.editor && c.editor.isFormField){
5339 c.editor = new Roo.grid.GridEditor(c.editor);
5341 this.lookup[c.id] = c;
5345 * The width of columns which have no width specified (defaults to 100)
5348 this.defaultWidth = 100;
5351 * Default sortable of columns which have no sortable specified (defaults to false)
5354 this.defaultSortable = false;
5358 * @event widthchange
5359 * Fires when the width of a column changes.
5360 * @param {ColumnModel} this
5361 * @param {Number} columnIndex The column index
5362 * @param {Number} newWidth The new width
5364 "widthchange": true,
5366 * @event headerchange
5367 * Fires when the text of a header changes.
5368 * @param {ColumnModel} this
5369 * @param {Number} columnIndex The column index
5370 * @param {Number} newText The new header text
5372 "headerchange": true,
5374 * @event hiddenchange
5375 * Fires when a column is hidden or "unhidden".
5376 * @param {ColumnModel} this
5377 * @param {Number} columnIndex The column index
5378 * @param {Boolean} hidden true if hidden, false otherwise
5380 "hiddenchange": true,
5382 * @event columnmoved
5383 * Fires when a column is moved.
5384 * @param {ColumnModel} this
5385 * @param {Number} oldIndex
5386 * @param {Number} newIndex
5388 "columnmoved" : true,
5390 * @event columlockchange
5391 * Fires when a column's locked state is changed
5392 * @param {ColumnModel} this
5393 * @param {Number} colIndex
5394 * @param {Boolean} locked true if locked
5396 "columnlockchange" : true
5398 Roo.grid.ColumnModel.superclass.constructor.call(this);
5400 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5402 * @cfg {String} header The header text to display in the Grid view.
5405 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5406 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5407 * specified, the column's index is used as an index into the Record's data Array.
5410 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5411 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5414 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5415 * Defaults to the value of the {@link #defaultSortable} property.
5416 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5419 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5422 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5425 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5428 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5431 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5432 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5433 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5434 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5437 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5440 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5443 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5446 * @cfg {String} cursor (Optional)
5449 * @cfg {String} tooltip (Optional)
5452 * @cfg {Number} xs (Optional)
5455 * @cfg {Number} sm (Optional)
5458 * @cfg {Number} md (Optional)
5461 * @cfg {Number} lg (Optional)
5464 * Returns the id of the column at the specified index.
5465 * @param {Number} index The column index
5466 * @return {String} the id
5468 getColumnId : function(index){
5469 return this.config[index].id;
5473 * Returns the column for a specified id.
5474 * @param {String} id The column id
5475 * @return {Object} the column
5477 getColumnById : function(id){
5478 return this.lookup[id];
5483 * Returns the column for a specified dataIndex.
5484 * @param {String} dataIndex The column dataIndex
5485 * @return {Object|Boolean} the column or false if not found
5487 getColumnByDataIndex: function(dataIndex){
5488 var index = this.findColumnIndex(dataIndex);
5489 return index > -1 ? this.config[index] : false;
5493 * Returns the index for a specified column id.
5494 * @param {String} id The column id
5495 * @return {Number} the index, or -1 if not found
5497 getIndexById : function(id){
5498 for(var i = 0, len = this.config.length; i < len; i++){
5499 if(this.config[i].id == id){
5507 * Returns the index for a specified column dataIndex.
5508 * @param {String} dataIndex The column dataIndex
5509 * @return {Number} the index, or -1 if not found
5512 findColumnIndex : function(dataIndex){
5513 for(var i = 0, len = this.config.length; i < len; i++){
5514 if(this.config[i].dataIndex == dataIndex){
5522 moveColumn : function(oldIndex, newIndex){
5523 var c = this.config[oldIndex];
5524 this.config.splice(oldIndex, 1);
5525 this.config.splice(newIndex, 0, c);
5526 this.dataMap = null;
5527 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5530 isLocked : function(colIndex){
5531 return this.config[colIndex].locked === true;
5534 setLocked : function(colIndex, value, suppressEvent){
5535 if(this.isLocked(colIndex) == value){
5538 this.config[colIndex].locked = value;
5540 this.fireEvent("columnlockchange", this, colIndex, value);
5544 getTotalLockedWidth : function(){
5546 for(var i = 0; i < this.config.length; i++){
5547 if(this.isLocked(i) && !this.isHidden(i)){
5548 this.totalWidth += this.getColumnWidth(i);
5554 getLockedCount : function(){
5555 for(var i = 0, len = this.config.length; i < len; i++){
5556 if(!this.isLocked(i)){
5561 return this.config.length;
5565 * Returns the number of columns.
5568 getColumnCount : function(visibleOnly){
5569 if(visibleOnly === true){
5571 for(var i = 0, len = this.config.length; i < len; i++){
5572 if(!this.isHidden(i)){
5578 return this.config.length;
5582 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5583 * @param {Function} fn
5584 * @param {Object} scope (optional)
5585 * @return {Array} result
5587 getColumnsBy : function(fn, scope){
5589 for(var i = 0, len = this.config.length; i < len; i++){
5590 var c = this.config[i];
5591 if(fn.call(scope||this, c, i) === true){
5599 * Returns true if the specified column is sortable.
5600 * @param {Number} col The column index
5603 isSortable : function(col){
5604 if(typeof this.config[col].sortable == "undefined"){
5605 return this.defaultSortable;
5607 return this.config[col].sortable;
5611 * Returns the rendering (formatting) function defined for the column.
5612 * @param {Number} col The column index.
5613 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5615 getRenderer : function(col){
5616 if(!this.config[col].renderer){
5617 return Roo.grid.ColumnModel.defaultRenderer;
5619 return this.config[col].renderer;
5623 * Sets the rendering (formatting) function for a column.
5624 * @param {Number} col The column index
5625 * @param {Function} fn The function to use to process the cell's raw data
5626 * to return HTML markup for the grid view. The render function is called with
5627 * the following parameters:<ul>
5628 * <li>Data value.</li>
5629 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5630 * <li>css A CSS style string to apply to the table cell.</li>
5631 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5632 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5633 * <li>Row index</li>
5634 * <li>Column index</li>
5635 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5637 setRenderer : function(col, fn){
5638 this.config[col].renderer = fn;
5642 * Returns the width for the specified column.
5643 * @param {Number} col The column index
5646 getColumnWidth : function(col){
5647 return this.config[col].width * 1 || this.defaultWidth;
5651 * Sets the width for a column.
5652 * @param {Number} col The column index
5653 * @param {Number} width The new width
5655 setColumnWidth : function(col, width, suppressEvent){
5656 this.config[col].width = width;
5657 this.totalWidth = null;
5659 this.fireEvent("widthchange", this, col, width);
5664 * Returns the total width of all columns.
5665 * @param {Boolean} includeHidden True to include hidden column widths
5668 getTotalWidth : function(includeHidden){
5669 if(!this.totalWidth){
5670 this.totalWidth = 0;
5671 for(var i = 0, len = this.config.length; i < len; i++){
5672 if(includeHidden || !this.isHidden(i)){
5673 this.totalWidth += this.getColumnWidth(i);
5677 return this.totalWidth;
5681 * Returns the header for the specified column.
5682 * @param {Number} col The column index
5685 getColumnHeader : function(col){
5686 return this.config[col].header;
5690 * Sets the header for a column.
5691 * @param {Number} col The column index
5692 * @param {String} header The new header
5694 setColumnHeader : function(col, header){
5695 this.config[col].header = header;
5696 this.fireEvent("headerchange", this, col, header);
5700 * Returns the tooltip for the specified column.
5701 * @param {Number} col The column index
5704 getColumnTooltip : function(col){
5705 return this.config[col].tooltip;
5708 * Sets the tooltip for a column.
5709 * @param {Number} col The column index
5710 * @param {String} tooltip The new tooltip
5712 setColumnTooltip : function(col, tooltip){
5713 this.config[col].tooltip = tooltip;
5717 * Returns the dataIndex for the specified column.
5718 * @param {Number} col The column index
5721 getDataIndex : function(col){
5722 return this.config[col].dataIndex;
5726 * Sets the dataIndex for a column.
5727 * @param {Number} col The column index
5728 * @param {Number} dataIndex The new dataIndex
5730 setDataIndex : function(col, dataIndex){
5731 this.config[col].dataIndex = dataIndex;
5737 * Returns true if the cell is editable.
5738 * @param {Number} colIndex The column index
5739 * @param {Number} rowIndex The row index - this is nto actually used..?
5742 isCellEditable : function(colIndex, rowIndex){
5743 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5747 * Returns the editor defined for the cell/column.
5748 * return false or null to disable editing.
5749 * @param {Number} colIndex The column index
5750 * @param {Number} rowIndex The row index
5753 getCellEditor : function(colIndex, rowIndex){
5754 return this.config[colIndex].editor;
5758 * Sets if a column is editable.
5759 * @param {Number} col The column index
5760 * @param {Boolean} editable True if the column is editable
5762 setEditable : function(col, editable){
5763 this.config[col].editable = editable;
5768 * Returns true if the column is hidden.
5769 * @param {Number} colIndex The column index
5772 isHidden : function(colIndex){
5773 return this.config[colIndex].hidden;
5778 * Returns true if the column width cannot be changed
5780 isFixed : function(colIndex){
5781 return this.config[colIndex].fixed;
5785 * Returns true if the column can be resized
5788 isResizable : function(colIndex){
5789 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5792 * Sets if a column is hidden.
5793 * @param {Number} colIndex The column index
5794 * @param {Boolean} hidden True if the column is hidden
5796 setHidden : function(colIndex, hidden){
5797 this.config[colIndex].hidden = hidden;
5798 this.totalWidth = null;
5799 this.fireEvent("hiddenchange", this, colIndex, hidden);
5803 * Sets the editor for a column.
5804 * @param {Number} col The column index
5805 * @param {Object} editor The editor object
5807 setEditor : function(col, editor){
5808 this.config[col].editor = editor;
5812 Roo.grid.ColumnModel.defaultRenderer = function(value)
5814 if(typeof value == "object") {
5817 if(typeof value == "string" && value.length < 1){
5821 return String.format("{0}", value);
5824 // Alias for backwards compatibility
5825 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5828 * Ext JS Library 1.1.1
5829 * Copyright(c) 2006-2007, Ext JS, LLC.
5831 * Originally Released Under LGPL - original licence link has changed is not relivant.
5834 * <script type="text/javascript">
5838 * @class Roo.LoadMask
5839 * A simple utility class for generically masking elements while loading data. If the element being masked has
5840 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5841 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5842 * element's UpdateManager load indicator and will be destroyed after the initial load.
5844 * Create a new LoadMask
5845 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5846 * @param {Object} config The config object
5848 Roo.LoadMask = function(el, config){
5849 this.el = Roo.get(el);
5850 Roo.apply(this, config);
5852 this.store.on('beforeload', this.onBeforeLoad, this);
5853 this.store.on('load', this.onLoad, this);
5854 this.store.on('loadexception', this.onLoadException, this);
5855 this.removeMask = false;
5857 var um = this.el.getUpdateManager();
5858 um.showLoadIndicator = false; // disable the default indicator
5859 um.on('beforeupdate', this.onBeforeLoad, this);
5860 um.on('update', this.onLoad, this);
5861 um.on('failure', this.onLoad, this);
5862 this.removeMask = true;
5866 Roo.LoadMask.prototype = {
5868 * @cfg {Boolean} removeMask
5869 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5870 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5874 * The text to display in a centered loading message box (defaults to 'Loading...')
5878 * @cfg {String} msgCls
5879 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5881 msgCls : 'x-mask-loading',
5884 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5890 * Disables the mask to prevent it from being displayed
5892 disable : function(){
5893 this.disabled = true;
5897 * Enables the mask so that it can be displayed
5899 enable : function(){
5900 this.disabled = false;
5903 onLoadException : function()
5907 if (typeof(arguments[3]) != 'undefined') {
5908 Roo.MessageBox.alert("Error loading",arguments[3]);
5912 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5913 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5920 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5925 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5929 onBeforeLoad : function(){
5931 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5936 destroy : function(){
5938 this.store.un('beforeload', this.onBeforeLoad, this);
5939 this.store.un('load', this.onLoad, this);
5940 this.store.un('loadexception', this.onLoadException, this);
5942 var um = this.el.getUpdateManager();
5943 um.un('beforeupdate', this.onBeforeLoad, this);
5944 um.un('update', this.onLoad, this);
5945 um.un('failure', this.onLoad, this);
5956 * @class Roo.bootstrap.Table
5957 * @extends Roo.bootstrap.Component
5958 * Bootstrap Table class
5959 * @cfg {String} cls table class
5960 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5961 * @cfg {String} bgcolor Specifies the background color for a table
5962 * @cfg {Number} border Specifies whether the table cells should have borders or not
5963 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5964 * @cfg {Number} cellspacing Specifies the space between cells
5965 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5966 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5967 * @cfg {String} sortable Specifies that the table should be sortable
5968 * @cfg {String} summary Specifies a summary of the content of a table
5969 * @cfg {Number} width Specifies the width of a table
5970 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5972 * @cfg {boolean} striped Should the rows be alternative striped
5973 * @cfg {boolean} bordered Add borders to the table
5974 * @cfg {boolean} hover Add hover highlighting
5975 * @cfg {boolean} condensed Format condensed
5976 * @cfg {boolean} responsive Format condensed
5977 * @cfg {Boolean} loadMask (true|false) default false
5978 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5979 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5980 * @cfg {Boolean} rowSelection (true|false) default false
5981 * @cfg {Boolean} cellSelection (true|false) default false
5982 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5983 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5984 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5985 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5989 * Create a new Table
5990 * @param {Object} config The config object
5993 Roo.bootstrap.Table = function(config){
5994 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5999 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6000 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6001 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6002 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6004 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6006 this.sm.grid = this;
6007 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6008 this.sm = this.selModel;
6009 this.sm.xmodule = this.xmodule || false;
6012 if (this.cm && typeof(this.cm.config) == 'undefined') {
6013 this.colModel = new Roo.grid.ColumnModel(this.cm);
6014 this.cm = this.colModel;
6015 this.cm.xmodule = this.xmodule || false;
6018 this.store= Roo.factory(this.store, Roo.data);
6019 this.ds = this.store;
6020 this.ds.xmodule = this.xmodule || false;
6023 if (this.footer && this.store) {
6024 this.footer.dataSource = this.ds;
6025 this.footer = Roo.factory(this.footer);
6032 * Fires when a cell is clicked
6033 * @param {Roo.bootstrap.Table} this
6034 * @param {Roo.Element} el
6035 * @param {Number} rowIndex
6036 * @param {Number} columnIndex
6037 * @param {Roo.EventObject} e
6041 * @event celldblclick
6042 * Fires when a cell is double clicked
6043 * @param {Roo.bootstrap.Table} this
6044 * @param {Roo.Element} el
6045 * @param {Number} rowIndex
6046 * @param {Number} columnIndex
6047 * @param {Roo.EventObject} e
6049 "celldblclick" : true,
6052 * Fires when a row is clicked
6053 * @param {Roo.bootstrap.Table} this
6054 * @param {Roo.Element} el
6055 * @param {Number} rowIndex
6056 * @param {Roo.EventObject} e
6060 * @event rowdblclick
6061 * Fires when a row is double clicked
6062 * @param {Roo.bootstrap.Table} this
6063 * @param {Roo.Element} el
6064 * @param {Number} rowIndex
6065 * @param {Roo.EventObject} e
6067 "rowdblclick" : true,
6070 * Fires when a mouseover occur
6071 * @param {Roo.bootstrap.Table} this
6072 * @param {Roo.Element} el
6073 * @param {Number} rowIndex
6074 * @param {Number} columnIndex
6075 * @param {Roo.EventObject} e
6080 * Fires when a mouseout occur
6081 * @param {Roo.bootstrap.Table} this
6082 * @param {Roo.Element} el
6083 * @param {Number} rowIndex
6084 * @param {Number} columnIndex
6085 * @param {Roo.EventObject} e
6090 * Fires when a row is rendered, so you can change add a style to it.
6091 * @param {Roo.bootstrap.Table} this
6092 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6096 * @event rowsrendered
6097 * Fires when all the rows have been rendered
6098 * @param {Roo.bootstrap.Table} this
6100 'rowsrendered' : true,
6102 * @event contextmenu
6103 * The raw contextmenu event for the entire grid.
6104 * @param {Roo.EventObject} e
6106 "contextmenu" : true,
6108 * @event rowcontextmenu
6109 * Fires when a row is right clicked
6110 * @param {Roo.bootstrap.Table} this
6111 * @param {Number} rowIndex
6112 * @param {Roo.EventObject} e
6114 "rowcontextmenu" : true,
6116 * @event cellcontextmenu
6117 * Fires when a cell is right clicked
6118 * @param {Roo.bootstrap.Table} this
6119 * @param {Number} rowIndex
6120 * @param {Number} cellIndex
6121 * @param {Roo.EventObject} e
6123 "cellcontextmenu" : true,
6125 * @event headercontextmenu
6126 * Fires when a header is right clicked
6127 * @param {Roo.bootstrap.Table} this
6128 * @param {Number} columnIndex
6129 * @param {Roo.EventObject} e
6131 "headercontextmenu" : true
6135 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6161 rowSelection : false,
6162 cellSelection : false,
6165 // Roo.Element - the tbody
6167 // Roo.Element - thead element
6170 container: false, // used by gridpanel...
6176 auto_hide_footer : false,
6178 getAutoCreate : function()
6180 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6187 if (this.scrollBody) {
6188 cfg.cls += ' table-body-fixed';
6191 cfg.cls += ' table-striped';
6195 cfg.cls += ' table-hover';
6197 if (this.bordered) {
6198 cfg.cls += ' table-bordered';
6200 if (this.condensed) {
6201 cfg.cls += ' table-condensed';
6203 if (this.responsive) {
6204 cfg.cls += ' table-responsive';
6208 cfg.cls+= ' ' +this.cls;
6211 // this lot should be simplifed...
6224 ].forEach(function(k) {
6232 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6235 if(this.store || this.cm){
6236 if(this.headerShow){
6237 cfg.cn.push(this.renderHeader());
6240 cfg.cn.push(this.renderBody());
6242 if(this.footerShow){
6243 cfg.cn.push(this.renderFooter());
6245 // where does this come from?
6246 //cfg.cls+= ' TableGrid';
6249 return { cn : [ cfg ] };
6252 initEvents : function()
6254 if(!this.store || !this.cm){
6257 if (this.selModel) {
6258 this.selModel.initEvents();
6262 //Roo.log('initEvents with ds!!!!');
6264 this.mainBody = this.el.select('tbody', true).first();
6265 this.mainHead = this.el.select('thead', true).first();
6266 this.mainFoot = this.el.select('tfoot', true).first();
6272 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6273 e.on('click', _this.sort, _this);
6276 this.mainBody.on("click", this.onClick, this);
6277 this.mainBody.on("dblclick", this.onDblClick, this);
6279 // why is this done????? = it breaks dialogs??
6280 //this.parent().el.setStyle('position', 'relative');
6284 this.footer.parentId = this.id;
6285 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6288 this.el.select('tfoot tr td').first().addClass('hide');
6293 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6296 this.store.on('load', this.onLoad, this);
6297 this.store.on('beforeload', this.onBeforeLoad, this);
6298 this.store.on('update', this.onUpdate, this);
6299 this.store.on('add', this.onAdd, this);
6300 this.store.on("clear", this.clear, this);
6302 this.el.on("contextmenu", this.onContextMenu, this);
6304 this.mainBody.on('scroll', this.onBodyScroll, this);
6306 this.cm.on("headerchange", this.onHeaderChange, this);
6308 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6312 onContextMenu : function(e, t)
6314 this.processEvent("contextmenu", e);
6317 processEvent : function(name, e)
6319 if (name != 'touchstart' ) {
6320 this.fireEvent(name, e);
6323 var t = e.getTarget();
6325 var cell = Roo.get(t);
6331 if(cell.findParent('tfoot', false, true)){
6335 if(cell.findParent('thead', false, true)){
6337 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6338 cell = Roo.get(t).findParent('th', false, true);
6340 Roo.log("failed to find th in thead?");
6341 Roo.log(e.getTarget());
6346 var cellIndex = cell.dom.cellIndex;
6348 var ename = name == 'touchstart' ? 'click' : name;
6349 this.fireEvent("header" + ename, this, cellIndex, e);
6354 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6355 cell = Roo.get(t).findParent('td', false, true);
6357 Roo.log("failed to find th in tbody?");
6358 Roo.log(e.getTarget());
6363 var row = cell.findParent('tr', false, true);
6364 var cellIndex = cell.dom.cellIndex;
6365 var rowIndex = row.dom.rowIndex - 1;
6369 this.fireEvent("row" + name, this, rowIndex, e);
6373 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6379 onMouseover : function(e, el)
6381 var cell = Roo.get(el);
6387 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6388 cell = cell.findParent('td', false, true);
6391 var row = cell.findParent('tr', false, true);
6392 var cellIndex = cell.dom.cellIndex;
6393 var rowIndex = row.dom.rowIndex - 1; // start from 0
6395 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6399 onMouseout : function(e, el)
6401 var cell = Roo.get(el);
6407 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6408 cell = cell.findParent('td', false, true);
6411 var row = cell.findParent('tr', false, true);
6412 var cellIndex = cell.dom.cellIndex;
6413 var rowIndex = row.dom.rowIndex - 1; // start from 0
6415 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6419 onClick : function(e, el)
6421 var cell = Roo.get(el);
6423 if(!cell || (!this.cellSelection && !this.rowSelection)){
6427 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6428 cell = cell.findParent('td', false, true);
6431 if(!cell || typeof(cell) == 'undefined'){
6435 var row = cell.findParent('tr', false, true);
6437 if(!row || typeof(row) == 'undefined'){
6441 var cellIndex = cell.dom.cellIndex;
6442 var rowIndex = this.getRowIndex(row);
6444 // why??? - should these not be based on SelectionModel?
6445 if(this.cellSelection){
6446 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6449 if(this.rowSelection){
6450 this.fireEvent('rowclick', this, row, rowIndex, e);
6456 onDblClick : function(e,el)
6458 var cell = Roo.get(el);
6460 if(!cell || (!this.cellSelection && !this.rowSelection)){
6464 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6465 cell = cell.findParent('td', false, true);
6468 if(!cell || typeof(cell) == 'undefined'){
6472 var row = cell.findParent('tr', false, true);
6474 if(!row || typeof(row) == 'undefined'){
6478 var cellIndex = cell.dom.cellIndex;
6479 var rowIndex = this.getRowIndex(row);
6481 if(this.cellSelection){
6482 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6485 if(this.rowSelection){
6486 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6490 sort : function(e,el)
6492 var col = Roo.get(el);
6494 if(!col.hasClass('sortable')){
6498 var sort = col.attr('sort');
6501 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6505 this.store.sortInfo = {field : sort, direction : dir};
6508 Roo.log("calling footer first");
6509 this.footer.onClick('first');
6512 this.store.load({ params : { start : 0 } });
6516 renderHeader : function()
6524 this.totalWidth = 0;
6526 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6528 var config = cm.config[i];
6532 cls : 'x-hcol-' + i,
6534 html: cm.getColumnHeader(i)
6539 if(typeof(config.sortable) != 'undefined' && config.sortable){
6541 c.html = '<i class="glyphicon"></i>' + c.html;
6544 if(typeof(config.lgHeader) != 'undefined'){
6545 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6548 if(typeof(config.mdHeader) != 'undefined'){
6549 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6552 if(typeof(config.smHeader) != 'undefined'){
6553 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6556 if(typeof(config.xsHeader) != 'undefined'){
6557 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6564 if(typeof(config.tooltip) != 'undefined'){
6565 c.tooltip = config.tooltip;
6568 if(typeof(config.colspan) != 'undefined'){
6569 c.colspan = config.colspan;
6572 if(typeof(config.hidden) != 'undefined' && config.hidden){
6573 c.style += ' display:none;';
6576 if(typeof(config.dataIndex) != 'undefined'){
6577 c.sort = config.dataIndex;
6582 if(typeof(config.align) != 'undefined' && config.align.length){
6583 c.style += ' text-align:' + config.align + ';';
6586 if(typeof(config.width) != 'undefined'){
6587 c.style += ' width:' + config.width + 'px;';
6588 this.totalWidth += config.width;
6590 this.totalWidth += 100; // assume minimum of 100 per column?
6593 if(typeof(config.cls) != 'undefined'){
6594 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6597 ['xs','sm','md','lg'].map(function(size){
6599 if(typeof(config[size]) == 'undefined'){
6603 if (!config[size]) { // 0 = hidden
6604 c.cls += ' hidden-' + size;
6608 c.cls += ' col-' + size + '-' + config[size];
6618 renderBody : function()
6628 colspan : this.cm.getColumnCount()
6638 renderFooter : function()
6648 colspan : this.cm.getColumnCount()
6662 // Roo.log('ds onload');
6667 var ds = this.store;
6669 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6670 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6671 if (_this.store.sortInfo) {
6673 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6674 e.select('i', true).addClass(['glyphicon-arrow-up']);
6677 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6678 e.select('i', true).addClass(['glyphicon-arrow-down']);
6683 var tbody = this.mainBody;
6685 if(ds.getCount() > 0){
6686 ds.data.each(function(d,rowIndex){
6687 var row = this.renderRow(cm, ds, rowIndex);
6689 tbody.createChild(row);
6693 if(row.cellObjects.length){
6694 Roo.each(row.cellObjects, function(r){
6695 _this.renderCellObject(r);
6702 var tfoot = this.el.select('tfoot', true).first();
6704 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6706 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6708 var total = this.ds.getTotalCount();
6710 if(this.footer.pageSize < total){
6711 this.mainFoot.show();
6715 Roo.each(this.el.select('tbody td', true).elements, function(e){
6716 e.on('mouseover', _this.onMouseover, _this);
6719 Roo.each(this.el.select('tbody td', true).elements, function(e){
6720 e.on('mouseout', _this.onMouseout, _this);
6722 this.fireEvent('rowsrendered', this);
6728 onUpdate : function(ds,record)
6730 this.refreshRow(record);
6734 onRemove : function(ds, record, index, isUpdate){
6735 if(isUpdate !== true){
6736 this.fireEvent("beforerowremoved", this, index, record);
6738 var bt = this.mainBody.dom;
6740 var rows = this.el.select('tbody > tr', true).elements;
6742 if(typeof(rows[index]) != 'undefined'){
6743 bt.removeChild(rows[index].dom);
6746 // if(bt.rows[index]){
6747 // bt.removeChild(bt.rows[index]);
6750 if(isUpdate !== true){
6751 //this.stripeRows(index);
6752 //this.syncRowHeights(index, index);
6754 this.fireEvent("rowremoved", this, index, record);
6758 onAdd : function(ds, records, rowIndex)
6760 //Roo.log('on Add called');
6761 // - note this does not handle multiple adding very well..
6762 var bt = this.mainBody.dom;
6763 for (var i =0 ; i < records.length;i++) {
6764 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6765 //Roo.log(records[i]);
6766 //Roo.log(this.store.getAt(rowIndex+i));
6767 this.insertRow(this.store, rowIndex + i, false);
6774 refreshRow : function(record){
6775 var ds = this.store, index;
6776 if(typeof record == 'number'){
6778 record = ds.getAt(index);
6780 index = ds.indexOf(record);
6782 this.insertRow(ds, index, true);
6784 this.onRemove(ds, record, index+1, true);
6786 //this.syncRowHeights(index, index);
6788 this.fireEvent("rowupdated", this, index, record);
6791 insertRow : function(dm, rowIndex, isUpdate){
6794 this.fireEvent("beforerowsinserted", this, rowIndex);
6796 //var s = this.getScrollState();
6797 var row = this.renderRow(this.cm, this.store, rowIndex);
6798 // insert before rowIndex..
6799 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6803 if(row.cellObjects.length){
6804 Roo.each(row.cellObjects, function(r){
6805 _this.renderCellObject(r);
6810 this.fireEvent("rowsinserted", this, rowIndex);
6811 //this.syncRowHeights(firstRow, lastRow);
6812 //this.stripeRows(firstRow);
6819 getRowDom : function(rowIndex)
6821 var rows = this.el.select('tbody > tr', true).elements;
6823 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6826 // returns the object tree for a tr..
6829 renderRow : function(cm, ds, rowIndex)
6831 var d = ds.getAt(rowIndex);
6835 cls : 'x-row-' + rowIndex,
6839 var cellObjects = [];
6841 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6842 var config = cm.config[i];
6844 var renderer = cm.getRenderer(i);
6848 if(typeof(renderer) !== 'undefined'){
6849 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6851 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6852 // and are rendered into the cells after the row is rendered - using the id for the element.
6854 if(typeof(value) === 'object'){
6864 rowIndex : rowIndex,
6869 this.fireEvent('rowclass', this, rowcfg);
6873 cls : rowcfg.rowClass + ' x-col-' + i,
6875 html: (typeof(value) === 'object') ? '' : value
6882 if(typeof(config.colspan) != 'undefined'){
6883 td.colspan = config.colspan;
6886 if(typeof(config.hidden) != 'undefined' && config.hidden){
6887 td.style += ' display:none;';
6890 if(typeof(config.align) != 'undefined' && config.align.length){
6891 td.style += ' text-align:' + config.align + ';';
6893 if(typeof(config.valign) != 'undefined' && config.valign.length){
6894 td.style += ' vertical-align:' + config.valign + ';';
6897 if(typeof(config.width) != 'undefined'){
6898 td.style += ' width:' + config.width + 'px;';
6901 if(typeof(config.cursor) != 'undefined'){
6902 td.style += ' cursor:' + config.cursor + ';';
6905 if(typeof(config.cls) != 'undefined'){
6906 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6909 ['xs','sm','md','lg'].map(function(size){
6911 if(typeof(config[size]) == 'undefined'){
6915 if (!config[size]) { // 0 = hidden
6916 td.cls += ' hidden-' + size;
6920 td.cls += ' col-' + size + '-' + config[size];
6928 row.cellObjects = cellObjects;
6936 onBeforeLoad : function()
6945 this.el.select('tbody', true).first().dom.innerHTML = '';
6948 * Show or hide a row.
6949 * @param {Number} rowIndex to show or hide
6950 * @param {Boolean} state hide
6952 setRowVisibility : function(rowIndex, state)
6954 var bt = this.mainBody.dom;
6956 var rows = this.el.select('tbody > tr', true).elements;
6958 if(typeof(rows[rowIndex]) == 'undefined'){
6961 rows[rowIndex].dom.style.display = state ? '' : 'none';
6965 getSelectionModel : function(){
6967 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6969 return this.selModel;
6972 * Render the Roo.bootstrap object from renderder
6974 renderCellObject : function(r)
6978 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6980 var t = r.cfg.render(r.container);
6983 Roo.each(r.cfg.cn, function(c){
6985 container: t.getChildContainer(),
6988 _this.renderCellObject(child);
6993 getRowIndex : function(row)
6997 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7008 * Returns the grid's underlying element = used by panel.Grid
7009 * @return {Element} The element
7011 getGridEl : function(){
7015 * Forces a resize - used by panel.Grid
7016 * @return {Element} The element
7018 autoSize : function()
7020 //var ctr = Roo.get(this.container.dom.parentElement);
7021 var ctr = Roo.get(this.el.dom);
7023 var thd = this.getGridEl().select('thead',true).first();
7024 var tbd = this.getGridEl().select('tbody', true).first();
7025 var tfd = this.getGridEl().select('tfoot', true).first();
7027 var cw = ctr.getWidth();
7031 tbd.setSize(ctr.getWidth(),
7032 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7034 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7037 cw = Math.max(cw, this.totalWidth);
7038 this.getGridEl().select('tr',true).setWidth(cw);
7039 // resize 'expandable coloumn?
7041 return; // we doe not have a view in this design..
7044 onBodyScroll: function()
7046 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7048 this.mainHead.setStyle({
7049 'position' : 'relative',
7050 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7056 var scrollHeight = this.mainBody.dom.scrollHeight;
7058 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7060 var height = this.mainBody.getHeight();
7062 if(scrollHeight - height == scrollTop) {
7064 var total = this.ds.getTotalCount();
7066 if(this.footer.cursor + this.footer.pageSize < total){
7068 this.footer.ds.load({
7070 start : this.footer.cursor + this.footer.pageSize,
7071 limit : this.footer.pageSize
7081 onHeaderChange : function()
7083 var header = this.renderHeader();
7084 var table = this.el.select('table', true).first();
7086 this.mainHead.remove();
7087 this.mainHead = table.createChild(header, this.mainBody, false);
7090 onHiddenChange : function(colModel, colIndex, hidden)
7092 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7093 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7095 this.CSS.updateRule(thSelector, "display", "");
7096 this.CSS.updateRule(tdSelector, "display", "");
7099 this.CSS.updateRule(thSelector, "display", "none");
7100 this.CSS.updateRule(tdSelector, "display", "none");
7103 this.onHeaderChange();
7107 setColumnWidth: function(col_index, width)
7109 // width = "md-2 xs-2..."
7110 if(!this.colModel.config[col_index]) {
7114 var w = width.split(" ");
7116 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7118 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7121 for(var j = 0; j < w.length; j++) {
7127 var size_cls = w[j].split("-");
7129 if(!Number.isInteger(size_cls[1] * 1)) {
7133 if(!this.colModel.config[col_index][size_cls[0]]) {
7137 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7141 h_row[0].classList.replace(
7142 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7143 "col-"+size_cls[0]+"-"+size_cls[1]
7146 for(var i = 0; i < rows.length; i++) {
7148 var size_cls = w[j].split("-");
7150 if(!Number.isInteger(size_cls[1] * 1)) {
7154 if(!this.colModel.config[col_index][size_cls[0]]) {
7158 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7162 rows[i].classList.replace(
7163 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7164 "col-"+size_cls[0]+"-"+size_cls[1]
7168 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7183 * @class Roo.bootstrap.TableCell
7184 * @extends Roo.bootstrap.Component
7185 * Bootstrap TableCell class
7186 * @cfg {String} html cell contain text
7187 * @cfg {String} cls cell class
7188 * @cfg {String} tag cell tag (td|th) default td
7189 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7190 * @cfg {String} align Aligns the content in a cell
7191 * @cfg {String} axis Categorizes cells
7192 * @cfg {String} bgcolor Specifies the background color of a cell
7193 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7194 * @cfg {Number} colspan Specifies the number of columns a cell should span
7195 * @cfg {String} headers Specifies one or more header cells a cell is related to
7196 * @cfg {Number} height Sets the height of a cell
7197 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7198 * @cfg {Number} rowspan Sets the number of rows a cell should span
7199 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7200 * @cfg {String} valign Vertical aligns the content in a cell
7201 * @cfg {Number} width Specifies the width of a cell
7204 * Create a new TableCell
7205 * @param {Object} config The config object
7208 Roo.bootstrap.TableCell = function(config){
7209 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7212 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7232 getAutoCreate : function(){
7233 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7253 cfg.align=this.align
7259 cfg.bgcolor=this.bgcolor
7262 cfg.charoff=this.charoff
7265 cfg.colspan=this.colspan
7268 cfg.headers=this.headers
7271 cfg.height=this.height
7274 cfg.nowrap=this.nowrap
7277 cfg.rowspan=this.rowspan
7280 cfg.scope=this.scope
7283 cfg.valign=this.valign
7286 cfg.width=this.width
7305 * @class Roo.bootstrap.TableRow
7306 * @extends Roo.bootstrap.Component
7307 * Bootstrap TableRow class
7308 * @cfg {String} cls row class
7309 * @cfg {String} align Aligns the content in a table row
7310 * @cfg {String} bgcolor Specifies a background color for a table row
7311 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7312 * @cfg {String} valign Vertical aligns the content in a table row
7315 * Create a new TableRow
7316 * @param {Object} config The config object
7319 Roo.bootstrap.TableRow = function(config){
7320 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7323 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7331 getAutoCreate : function(){
7332 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7342 cfg.align = this.align;
7345 cfg.bgcolor = this.bgcolor;
7348 cfg.charoff = this.charoff;
7351 cfg.valign = this.valign;
7369 * @class Roo.bootstrap.TableBody
7370 * @extends Roo.bootstrap.Component
7371 * Bootstrap TableBody class
7372 * @cfg {String} cls element class
7373 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7374 * @cfg {String} align Aligns the content inside the element
7375 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7376 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7379 * Create a new TableBody
7380 * @param {Object} config The config object
7383 Roo.bootstrap.TableBody = function(config){
7384 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7387 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7395 getAutoCreate : function(){
7396 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7410 cfg.align = this.align;
7413 cfg.charoff = this.charoff;
7416 cfg.valign = this.valign;
7423 // initEvents : function()
7430 // this.store = Roo.factory(this.store, Roo.data);
7431 // this.store.on('load', this.onLoad, this);
7433 // this.store.load();
7437 // onLoad: function ()
7439 // this.fireEvent('load', this);
7449 * Ext JS Library 1.1.1
7450 * Copyright(c) 2006-2007, Ext JS, LLC.
7452 * Originally Released Under LGPL - original licence link has changed is not relivant.
7455 * <script type="text/javascript">
7458 // as we use this in bootstrap.
7459 Roo.namespace('Roo.form');
7461 * @class Roo.form.Action
7462 * Internal Class used to handle form actions
7464 * @param {Roo.form.BasicForm} el The form element or its id
7465 * @param {Object} config Configuration options
7470 // define the action interface
7471 Roo.form.Action = function(form, options){
7473 this.options = options || {};
7476 * Client Validation Failed
7479 Roo.form.Action.CLIENT_INVALID = 'client';
7481 * Server Validation Failed
7484 Roo.form.Action.SERVER_INVALID = 'server';
7486 * Connect to Server Failed
7489 Roo.form.Action.CONNECT_FAILURE = 'connect';
7491 * Reading Data from Server Failed
7494 Roo.form.Action.LOAD_FAILURE = 'load';
7496 Roo.form.Action.prototype = {
7498 failureType : undefined,
7499 response : undefined,
7503 run : function(options){
7508 success : function(response){
7513 handleResponse : function(response){
7517 // default connection failure
7518 failure : function(response){
7520 this.response = response;
7521 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7522 this.form.afterAction(this, false);
7525 processResponse : function(response){
7526 this.response = response;
7527 if(!response.responseText){
7530 this.result = this.handleResponse(response);
7534 // utility functions used internally
7535 getUrl : function(appendParams){
7536 var url = this.options.url || this.form.url || this.form.el.dom.action;
7538 var p = this.getParams();
7540 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7546 getMethod : function(){
7547 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7550 getParams : function(){
7551 var bp = this.form.baseParams;
7552 var p = this.options.params;
7554 if(typeof p == "object"){
7555 p = Roo.urlEncode(Roo.applyIf(p, bp));
7556 }else if(typeof p == 'string' && bp){
7557 p += '&' + Roo.urlEncode(bp);
7560 p = Roo.urlEncode(bp);
7565 createCallback : function(){
7567 success: this.success,
7568 failure: this.failure,
7570 timeout: (this.form.timeout*1000),
7571 upload: this.form.fileUpload ? this.success : undefined
7576 Roo.form.Action.Submit = function(form, options){
7577 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7580 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7583 haveProgress : false,
7584 uploadComplete : false,
7586 // uploadProgress indicator.
7587 uploadProgress : function()
7589 if (!this.form.progressUrl) {
7593 if (!this.haveProgress) {
7594 Roo.MessageBox.progress("Uploading", "Uploading");
7596 if (this.uploadComplete) {
7597 Roo.MessageBox.hide();
7601 this.haveProgress = true;
7603 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7605 var c = new Roo.data.Connection();
7607 url : this.form.progressUrl,
7612 success : function(req){
7613 //console.log(data);
7617 rdata = Roo.decode(req.responseText)
7619 Roo.log("Invalid data from server..");
7623 if (!rdata || !rdata.success) {
7625 Roo.MessageBox.alert(Roo.encode(rdata));
7628 var data = rdata.data;
7630 if (this.uploadComplete) {
7631 Roo.MessageBox.hide();
7636 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7637 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7640 this.uploadProgress.defer(2000,this);
7643 failure: function(data) {
7644 Roo.log('progress url failed ');
7655 // run get Values on the form, so it syncs any secondary forms.
7656 this.form.getValues();
7658 var o = this.options;
7659 var method = this.getMethod();
7660 var isPost = method == 'POST';
7661 if(o.clientValidation === false || this.form.isValid()){
7663 if (this.form.progressUrl) {
7664 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7665 (new Date() * 1) + '' + Math.random());
7670 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7671 form:this.form.el.dom,
7672 url:this.getUrl(!isPost),
7674 params:isPost ? this.getParams() : null,
7675 isUpload: this.form.fileUpload
7678 this.uploadProgress();
7680 }else if (o.clientValidation !== false){ // client validation failed
7681 this.failureType = Roo.form.Action.CLIENT_INVALID;
7682 this.form.afterAction(this, false);
7686 success : function(response)
7688 this.uploadComplete= true;
7689 if (this.haveProgress) {
7690 Roo.MessageBox.hide();
7694 var result = this.processResponse(response);
7695 if(result === true || result.success){
7696 this.form.afterAction(this, true);
7700 this.form.markInvalid(result.errors);
7701 this.failureType = Roo.form.Action.SERVER_INVALID;
7703 this.form.afterAction(this, false);
7705 failure : function(response)
7707 this.uploadComplete= true;
7708 if (this.haveProgress) {
7709 Roo.MessageBox.hide();
7712 this.response = response;
7713 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7714 this.form.afterAction(this, false);
7717 handleResponse : function(response){
7718 if(this.form.errorReader){
7719 var rs = this.form.errorReader.read(response);
7722 for(var i = 0, len = rs.records.length; i < len; i++) {
7723 var r = rs.records[i];
7727 if(errors.length < 1){
7731 success : rs.success,
7737 ret = Roo.decode(response.responseText);
7741 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7751 Roo.form.Action.Load = function(form, options){
7752 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7753 this.reader = this.form.reader;
7756 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7761 Roo.Ajax.request(Roo.apply(
7762 this.createCallback(), {
7763 method:this.getMethod(),
7764 url:this.getUrl(false),
7765 params:this.getParams()
7769 success : function(response){
7771 var result = this.processResponse(response);
7772 if(result === true || !result.success || !result.data){
7773 this.failureType = Roo.form.Action.LOAD_FAILURE;
7774 this.form.afterAction(this, false);
7777 this.form.clearInvalid();
7778 this.form.setValues(result.data);
7779 this.form.afterAction(this, true);
7782 handleResponse : function(response){
7783 if(this.form.reader){
7784 var rs = this.form.reader.read(response);
7785 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7787 success : rs.success,
7791 return Roo.decode(response.responseText);
7795 Roo.form.Action.ACTION_TYPES = {
7796 'load' : Roo.form.Action.Load,
7797 'submit' : Roo.form.Action.Submit
7806 * @class Roo.bootstrap.Form
7807 * @extends Roo.bootstrap.Component
7808 * Bootstrap Form class
7809 * @cfg {String} method GET | POST (default POST)
7810 * @cfg {String} labelAlign top | left (default top)
7811 * @cfg {String} align left | right - for navbars
7812 * @cfg {Boolean} loadMask load mask when submit (default true)
7817 * @param {Object} config The config object
7821 Roo.bootstrap.Form = function(config){
7823 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7825 Roo.bootstrap.Form.popover.apply();
7829 * @event clientvalidation
7830 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7831 * @param {Form} this
7832 * @param {Boolean} valid true if the form has passed client-side validation
7834 clientvalidation: true,
7836 * @event beforeaction
7837 * Fires before any action is performed. Return false to cancel the action.
7838 * @param {Form} this
7839 * @param {Action} action The action to be performed
7843 * @event actionfailed
7844 * Fires when an action fails.
7845 * @param {Form} this
7846 * @param {Action} action The action that failed
7848 actionfailed : true,
7850 * @event actioncomplete
7851 * Fires when an action is completed.
7852 * @param {Form} this
7853 * @param {Action} action The action that completed
7855 actioncomplete : true
7859 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7862 * @cfg {String} method
7863 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7868 * The URL to use for form actions if one isn't supplied in the action options.
7871 * @cfg {Boolean} fileUpload
7872 * Set to true if this form is a file upload.
7876 * @cfg {Object} baseParams
7877 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7881 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7885 * @cfg {Sting} align (left|right) for navbar forms
7890 activeAction : null,
7893 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7894 * element by passing it or its id or mask the form itself by passing in true.
7897 waitMsgTarget : false,
7902 * @cfg {Boolean} errorMask (true|false) default false
7907 * @cfg {Number} maskOffset Default 100
7912 * @cfg {Boolean} maskBody
7916 getAutoCreate : function(){
7920 method : this.method || 'POST',
7921 id : this.id || Roo.id(),
7924 if (this.parent().xtype.match(/^Nav/)) {
7925 cfg.cls = 'navbar-form navbar-' + this.align;
7929 if (this.labelAlign == 'left' ) {
7930 cfg.cls += ' form-horizontal';
7936 initEvents : function()
7938 this.el.on('submit', this.onSubmit, this);
7939 // this was added as random key presses on the form where triggering form submit.
7940 this.el.on('keypress', function(e) {
7941 if (e.getCharCode() != 13) {
7944 // we might need to allow it for textareas.. and some other items.
7945 // check e.getTarget().
7947 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7951 Roo.log("keypress blocked");
7959 onSubmit : function(e){
7964 * Returns true if client-side validation on the form is successful.
7967 isValid : function(){
7968 var items = this.getItems();
7972 items.each(function(f){
7978 Roo.log('invalid field: ' + f.name);
7982 if(!target && f.el.isVisible(true)){
7988 if(this.errorMask && !valid){
7989 Roo.bootstrap.Form.popover.mask(this, target);
7996 * Returns true if any fields in this form have changed since their original load.
7999 isDirty : function(){
8001 var items = this.getItems();
8002 items.each(function(f){
8012 * Performs a predefined action (submit or load) or custom actions you define on this form.
8013 * @param {String} actionName The name of the action type
8014 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
8015 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8016 * accept other config options):
8018 Property Type Description
8019 ---------------- --------------- ----------------------------------------------------------------------------------
8020 url String The url for the action (defaults to the form's url)
8021 method String The form method to use (defaults to the form's method, or POST if not defined)
8022 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8023 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8024 validate the form on the client (defaults to false)
8026 * @return {BasicForm} this
8028 doAction : function(action, options){
8029 if(typeof action == 'string'){
8030 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8032 if(this.fireEvent('beforeaction', this, action) !== false){
8033 this.beforeAction(action);
8034 action.run.defer(100, action);
8040 beforeAction : function(action){
8041 var o = action.options;
8046 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8048 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8051 // not really supported yet.. ??
8053 //if(this.waitMsgTarget === true){
8054 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8055 //}else if(this.waitMsgTarget){
8056 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8057 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8059 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8065 afterAction : function(action, success){
8066 this.activeAction = null;
8067 var o = action.options;
8072 Roo.get(document.body).unmask();
8078 //if(this.waitMsgTarget === true){
8079 // this.el.unmask();
8080 //}else if(this.waitMsgTarget){
8081 // this.waitMsgTarget.unmask();
8083 // Roo.MessageBox.updateProgress(1);
8084 // Roo.MessageBox.hide();
8091 Roo.callback(o.success, o.scope, [this, action]);
8092 this.fireEvent('actioncomplete', this, action);
8096 // failure condition..
8097 // we have a scenario where updates need confirming.
8098 // eg. if a locking scenario exists..
8099 // we look for { errors : { needs_confirm : true }} in the response.
8101 (typeof(action.result) != 'undefined') &&
8102 (typeof(action.result.errors) != 'undefined') &&
8103 (typeof(action.result.errors.needs_confirm) != 'undefined')
8106 Roo.log("not supported yet");
8109 Roo.MessageBox.confirm(
8110 "Change requires confirmation",
8111 action.result.errorMsg,
8116 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8126 Roo.callback(o.failure, o.scope, [this, action]);
8127 // show an error message if no failed handler is set..
8128 if (!this.hasListener('actionfailed')) {
8129 Roo.log("need to add dialog support");
8131 Roo.MessageBox.alert("Error",
8132 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8133 action.result.errorMsg :
8134 "Saving Failed, please check your entries or try again"
8139 this.fireEvent('actionfailed', this, action);
8144 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8145 * @param {String} id The value to search for
8148 findField : function(id){
8149 var items = this.getItems();
8150 var field = items.get(id);
8152 items.each(function(f){
8153 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8160 return field || null;
8163 * Mark fields in this form invalid in bulk.
8164 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8165 * @return {BasicForm} this
8167 markInvalid : function(errors){
8168 if(errors instanceof Array){
8169 for(var i = 0, len = errors.length; i < len; i++){
8170 var fieldError = errors[i];
8171 var f = this.findField(fieldError.id);
8173 f.markInvalid(fieldError.msg);
8179 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8180 field.markInvalid(errors[id]);
8184 //Roo.each(this.childForms || [], function (f) {
8185 // f.markInvalid(errors);
8192 * Set values for fields in this form in bulk.
8193 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8194 * @return {BasicForm} this
8196 setValues : function(values){
8197 if(values instanceof Array){ // array of objects
8198 for(var i = 0, len = values.length; i < len; i++){
8200 var f = this.findField(v.id);
8202 f.setValue(v.value);
8203 if(this.trackResetOnLoad){
8204 f.originalValue = f.getValue();
8208 }else{ // object hash
8211 if(typeof values[id] != 'function' && (field = this.findField(id))){
8213 if (field.setFromData &&
8215 field.displayField &&
8216 // combos' with local stores can
8217 // be queried via setValue()
8218 // to set their value..
8219 (field.store && !field.store.isLocal)
8223 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8224 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8225 field.setFromData(sd);
8227 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8229 field.setFromData(values);
8232 field.setValue(values[id]);
8236 if(this.trackResetOnLoad){
8237 field.originalValue = field.getValue();
8243 //Roo.each(this.childForms || [], function (f) {
8244 // f.setValues(values);
8251 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8252 * they are returned as an array.
8253 * @param {Boolean} asString
8256 getValues : function(asString){
8257 //if (this.childForms) {
8258 // copy values from the child forms
8259 // Roo.each(this.childForms, function (f) {
8260 // this.setValues(f.getValues());
8266 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8267 if(asString === true){
8270 return Roo.urlDecode(fs);
8274 * Returns the fields in this form as an object with key/value pairs.
8275 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8278 getFieldValues : function(with_hidden)
8280 var items = this.getItems();
8282 items.each(function(f){
8288 var v = f.getValue();
8290 if (f.inputType =='radio') {
8291 if (typeof(ret[f.getName()]) == 'undefined') {
8292 ret[f.getName()] = ''; // empty..
8295 if (!f.el.dom.checked) {
8303 if(f.xtype == 'MoneyField'){
8304 ret[f.currencyName] = f.getCurrency();
8307 // not sure if this supported any more..
8308 if ((typeof(v) == 'object') && f.getRawValue) {
8309 v = f.getRawValue() ; // dates..
8311 // combo boxes where name != hiddenName...
8312 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8313 ret[f.name] = f.getRawValue();
8315 ret[f.getName()] = v;
8322 * Clears all invalid messages in this form.
8323 * @return {BasicForm} this
8325 clearInvalid : function(){
8326 var items = this.getItems();
8328 items.each(function(f){
8337 * @return {BasicForm} this
8340 var items = this.getItems();
8341 items.each(function(f){
8345 Roo.each(this.childForms || [], function (f) {
8353 getItems : function()
8355 var r=new Roo.util.MixedCollection(false, function(o){
8356 return o.id || (o.id = Roo.id());
8358 var iter = function(el) {
8365 Roo.each(el.items,function(e) {
8374 hideFields : function(items)
8376 Roo.each(items, function(i){
8378 var f = this.findField(i);
8389 showFields : function(items)
8391 Roo.each(items, function(i){
8393 var f = this.findField(i);
8406 Roo.apply(Roo.bootstrap.Form, {
8433 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8434 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8435 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8436 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8439 this.maskEl.top.enableDisplayMode("block");
8440 this.maskEl.left.enableDisplayMode("block");
8441 this.maskEl.bottom.enableDisplayMode("block");
8442 this.maskEl.right.enableDisplayMode("block");
8444 this.toolTip = new Roo.bootstrap.Tooltip({
8445 cls : 'roo-form-error-popover',
8447 'left' : ['r-l', [-2,0], 'right'],
8448 'right' : ['l-r', [2,0], 'left'],
8449 'bottom' : ['tl-bl', [0,2], 'top'],
8450 'top' : [ 'bl-tl', [0,-2], 'bottom']
8454 this.toolTip.render(Roo.get(document.body));
8456 this.toolTip.el.enableDisplayMode("block");
8458 Roo.get(document.body).on('click', function(){
8462 Roo.get(document.body).on('touchstart', function(){
8466 this.isApplied = true
8469 mask : function(form, target)
8473 this.target = target;
8475 if(!this.form.errorMask || !target.el){
8479 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8481 Roo.log(scrollable);
8483 var ot = this.target.el.calcOffsetsTo(scrollable);
8485 var scrollTo = ot[1] - this.form.maskOffset;
8487 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8489 scrollable.scrollTo('top', scrollTo);
8491 var box = this.target.el.getBox();
8493 var zIndex = Roo.bootstrap.Modal.zIndex++;
8496 this.maskEl.top.setStyle('position', 'absolute');
8497 this.maskEl.top.setStyle('z-index', zIndex);
8498 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8499 this.maskEl.top.setLeft(0);
8500 this.maskEl.top.setTop(0);
8501 this.maskEl.top.show();
8503 this.maskEl.left.setStyle('position', 'absolute');
8504 this.maskEl.left.setStyle('z-index', zIndex);
8505 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8506 this.maskEl.left.setLeft(0);
8507 this.maskEl.left.setTop(box.y - this.padding);
8508 this.maskEl.left.show();
8510 this.maskEl.bottom.setStyle('position', 'absolute');
8511 this.maskEl.bottom.setStyle('z-index', zIndex);
8512 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8513 this.maskEl.bottom.setLeft(0);
8514 this.maskEl.bottom.setTop(box.bottom + this.padding);
8515 this.maskEl.bottom.show();
8517 this.maskEl.right.setStyle('position', 'absolute');
8518 this.maskEl.right.setStyle('z-index', zIndex);
8519 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8520 this.maskEl.right.setLeft(box.right + this.padding);
8521 this.maskEl.right.setTop(box.y - this.padding);
8522 this.maskEl.right.show();
8524 this.toolTip.bindEl = this.target.el;
8526 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8528 var tip = this.target.blankText;
8530 if(this.target.getValue() !== '' ) {
8532 if (this.target.invalidText.length) {
8533 tip = this.target.invalidText;
8534 } else if (this.target.regexText.length){
8535 tip = this.target.regexText;
8539 this.toolTip.show(tip);
8541 this.intervalID = window.setInterval(function() {
8542 Roo.bootstrap.Form.popover.unmask();
8545 window.onwheel = function(){ return false;};
8547 (function(){ this.isMasked = true; }).defer(500, this);
8553 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8557 this.maskEl.top.setStyle('position', 'absolute');
8558 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8559 this.maskEl.top.hide();
8561 this.maskEl.left.setStyle('position', 'absolute');
8562 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8563 this.maskEl.left.hide();
8565 this.maskEl.bottom.setStyle('position', 'absolute');
8566 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8567 this.maskEl.bottom.hide();
8569 this.maskEl.right.setStyle('position', 'absolute');
8570 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8571 this.maskEl.right.hide();
8573 this.toolTip.hide();
8575 this.toolTip.el.hide();
8577 window.onwheel = function(){ return true;};
8579 if(this.intervalID){
8580 window.clearInterval(this.intervalID);
8581 this.intervalID = false;
8584 this.isMasked = false;
8594 * Ext JS Library 1.1.1
8595 * Copyright(c) 2006-2007, Ext JS, LLC.
8597 * Originally Released Under LGPL - original licence link has changed is not relivant.
8600 * <script type="text/javascript">
8603 * @class Roo.form.VTypes
8604 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8607 Roo.form.VTypes = function(){
8608 // closure these in so they are only created once.
8609 var alpha = /^[a-zA-Z_]+$/;
8610 var alphanum = /^[a-zA-Z0-9_]+$/;
8611 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8612 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8614 // All these messages and functions are configurable
8617 * The function used to validate email addresses
8618 * @param {String} value The email address
8620 'email' : function(v){
8621 return email.test(v);
8624 * The error text to display when the email validation function returns false
8627 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8629 * The keystroke filter mask to be applied on email input
8632 'emailMask' : /[a-z0-9_\.\-@]/i,
8635 * The function used to validate URLs
8636 * @param {String} value The URL
8638 'url' : function(v){
8642 * The error text to display when the url validation function returns false
8645 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8648 * The function used to validate alpha values
8649 * @param {String} value The value
8651 'alpha' : function(v){
8652 return alpha.test(v);
8655 * The error text to display when the alpha validation function returns false
8658 'alphaText' : 'This field should only contain letters and _',
8660 * The keystroke filter mask to be applied on alpha input
8663 'alphaMask' : /[a-z_]/i,
8666 * The function used to validate alphanumeric values
8667 * @param {String} value The value
8669 'alphanum' : function(v){
8670 return alphanum.test(v);
8673 * The error text to display when the alphanumeric validation function returns false
8676 'alphanumText' : 'This field should only contain letters, numbers and _',
8678 * The keystroke filter mask to be applied on alphanumeric input
8681 'alphanumMask' : /[a-z0-9_]/i
8691 * @class Roo.bootstrap.Input
8692 * @extends Roo.bootstrap.Component
8693 * Bootstrap Input class
8694 * @cfg {Boolean} disabled is it disabled
8695 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8696 * @cfg {String} name name of the input
8697 * @cfg {string} fieldLabel - the label associated
8698 * @cfg {string} placeholder - placeholder to put in text.
8699 * @cfg {string} before - input group add on before
8700 * @cfg {string} after - input group add on after
8701 * @cfg {string} size - (lg|sm) or leave empty..
8702 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8703 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8704 * @cfg {Number} md colspan out of 12 for computer-sized screens
8705 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8706 * @cfg {string} value default value of the input
8707 * @cfg {Number} labelWidth set the width of label
8708 * @cfg {Number} labellg set the width of label (1-12)
8709 * @cfg {Number} labelmd set the width of label (1-12)
8710 * @cfg {Number} labelsm set the width of label (1-12)
8711 * @cfg {Number} labelxs set the width of label (1-12)
8712 * @cfg {String} labelAlign (top|left)
8713 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8714 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8715 * @cfg {String} indicatorpos (left|right) default left
8716 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8717 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8719 * @cfg {String} align (left|center|right) Default left
8720 * @cfg {Boolean} forceFeedback (true|false) Default false
8723 * Create a new Input
8724 * @param {Object} config The config object
8727 Roo.bootstrap.Input = function(config){
8729 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8734 * Fires when this field receives input focus.
8735 * @param {Roo.form.Field} this
8740 * Fires when this field loses input focus.
8741 * @param {Roo.form.Field} this
8746 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8747 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8748 * @param {Roo.form.Field} this
8749 * @param {Roo.EventObject} e The event object
8754 * Fires just before the field blurs if the field value has changed.
8755 * @param {Roo.form.Field} this
8756 * @param {Mixed} newValue The new value
8757 * @param {Mixed} oldValue The original value
8762 * Fires after the field has been marked as invalid.
8763 * @param {Roo.form.Field} this
8764 * @param {String} msg The validation message
8769 * Fires after the field has been validated with no errors.
8770 * @param {Roo.form.Field} this
8775 * Fires after the key up
8776 * @param {Roo.form.Field} this
8777 * @param {Roo.EventObject} e The event Object
8783 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8785 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8786 automatic validation (defaults to "keyup").
8788 validationEvent : "keyup",
8790 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8792 validateOnBlur : true,
8794 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8796 validationDelay : 250,
8798 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8800 focusClass : "x-form-focus", // not needed???
8804 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8806 invalidClass : "has-warning",
8809 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8811 validClass : "has-success",
8814 * @cfg {Boolean} hasFeedback (true|false) default true
8819 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8821 invalidFeedbackClass : "glyphicon-warning-sign",
8824 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8826 validFeedbackClass : "glyphicon-ok",
8829 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8831 selectOnFocus : false,
8834 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8838 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8843 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8845 disableKeyFilter : false,
8848 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8852 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8856 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8858 blankText : "Please complete this mandatory field",
8861 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8865 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8867 maxLength : Number.MAX_VALUE,
8869 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8871 minLengthText : "The minimum length for this field is {0}",
8873 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8875 maxLengthText : "The maximum length for this field is {0}",
8879 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8880 * If available, this function will be called only after the basic validators all return true, and will be passed the
8881 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8885 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8886 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8887 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8891 * @cfg {String} regexText -- Depricated - use Invalid Text
8896 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8902 autocomplete: false,
8921 formatedValue : false,
8922 forceFeedback : false,
8924 indicatorpos : 'left',
8934 parentLabelAlign : function()
8937 while (parent.parent()) {
8938 parent = parent.parent();
8939 if (typeof(parent.labelAlign) !='undefined') {
8940 return parent.labelAlign;
8947 getAutoCreate : function()
8949 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8955 if(this.inputType != 'hidden'){
8956 cfg.cls = 'form-group' //input-group
8962 type : this.inputType,
8964 cls : 'form-control',
8965 placeholder : this.placeholder || '',
8966 autocomplete : this.autocomplete || 'new-password'
8969 if(this.capture.length){
8970 input.capture = this.capture;
8973 if(this.accept.length){
8974 input.accept = this.accept + "/*";
8978 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8981 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8982 input.maxLength = this.maxLength;
8985 if (this.disabled) {
8986 input.disabled=true;
8989 if (this.readOnly) {
8990 input.readonly=true;
8994 input.name = this.name;
8998 input.cls += ' input-' + this.size;
9002 ['xs','sm','md','lg'].map(function(size){
9003 if (settings[size]) {
9004 cfg.cls += ' col-' + size + '-' + settings[size];
9008 var inputblock = input;
9012 cls: 'glyphicon form-control-feedback'
9015 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9018 cls : 'has-feedback',
9026 if (this.before || this.after) {
9029 cls : 'input-group',
9033 if (this.before && typeof(this.before) == 'string') {
9035 inputblock.cn.push({
9037 cls : 'roo-input-before input-group-addon',
9041 if (this.before && typeof(this.before) == 'object') {
9042 this.before = Roo.factory(this.before);
9044 inputblock.cn.push({
9046 cls : 'roo-input-before input-group-' +
9047 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9051 inputblock.cn.push(input);
9053 if (this.after && typeof(this.after) == 'string') {
9054 inputblock.cn.push({
9056 cls : 'roo-input-after input-group-addon',
9060 if (this.after && typeof(this.after) == 'object') {
9061 this.after = Roo.factory(this.after);
9063 inputblock.cn.push({
9065 cls : 'roo-input-after input-group-' +
9066 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9070 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9071 inputblock.cls += ' has-feedback';
9072 inputblock.cn.push(feedback);
9076 if (align ==='left' && this.fieldLabel.length) {
9078 cfg.cls += ' roo-form-group-label-left';
9083 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9084 tooltip : 'This field is required'
9089 cls : 'control-label',
9090 html : this.fieldLabel
9101 var labelCfg = cfg.cn[1];
9102 var contentCfg = cfg.cn[2];
9104 if(this.indicatorpos == 'right'){
9109 cls : 'control-label',
9113 html : this.fieldLabel
9117 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9118 tooltip : 'This field is required'
9131 labelCfg = cfg.cn[0];
9132 contentCfg = cfg.cn[1];
9136 if(this.labelWidth > 12){
9137 labelCfg.style = "width: " + this.labelWidth + 'px';
9140 if(this.labelWidth < 13 && this.labelmd == 0){
9141 this.labelmd = this.labelWidth;
9144 if(this.labellg > 0){
9145 labelCfg.cls += ' col-lg-' + this.labellg;
9146 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9149 if(this.labelmd > 0){
9150 labelCfg.cls += ' col-md-' + this.labelmd;
9151 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9154 if(this.labelsm > 0){
9155 labelCfg.cls += ' col-sm-' + this.labelsm;
9156 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9159 if(this.labelxs > 0){
9160 labelCfg.cls += ' col-xs-' + this.labelxs;
9161 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9165 } else if ( this.fieldLabel.length) {
9170 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9171 tooltip : 'This field is required'
9175 //cls : 'input-group-addon',
9176 html : this.fieldLabel
9184 if(this.indicatorpos == 'right'){
9189 //cls : 'input-group-addon',
9190 html : this.fieldLabel
9195 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9196 tooltip : 'This field is required'
9216 if (this.parentType === 'Navbar' && this.parent().bar) {
9217 cfg.cls += ' navbar-form';
9220 if (this.parentType === 'NavGroup') {
9221 cfg.cls += ' navbar-form';
9229 * return the real input element.
9231 inputEl: function ()
9233 return this.el.select('input.form-control',true).first();
9236 tooltipEl : function()
9238 return this.inputEl();
9241 indicatorEl : function()
9243 var indicator = this.el.select('i.roo-required-indicator',true).first();
9253 setDisabled : function(v)
9255 var i = this.inputEl().dom;
9257 i.removeAttribute('disabled');
9261 i.setAttribute('disabled','true');
9263 initEvents : function()
9266 this.inputEl().on("keydown" , this.fireKey, this);
9267 this.inputEl().on("focus", this.onFocus, this);
9268 this.inputEl().on("blur", this.onBlur, this);
9270 this.inputEl().relayEvent('keyup', this);
9272 this.indicator = this.indicatorEl();
9275 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9278 // reference to original value for reset
9279 this.originalValue = this.getValue();
9280 //Roo.form.TextField.superclass.initEvents.call(this);
9281 if(this.validationEvent == 'keyup'){
9282 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9283 this.inputEl().on('keyup', this.filterValidation, this);
9285 else if(this.validationEvent !== false){
9286 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9289 if(this.selectOnFocus){
9290 this.on("focus", this.preFocus, this);
9293 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9294 this.inputEl().on("keypress", this.filterKeys, this);
9296 this.inputEl().relayEvent('keypress', this);
9299 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9300 this.el.on("click", this.autoSize, this);
9303 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9304 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9307 if (typeof(this.before) == 'object') {
9308 this.before.render(this.el.select('.roo-input-before',true).first());
9310 if (typeof(this.after) == 'object') {
9311 this.after.render(this.el.select('.roo-input-after',true).first());
9314 this.inputEl().on('change', this.onChange, this);
9317 filterValidation : function(e){
9318 if(!e.isNavKeyPress()){
9319 this.validationTask.delay(this.validationDelay);
9323 * Validates the field value
9324 * @return {Boolean} True if the value is valid, else false
9326 validate : function(){
9327 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9328 if(this.disabled || this.validateValue(this.getRawValue())){
9339 * Validates a value according to the field's validation rules and marks the field as invalid
9340 * if the validation fails
9341 * @param {Mixed} value The value to validate
9342 * @return {Boolean} True if the value is valid, else false
9344 validateValue : function(value)
9346 if(this.getVisibilityEl().hasClass('hidden')){
9350 if(value.length < 1) { // if it's blank
9351 if(this.allowBlank){
9357 if(value.length < this.minLength){
9360 if(value.length > this.maxLength){
9364 var vt = Roo.form.VTypes;
9365 if(!vt[this.vtype](value, this)){
9369 if(typeof this.validator == "function"){
9370 var msg = this.validator(value);
9374 if (typeof(msg) == 'string') {
9375 this.invalidText = msg;
9379 if(this.regex && !this.regex.test(value)){
9387 fireKey : function(e){
9388 //Roo.log('field ' + e.getKey());
9389 if(e.isNavKeyPress()){
9390 this.fireEvent("specialkey", this, e);
9393 focus : function (selectText){
9395 this.inputEl().focus();
9396 if(selectText === true){
9397 this.inputEl().dom.select();
9403 onFocus : function(){
9404 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9405 // this.el.addClass(this.focusClass);
9408 this.hasFocus = true;
9409 this.startValue = this.getValue();
9410 this.fireEvent("focus", this);
9414 beforeBlur : Roo.emptyFn,
9418 onBlur : function(){
9420 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9421 //this.el.removeClass(this.focusClass);
9423 this.hasFocus = false;
9424 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9427 var v = this.getValue();
9428 if(String(v) !== String(this.startValue)){
9429 this.fireEvent('change', this, v, this.startValue);
9431 this.fireEvent("blur", this);
9434 onChange : function(e)
9436 var v = this.getValue();
9437 if(String(v) !== String(this.startValue)){
9438 this.fireEvent('change', this, v, this.startValue);
9444 * Resets the current field value to the originally loaded value and clears any validation messages
9447 this.setValue(this.originalValue);
9451 * Returns the name of the field
9452 * @return {Mixed} name The name field
9454 getName: function(){
9458 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9459 * @return {Mixed} value The field value
9461 getValue : function(){
9463 var v = this.inputEl().getValue();
9468 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9469 * @return {Mixed} value The field value
9471 getRawValue : function(){
9472 var v = this.inputEl().getValue();
9478 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9479 * @param {Mixed} value The value to set
9481 setRawValue : function(v){
9482 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9485 selectText : function(start, end){
9486 var v = this.getRawValue();
9488 start = start === undefined ? 0 : start;
9489 end = end === undefined ? v.length : end;
9490 var d = this.inputEl().dom;
9491 if(d.setSelectionRange){
9492 d.setSelectionRange(start, end);
9493 }else if(d.createTextRange){
9494 var range = d.createTextRange();
9495 range.moveStart("character", start);
9496 range.moveEnd("character", v.length-end);
9503 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9504 * @param {Mixed} value The value to set
9506 setValue : function(v){
9509 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9515 processValue : function(value){
9516 if(this.stripCharsRe){
9517 var newValue = value.replace(this.stripCharsRe, '');
9518 if(newValue !== value){
9519 this.setRawValue(newValue);
9526 preFocus : function(){
9528 if(this.selectOnFocus){
9529 this.inputEl().dom.select();
9532 filterKeys : function(e){
9534 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9537 var c = e.getCharCode(), cc = String.fromCharCode(c);
9538 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9541 if(!this.maskRe.test(cc)){
9546 * Clear any invalid styles/messages for this field
9548 clearInvalid : function(){
9550 if(!this.el || this.preventMark){ // not rendered
9555 this.el.removeClass(this.invalidClass);
9557 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9559 var feedback = this.el.select('.form-control-feedback', true).first();
9562 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9568 this.indicator.removeClass('visible');
9569 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9572 this.fireEvent('valid', this);
9576 * Mark this field as valid
9578 markValid : function()
9580 if(!this.el || this.preventMark){ // not rendered...
9584 this.el.removeClass([this.invalidClass, this.validClass]);
9586 var feedback = this.el.select('.form-control-feedback', true).first();
9589 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9593 this.indicator.removeClass('visible');
9594 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9601 if(this.allowBlank && !this.getRawValue().length){
9605 this.el.addClass(this.validClass);
9607 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9609 var feedback = this.el.select('.form-control-feedback', true).first();
9612 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9613 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9618 this.fireEvent('valid', this);
9622 * Mark this field as invalid
9623 * @param {String} msg The validation message
9625 markInvalid : function(msg)
9627 if(!this.el || this.preventMark){ // not rendered
9631 this.el.removeClass([this.invalidClass, this.validClass]);
9633 var feedback = this.el.select('.form-control-feedback', true).first();
9636 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9643 if(this.allowBlank && !this.getRawValue().length){
9648 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9649 this.indicator.addClass('visible');
9652 this.el.addClass(this.invalidClass);
9654 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9656 var feedback = this.el.select('.form-control-feedback', true).first();
9659 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9661 if(this.getValue().length || this.forceFeedback){
9662 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9669 this.fireEvent('invalid', this, msg);
9672 SafariOnKeyDown : function(event)
9674 // this is a workaround for a password hang bug on chrome/ webkit.
9675 if (this.inputEl().dom.type != 'password') {
9679 var isSelectAll = false;
9681 if(this.inputEl().dom.selectionEnd > 0){
9682 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9684 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9685 event.preventDefault();
9690 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9692 event.preventDefault();
9693 // this is very hacky as keydown always get's upper case.
9695 var cc = String.fromCharCode(event.getCharCode());
9696 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9700 adjustWidth : function(tag, w){
9701 tag = tag.toLowerCase();
9702 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9703 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9707 if(tag == 'textarea'){
9710 }else if(Roo.isOpera){
9714 if(tag == 'textarea'){
9722 setFieldLabel : function(v)
9729 var ar = this.el.select('label > span',true);
9731 if (ar.elements.length) {
9732 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9733 this.fieldLabel = v;
9737 var br = this.el.select('label',true);
9739 if(br.elements.length) {
9740 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9741 this.fieldLabel = v;
9745 Roo.log('Cannot Found any of label > span || label in input');
9749 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9750 this.fieldLabel = v;
9765 * @class Roo.bootstrap.TextArea
9766 * @extends Roo.bootstrap.Input
9767 * Bootstrap TextArea class
9768 * @cfg {Number} cols Specifies the visible width of a text area
9769 * @cfg {Number} rows Specifies the visible number of lines in a text area
9770 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9771 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9772 * @cfg {string} html text
9775 * Create a new TextArea
9776 * @param {Object} config The config object
9779 Roo.bootstrap.TextArea = function(config){
9780 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9784 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9794 getAutoCreate : function(){
9796 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9802 if(this.inputType != 'hidden'){
9803 cfg.cls = 'form-group' //input-group
9811 value : this.value || '',
9812 html: this.html || '',
9813 cls : 'form-control',
9814 placeholder : this.placeholder || ''
9818 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9819 input.maxLength = this.maxLength;
9823 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9827 input.cols = this.cols;
9830 if (this.readOnly) {
9831 input.readonly = true;
9835 input.name = this.name;
9839 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9843 ['xs','sm','md','lg'].map(function(size){
9844 if (settings[size]) {
9845 cfg.cls += ' col-' + size + '-' + settings[size];
9849 var inputblock = input;
9851 if(this.hasFeedback && !this.allowBlank){
9855 cls: 'glyphicon form-control-feedback'
9859 cls : 'has-feedback',
9868 if (this.before || this.after) {
9871 cls : 'input-group',
9875 inputblock.cn.push({
9877 cls : 'input-group-addon',
9882 inputblock.cn.push(input);
9884 if(this.hasFeedback && !this.allowBlank){
9885 inputblock.cls += ' has-feedback';
9886 inputblock.cn.push(feedback);
9890 inputblock.cn.push({
9892 cls : 'input-group-addon',
9899 if (align ==='left' && this.fieldLabel.length) {
9904 cls : 'control-label',
9905 html : this.fieldLabel
9916 if(this.labelWidth > 12){
9917 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9920 if(this.labelWidth < 13 && this.labelmd == 0){
9921 this.labelmd = this.labelWidth;
9924 if(this.labellg > 0){
9925 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9926 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9929 if(this.labelmd > 0){
9930 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9931 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9934 if(this.labelsm > 0){
9935 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9936 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9939 if(this.labelxs > 0){
9940 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9941 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9944 } else if ( this.fieldLabel.length) {
9949 //cls : 'input-group-addon',
9950 html : this.fieldLabel
9968 if (this.disabled) {
9969 input.disabled=true;
9976 * return the real textarea element.
9978 inputEl: function ()
9980 return this.el.select('textarea.form-control',true).first();
9984 * Clear any invalid styles/messages for this field
9986 clearInvalid : function()
9989 if(!this.el || this.preventMark){ // not rendered
9993 var label = this.el.select('label', true).first();
9994 var icon = this.el.select('i.fa-star', true).first();
10000 this.el.removeClass(this.invalidClass);
10002 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10004 var feedback = this.el.select('.form-control-feedback', true).first();
10007 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10012 this.fireEvent('valid', this);
10016 * Mark this field as valid
10018 markValid : function()
10020 if(!this.el || this.preventMark){ // not rendered
10024 this.el.removeClass([this.invalidClass, this.validClass]);
10026 var feedback = this.el.select('.form-control-feedback', true).first();
10029 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10032 if(this.disabled || this.allowBlank){
10036 var label = this.el.select('label', true).first();
10037 var icon = this.el.select('i.fa-star', true).first();
10043 this.el.addClass(this.validClass);
10045 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10047 var feedback = this.el.select('.form-control-feedback', true).first();
10050 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10051 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10056 this.fireEvent('valid', this);
10060 * Mark this field as invalid
10061 * @param {String} msg The validation message
10063 markInvalid : function(msg)
10065 if(!this.el || this.preventMark){ // not rendered
10069 this.el.removeClass([this.invalidClass, this.validClass]);
10071 var feedback = this.el.select('.form-control-feedback', true).first();
10074 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10077 if(this.disabled || this.allowBlank){
10081 var label = this.el.select('label', true).first();
10082 var icon = this.el.select('i.fa-star', true).first();
10084 if(!this.getValue().length && label && !icon){
10085 this.el.createChild({
10087 cls : 'text-danger fa fa-lg fa-star',
10088 tooltip : 'This field is required',
10089 style : 'margin-right:5px;'
10093 this.el.addClass(this.invalidClass);
10095 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10097 var feedback = this.el.select('.form-control-feedback', true).first();
10100 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10102 if(this.getValue().length || this.forceFeedback){
10103 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10110 this.fireEvent('invalid', this, msg);
10118 * trigger field - base class for combo..
10123 * @class Roo.bootstrap.TriggerField
10124 * @extends Roo.bootstrap.Input
10125 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10126 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10127 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10128 * for which you can provide a custom implementation. For example:
10130 var trigger = new Roo.bootstrap.TriggerField();
10131 trigger.onTriggerClick = myTriggerFn;
10132 trigger.applyTo('my-field');
10135 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10136 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10137 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10138 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10139 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10142 * Create a new TriggerField.
10143 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10144 * to the base TextField)
10146 Roo.bootstrap.TriggerField = function(config){
10147 this.mimicing = false;
10148 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10151 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10153 * @cfg {String} triggerClass A CSS class to apply to the trigger
10156 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10161 * @cfg {Boolean} removable (true|false) special filter default false
10165 /** @cfg {Boolean} grow @hide */
10166 /** @cfg {Number} growMin @hide */
10167 /** @cfg {Number} growMax @hide */
10173 autoSize: Roo.emptyFn,
10177 deferHeight : true,
10180 actionMode : 'wrap',
10185 getAutoCreate : function(){
10187 var align = this.labelAlign || this.parentLabelAlign();
10192 cls: 'form-group' //input-group
10199 type : this.inputType,
10200 cls : 'form-control',
10201 autocomplete: 'new-password',
10202 placeholder : this.placeholder || ''
10206 input.name = this.name;
10209 input.cls += ' input-' + this.size;
10212 if (this.disabled) {
10213 input.disabled=true;
10216 var inputblock = input;
10218 if(this.hasFeedback && !this.allowBlank){
10222 cls: 'glyphicon form-control-feedback'
10225 if(this.removable && !this.editable && !this.tickable){
10227 cls : 'has-feedback',
10233 cls : 'roo-combo-removable-btn close'
10240 cls : 'has-feedback',
10249 if(this.removable && !this.editable && !this.tickable){
10251 cls : 'roo-removable',
10257 cls : 'roo-combo-removable-btn close'
10264 if (this.before || this.after) {
10267 cls : 'input-group',
10271 inputblock.cn.push({
10273 cls : 'input-group-addon',
10278 inputblock.cn.push(input);
10280 if(this.hasFeedback && !this.allowBlank){
10281 inputblock.cls += ' has-feedback';
10282 inputblock.cn.push(feedback);
10286 inputblock.cn.push({
10288 cls : 'input-group-addon',
10301 cls: 'form-hidden-field'
10315 cls: 'form-hidden-field'
10319 cls: 'roo-select2-choices',
10323 cls: 'roo-select2-search-field',
10336 cls: 'roo-select2-container input-group',
10341 // cls: 'typeahead typeahead-long dropdown-menu',
10342 // style: 'display:none'
10347 if(!this.multiple && this.showToggleBtn){
10353 if (this.caret != false) {
10356 cls: 'fa fa-' + this.caret
10363 cls : 'input-group-addon btn dropdown-toggle',
10368 cls: 'combobox-clear',
10382 combobox.cls += ' roo-select2-container-multi';
10385 if (align ==='left' && this.fieldLabel.length) {
10387 cfg.cls += ' roo-form-group-label-left';
10392 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10393 tooltip : 'This field is required'
10398 cls : 'control-label',
10399 html : this.fieldLabel
10411 var labelCfg = cfg.cn[1];
10412 var contentCfg = cfg.cn[2];
10414 if(this.indicatorpos == 'right'){
10419 cls : 'control-label',
10423 html : this.fieldLabel
10427 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10428 tooltip : 'This field is required'
10441 labelCfg = cfg.cn[0];
10442 contentCfg = cfg.cn[1];
10445 if(this.labelWidth > 12){
10446 labelCfg.style = "width: " + this.labelWidth + 'px';
10449 if(this.labelWidth < 13 && this.labelmd == 0){
10450 this.labelmd = this.labelWidth;
10453 if(this.labellg > 0){
10454 labelCfg.cls += ' col-lg-' + this.labellg;
10455 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10458 if(this.labelmd > 0){
10459 labelCfg.cls += ' col-md-' + this.labelmd;
10460 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10463 if(this.labelsm > 0){
10464 labelCfg.cls += ' col-sm-' + this.labelsm;
10465 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10468 if(this.labelxs > 0){
10469 labelCfg.cls += ' col-xs-' + this.labelxs;
10470 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10473 } else if ( this.fieldLabel.length) {
10474 // Roo.log(" label");
10478 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10479 tooltip : 'This field is required'
10483 //cls : 'input-group-addon',
10484 html : this.fieldLabel
10492 if(this.indicatorpos == 'right'){
10500 html : this.fieldLabel
10504 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10505 tooltip : 'This field is required'
10518 // Roo.log(" no label && no align");
10525 ['xs','sm','md','lg'].map(function(size){
10526 if (settings[size]) {
10527 cfg.cls += ' col-' + size + '-' + settings[size];
10538 onResize : function(w, h){
10539 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10540 // if(typeof w == 'number'){
10541 // var x = w - this.trigger.getWidth();
10542 // this.inputEl().setWidth(this.adjustWidth('input', x));
10543 // this.trigger.setStyle('left', x+'px');
10548 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10551 getResizeEl : function(){
10552 return this.inputEl();
10556 getPositionEl : function(){
10557 return this.inputEl();
10561 alignErrorIcon : function(){
10562 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10566 initEvents : function(){
10570 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10571 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10572 if(!this.multiple && this.showToggleBtn){
10573 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10574 if(this.hideTrigger){
10575 this.trigger.setDisplayed(false);
10577 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10581 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10584 if(this.removable && !this.editable && !this.tickable){
10585 var close = this.closeTriggerEl();
10588 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10589 close.on('click', this.removeBtnClick, this, close);
10593 //this.trigger.addClassOnOver('x-form-trigger-over');
10594 //this.trigger.addClassOnClick('x-form-trigger-click');
10597 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10601 closeTriggerEl : function()
10603 var close = this.el.select('.roo-combo-removable-btn', true).first();
10604 return close ? close : false;
10607 removeBtnClick : function(e, h, el)
10609 e.preventDefault();
10611 if(this.fireEvent("remove", this) !== false){
10613 this.fireEvent("afterremove", this)
10617 createList : function()
10619 this.list = Roo.get(document.body).createChild({
10621 cls: 'typeahead typeahead-long dropdown-menu',
10622 style: 'display:none'
10625 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10630 initTrigger : function(){
10635 onDestroy : function(){
10637 this.trigger.removeAllListeners();
10638 // this.trigger.remove();
10641 // this.wrap.remove();
10643 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10647 onFocus : function(){
10648 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10650 if(!this.mimicing){
10651 this.wrap.addClass('x-trigger-wrap-focus');
10652 this.mimicing = true;
10653 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10654 if(this.monitorTab){
10655 this.el.on("keydown", this.checkTab, this);
10662 checkTab : function(e){
10663 if(e.getKey() == e.TAB){
10664 this.triggerBlur();
10669 onBlur : function(){
10674 mimicBlur : function(e, t){
10676 if(!this.wrap.contains(t) && this.validateBlur()){
10677 this.triggerBlur();
10683 triggerBlur : function(){
10684 this.mimicing = false;
10685 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10686 if(this.monitorTab){
10687 this.el.un("keydown", this.checkTab, this);
10689 //this.wrap.removeClass('x-trigger-wrap-focus');
10690 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10694 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10695 validateBlur : function(e, t){
10700 onDisable : function(){
10701 this.inputEl().dom.disabled = true;
10702 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10704 // this.wrap.addClass('x-item-disabled');
10709 onEnable : function(){
10710 this.inputEl().dom.disabled = false;
10711 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10713 // this.el.removeClass('x-item-disabled');
10718 onShow : function(){
10719 var ae = this.getActionEl();
10722 ae.dom.style.display = '';
10723 ae.dom.style.visibility = 'visible';
10729 onHide : function(){
10730 var ae = this.getActionEl();
10731 ae.dom.style.display = 'none';
10735 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10736 * by an implementing function.
10738 * @param {EventObject} e
10740 onTriggerClick : Roo.emptyFn
10744 * Ext JS Library 1.1.1
10745 * Copyright(c) 2006-2007, Ext JS, LLC.
10747 * Originally Released Under LGPL - original licence link has changed is not relivant.
10750 * <script type="text/javascript">
10755 * @class Roo.data.SortTypes
10757 * Defines the default sorting (casting?) comparison functions used when sorting data.
10759 Roo.data.SortTypes = {
10761 * Default sort that does nothing
10762 * @param {Mixed} s The value being converted
10763 * @return {Mixed} The comparison value
10765 none : function(s){
10770 * The regular expression used to strip tags
10774 stripTagsRE : /<\/?[^>]+>/gi,
10777 * Strips all HTML tags to sort on text only
10778 * @param {Mixed} s The value being converted
10779 * @return {String} The comparison value
10781 asText : function(s){
10782 return String(s).replace(this.stripTagsRE, "");
10786 * Strips all HTML tags to sort on text only - Case insensitive
10787 * @param {Mixed} s The value being converted
10788 * @return {String} The comparison value
10790 asUCText : function(s){
10791 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10795 * Case insensitive string
10796 * @param {Mixed} s The value being converted
10797 * @return {String} The comparison value
10799 asUCString : function(s) {
10800 return String(s).toUpperCase();
10805 * @param {Mixed} s The value being converted
10806 * @return {Number} The comparison value
10808 asDate : function(s) {
10812 if(s instanceof Date){
10813 return s.getTime();
10815 return Date.parse(String(s));
10820 * @param {Mixed} s The value being converted
10821 * @return {Float} The comparison value
10823 asFloat : function(s) {
10824 var val = parseFloat(String(s).replace(/,/g, ""));
10833 * @param {Mixed} s The value being converted
10834 * @return {Number} The comparison value
10836 asInt : function(s) {
10837 var val = parseInt(String(s).replace(/,/g, ""));
10845 * Ext JS Library 1.1.1
10846 * Copyright(c) 2006-2007, Ext JS, LLC.
10848 * Originally Released Under LGPL - original licence link has changed is not relivant.
10851 * <script type="text/javascript">
10855 * @class Roo.data.Record
10856 * Instances of this class encapsulate both record <em>definition</em> information, and record
10857 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10858 * to access Records cached in an {@link Roo.data.Store} object.<br>
10860 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10861 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10864 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10866 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10867 * {@link #create}. The parameters are the same.
10868 * @param {Array} data An associative Array of data values keyed by the field name.
10869 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10870 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10871 * not specified an integer id is generated.
10873 Roo.data.Record = function(data, id){
10874 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10879 * Generate a constructor for a specific record layout.
10880 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10881 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10882 * Each field definition object may contain the following properties: <ul>
10883 * <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,
10884 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10885 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10886 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10887 * is being used, then this is a string containing the javascript expression to reference the data relative to
10888 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10889 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10890 * this may be omitted.</p></li>
10891 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10892 * <ul><li>auto (Default, implies no conversion)</li>
10897 * <li>date</li></ul></p></li>
10898 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10899 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10900 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10901 * by the Reader into an object that will be stored in the Record. It is passed the
10902 * following parameters:<ul>
10903 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10905 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10907 * <br>usage:<br><pre><code>
10908 var TopicRecord = Roo.data.Record.create(
10909 {name: 'title', mapping: 'topic_title'},
10910 {name: 'author', mapping: 'username'},
10911 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10912 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10913 {name: 'lastPoster', mapping: 'user2'},
10914 {name: 'excerpt', mapping: 'post_text'}
10917 var myNewRecord = new TopicRecord({
10918 title: 'Do my job please',
10921 lastPost: new Date(),
10922 lastPoster: 'Animal',
10923 excerpt: 'No way dude!'
10925 myStore.add(myNewRecord);
10930 Roo.data.Record.create = function(o){
10931 var f = function(){
10932 f.superclass.constructor.apply(this, arguments);
10934 Roo.extend(f, Roo.data.Record);
10935 var p = f.prototype;
10936 p.fields = new Roo.util.MixedCollection(false, function(field){
10939 for(var i = 0, len = o.length; i < len; i++){
10940 p.fields.add(new Roo.data.Field(o[i]));
10942 f.getField = function(name){
10943 return p.fields.get(name);
10948 Roo.data.Record.AUTO_ID = 1000;
10949 Roo.data.Record.EDIT = 'edit';
10950 Roo.data.Record.REJECT = 'reject';
10951 Roo.data.Record.COMMIT = 'commit';
10953 Roo.data.Record.prototype = {
10955 * Readonly flag - true if this record has been modified.
10964 join : function(store){
10965 this.store = store;
10969 * Set the named field to the specified value.
10970 * @param {String} name The name of the field to set.
10971 * @param {Object} value The value to set the field to.
10973 set : function(name, value){
10974 if(this.data[name] == value){
10978 if(!this.modified){
10979 this.modified = {};
10981 if(typeof this.modified[name] == 'undefined'){
10982 this.modified[name] = this.data[name];
10984 this.data[name] = value;
10985 if(!this.editing && this.store){
10986 this.store.afterEdit(this);
10991 * Get the value of the named field.
10992 * @param {String} name The name of the field to get the value of.
10993 * @return {Object} The value of the field.
10995 get : function(name){
10996 return this.data[name];
11000 beginEdit : function(){
11001 this.editing = true;
11002 this.modified = {};
11006 cancelEdit : function(){
11007 this.editing = false;
11008 delete this.modified;
11012 endEdit : function(){
11013 this.editing = false;
11014 if(this.dirty && this.store){
11015 this.store.afterEdit(this);
11020 * Usually called by the {@link Roo.data.Store} which owns the Record.
11021 * Rejects all changes made to the Record since either creation, or the last commit operation.
11022 * Modified fields are reverted to their original values.
11024 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11025 * of reject operations.
11027 reject : function(){
11028 var m = this.modified;
11030 if(typeof m[n] != "function"){
11031 this.data[n] = m[n];
11034 this.dirty = false;
11035 delete this.modified;
11036 this.editing = false;
11038 this.store.afterReject(this);
11043 * Usually called by the {@link Roo.data.Store} which owns the Record.
11044 * Commits all changes made to the Record since either creation, or the last commit operation.
11046 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11047 * of commit operations.
11049 commit : function(){
11050 this.dirty = false;
11051 delete this.modified;
11052 this.editing = false;
11054 this.store.afterCommit(this);
11059 hasError : function(){
11060 return this.error != null;
11064 clearError : function(){
11069 * Creates a copy of this record.
11070 * @param {String} id (optional) A new record id if you don't want to use this record's id
11073 copy : function(newId) {
11074 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11078 * Ext JS Library 1.1.1
11079 * Copyright(c) 2006-2007, Ext JS, LLC.
11081 * Originally Released Under LGPL - original licence link has changed is not relivant.
11084 * <script type="text/javascript">
11090 * @class Roo.data.Store
11091 * @extends Roo.util.Observable
11092 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11093 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11095 * 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
11096 * has no knowledge of the format of the data returned by the Proxy.<br>
11098 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11099 * instances from the data object. These records are cached and made available through accessor functions.
11101 * Creates a new Store.
11102 * @param {Object} config A config object containing the objects needed for the Store to access data,
11103 * and read the data into Records.
11105 Roo.data.Store = function(config){
11106 this.data = new Roo.util.MixedCollection(false);
11107 this.data.getKey = function(o){
11110 this.baseParams = {};
11112 this.paramNames = {
11117 "multisort" : "_multisort"
11120 if(config && config.data){
11121 this.inlineData = config.data;
11122 delete config.data;
11125 Roo.apply(this, config);
11127 if(this.reader){ // reader passed
11128 this.reader = Roo.factory(this.reader, Roo.data);
11129 this.reader.xmodule = this.xmodule || false;
11130 if(!this.recordType){
11131 this.recordType = this.reader.recordType;
11133 if(this.reader.onMetaChange){
11134 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11138 if(this.recordType){
11139 this.fields = this.recordType.prototype.fields;
11141 this.modified = [];
11145 * @event datachanged
11146 * Fires when the data cache has changed, and a widget which is using this Store
11147 * as a Record cache should refresh its view.
11148 * @param {Store} this
11150 datachanged : true,
11152 * @event metachange
11153 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11154 * @param {Store} this
11155 * @param {Object} meta The JSON metadata
11160 * Fires when Records have been added to the Store
11161 * @param {Store} this
11162 * @param {Roo.data.Record[]} records The array of Records added
11163 * @param {Number} index The index at which the record(s) were added
11168 * Fires when a Record has been removed from the Store
11169 * @param {Store} this
11170 * @param {Roo.data.Record} record The Record that was removed
11171 * @param {Number} index The index at which the record was removed
11176 * Fires when a Record has been updated
11177 * @param {Store} this
11178 * @param {Roo.data.Record} record The Record that was updated
11179 * @param {String} operation The update operation being performed. Value may be one of:
11181 Roo.data.Record.EDIT
11182 Roo.data.Record.REJECT
11183 Roo.data.Record.COMMIT
11189 * Fires when the data cache has been cleared.
11190 * @param {Store} this
11194 * @event beforeload
11195 * Fires before a request is made for a new data object. If the beforeload handler returns false
11196 * the load action will be canceled.
11197 * @param {Store} this
11198 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11202 * @event beforeloadadd
11203 * Fires after a new set of Records has been loaded.
11204 * @param {Store} this
11205 * @param {Roo.data.Record[]} records The Records that were loaded
11206 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11208 beforeloadadd : true,
11211 * Fires after a new set of Records has been loaded, before they are added to the store.
11212 * @param {Store} this
11213 * @param {Roo.data.Record[]} records The Records that were loaded
11214 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11215 * @params {Object} return from reader
11219 * @event loadexception
11220 * Fires if an exception occurs in the Proxy during loading.
11221 * Called with the signature of the Proxy's "loadexception" event.
11222 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11225 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11226 * @param {Object} load options
11227 * @param {Object} jsonData from your request (normally this contains the Exception)
11229 loadexception : true
11233 this.proxy = Roo.factory(this.proxy, Roo.data);
11234 this.proxy.xmodule = this.xmodule || false;
11235 this.relayEvents(this.proxy, ["loadexception"]);
11237 this.sortToggle = {};
11238 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11240 Roo.data.Store.superclass.constructor.call(this);
11242 if(this.inlineData){
11243 this.loadData(this.inlineData);
11244 delete this.inlineData;
11248 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11250 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11251 * without a remote query - used by combo/forms at present.
11255 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11258 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11261 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11262 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11265 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11266 * on any HTTP request
11269 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11272 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11276 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11277 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11279 remoteSort : false,
11282 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11283 * loaded or when a record is removed. (defaults to false).
11285 pruneModifiedRecords : false,
11288 lastOptions : null,
11291 * Add Records to the Store and fires the add event.
11292 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11294 add : function(records){
11295 records = [].concat(records);
11296 for(var i = 0, len = records.length; i < len; i++){
11297 records[i].join(this);
11299 var index = this.data.length;
11300 this.data.addAll(records);
11301 this.fireEvent("add", this, records, index);
11305 * Remove a Record from the Store and fires the remove event.
11306 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11308 remove : function(record){
11309 var index = this.data.indexOf(record);
11310 this.data.removeAt(index);
11312 if(this.pruneModifiedRecords){
11313 this.modified.remove(record);
11315 this.fireEvent("remove", this, record, index);
11319 * Remove all Records from the Store and fires the clear event.
11321 removeAll : function(){
11323 if(this.pruneModifiedRecords){
11324 this.modified = [];
11326 this.fireEvent("clear", this);
11330 * Inserts Records to the Store at the given index and fires the add event.
11331 * @param {Number} index The start index at which to insert the passed Records.
11332 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11334 insert : function(index, records){
11335 records = [].concat(records);
11336 for(var i = 0, len = records.length; i < len; i++){
11337 this.data.insert(index, records[i]);
11338 records[i].join(this);
11340 this.fireEvent("add", this, records, index);
11344 * Get the index within the cache of the passed Record.
11345 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11346 * @return {Number} The index of the passed Record. Returns -1 if not found.
11348 indexOf : function(record){
11349 return this.data.indexOf(record);
11353 * Get the index within the cache of the Record with the passed id.
11354 * @param {String} id The id of the Record to find.
11355 * @return {Number} The index of the Record. Returns -1 if not found.
11357 indexOfId : function(id){
11358 return this.data.indexOfKey(id);
11362 * Get the Record with the specified id.
11363 * @param {String} id The id of the Record to find.
11364 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11366 getById : function(id){
11367 return this.data.key(id);
11371 * Get the Record at the specified index.
11372 * @param {Number} index The index of the Record to find.
11373 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11375 getAt : function(index){
11376 return this.data.itemAt(index);
11380 * Returns a range of Records between specified indices.
11381 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11382 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11383 * @return {Roo.data.Record[]} An array of Records
11385 getRange : function(start, end){
11386 return this.data.getRange(start, end);
11390 storeOptions : function(o){
11391 o = Roo.apply({}, o);
11394 this.lastOptions = o;
11398 * Loads the Record cache from the configured Proxy using the configured Reader.
11400 * If using remote paging, then the first load call must specify the <em>start</em>
11401 * and <em>limit</em> properties in the options.params property to establish the initial
11402 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11404 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11405 * and this call will return before the new data has been loaded. Perform any post-processing
11406 * in a callback function, or in a "load" event handler.</strong>
11408 * @param {Object} options An object containing properties which control loading options:<ul>
11409 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11410 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11411 * passed the following arguments:<ul>
11412 * <li>r : Roo.data.Record[]</li>
11413 * <li>options: Options object from the load call</li>
11414 * <li>success: Boolean success indicator</li></ul></li>
11415 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11416 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11419 load : function(options){
11420 options = options || {};
11421 if(this.fireEvent("beforeload", this, options) !== false){
11422 this.storeOptions(options);
11423 var p = Roo.apply(options.params || {}, this.baseParams);
11424 // if meta was not loaded from remote source.. try requesting it.
11425 if (!this.reader.metaFromRemote) {
11426 p._requestMeta = 1;
11428 if(this.sortInfo && this.remoteSort){
11429 var pn = this.paramNames;
11430 p[pn["sort"]] = this.sortInfo.field;
11431 p[pn["dir"]] = this.sortInfo.direction;
11433 if (this.multiSort) {
11434 var pn = this.paramNames;
11435 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11438 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11443 * Reloads the Record cache from the configured Proxy using the configured Reader and
11444 * the options from the last load operation performed.
11445 * @param {Object} options (optional) An object containing properties which may override the options
11446 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11447 * the most recently used options are reused).
11449 reload : function(options){
11450 this.load(Roo.applyIf(options||{}, this.lastOptions));
11454 // Called as a callback by the Reader during a load operation.
11455 loadRecords : function(o, options, success){
11456 if(!o || success === false){
11457 if(success !== false){
11458 this.fireEvent("load", this, [], options, o);
11460 if(options.callback){
11461 options.callback.call(options.scope || this, [], options, false);
11465 // if data returned failure - throw an exception.
11466 if (o.success === false) {
11467 // show a message if no listener is registered.
11468 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11469 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11471 // loadmask wil be hooked into this..
11472 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11475 var r = o.records, t = o.totalRecords || r.length;
11477 this.fireEvent("beforeloadadd", this, r, options, o);
11479 if(!options || options.add !== true){
11480 if(this.pruneModifiedRecords){
11481 this.modified = [];
11483 for(var i = 0, len = r.length; i < len; i++){
11487 this.data = this.snapshot;
11488 delete this.snapshot;
11491 this.data.addAll(r);
11492 this.totalLength = t;
11494 this.fireEvent("datachanged", this);
11496 this.totalLength = Math.max(t, this.data.length+r.length);
11500 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11502 var e = new Roo.data.Record({});
11504 e.set(this.parent.displayField, this.parent.emptyTitle);
11505 e.set(this.parent.valueField, '');
11510 this.fireEvent("load", this, r, options, o);
11511 if(options.callback){
11512 options.callback.call(options.scope || this, r, options, true);
11518 * Loads data from a passed data block. A Reader which understands the format of the data
11519 * must have been configured in the constructor.
11520 * @param {Object} data The data block from which to read the Records. The format of the data expected
11521 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11522 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11524 loadData : function(o, append){
11525 var r = this.reader.readRecords(o);
11526 this.loadRecords(r, {add: append}, true);
11530 * Gets the number of cached records.
11532 * <em>If using paging, this may not be the total size of the dataset. If the data object
11533 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11534 * the data set size</em>
11536 getCount : function(){
11537 return this.data.length || 0;
11541 * Gets the total number of records in the dataset as returned by the server.
11543 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11544 * the dataset size</em>
11546 getTotalCount : function(){
11547 return this.totalLength || 0;
11551 * Returns the sort state of the Store as an object with two properties:
11553 field {String} The name of the field by which the Records are sorted
11554 direction {String} The sort order, "ASC" or "DESC"
11557 getSortState : function(){
11558 return this.sortInfo;
11562 applySort : function(){
11563 if(this.sortInfo && !this.remoteSort){
11564 var s = this.sortInfo, f = s.field;
11565 var st = this.fields.get(f).sortType;
11566 var fn = function(r1, r2){
11567 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11568 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11570 this.data.sort(s.direction, fn);
11571 if(this.snapshot && this.snapshot != this.data){
11572 this.snapshot.sort(s.direction, fn);
11578 * Sets the default sort column and order to be used by the next load operation.
11579 * @param {String} fieldName The name of the field to sort by.
11580 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11582 setDefaultSort : function(field, dir){
11583 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11587 * Sort the Records.
11588 * If remote sorting is used, the sort is performed on the server, and the cache is
11589 * reloaded. If local sorting is used, the cache is sorted internally.
11590 * @param {String} fieldName The name of the field to sort by.
11591 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11593 sort : function(fieldName, dir){
11594 var f = this.fields.get(fieldName);
11596 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11598 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11599 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11604 this.sortToggle[f.name] = dir;
11605 this.sortInfo = {field: f.name, direction: dir};
11606 if(!this.remoteSort){
11608 this.fireEvent("datachanged", this);
11610 this.load(this.lastOptions);
11615 * Calls the specified function for each of the Records in the cache.
11616 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11617 * Returning <em>false</em> aborts and exits the iteration.
11618 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11620 each : function(fn, scope){
11621 this.data.each(fn, scope);
11625 * Gets all records modified since the last commit. Modified records are persisted across load operations
11626 * (e.g., during paging).
11627 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11629 getModifiedRecords : function(){
11630 return this.modified;
11634 createFilterFn : function(property, value, anyMatch){
11635 if(!value.exec){ // not a regex
11636 value = String(value);
11637 if(value.length == 0){
11640 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11642 return function(r){
11643 return value.test(r.data[property]);
11648 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11649 * @param {String} property A field on your records
11650 * @param {Number} start The record index to start at (defaults to 0)
11651 * @param {Number} end The last record index to include (defaults to length - 1)
11652 * @return {Number} The sum
11654 sum : function(property, start, end){
11655 var rs = this.data.items, v = 0;
11656 start = start || 0;
11657 end = (end || end === 0) ? end : rs.length-1;
11659 for(var i = start; i <= end; i++){
11660 v += (rs[i].data[property] || 0);
11666 * Filter the records by a specified property.
11667 * @param {String} field A field on your records
11668 * @param {String/RegExp} value Either a string that the field
11669 * should start with or a RegExp to test against the field
11670 * @param {Boolean} anyMatch True to match any part not just the beginning
11672 filter : function(property, value, anyMatch){
11673 var fn = this.createFilterFn(property, value, anyMatch);
11674 return fn ? this.filterBy(fn) : this.clearFilter();
11678 * Filter by a function. The specified function will be called with each
11679 * record in this data source. If the function returns true the record is included,
11680 * otherwise it is filtered.
11681 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11682 * @param {Object} scope (optional) The scope of the function (defaults to this)
11684 filterBy : function(fn, scope){
11685 this.snapshot = this.snapshot || this.data;
11686 this.data = this.queryBy(fn, scope||this);
11687 this.fireEvent("datachanged", this);
11691 * Query the records by a specified property.
11692 * @param {String} field A field on your records
11693 * @param {String/RegExp} value Either a string that the field
11694 * should start with or a RegExp to test against the field
11695 * @param {Boolean} anyMatch True to match any part not just the beginning
11696 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11698 query : function(property, value, anyMatch){
11699 var fn = this.createFilterFn(property, value, anyMatch);
11700 return fn ? this.queryBy(fn) : this.data.clone();
11704 * Query by a function. The specified function will be called with each
11705 * record in this data source. If the function returns true the record is included
11707 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11708 * @param {Object} scope (optional) The scope of the function (defaults to this)
11709 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11711 queryBy : function(fn, scope){
11712 var data = this.snapshot || this.data;
11713 return data.filterBy(fn, scope||this);
11717 * Collects unique values for a particular dataIndex from this store.
11718 * @param {String} dataIndex The property to collect
11719 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11720 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11721 * @return {Array} An array of the unique values
11723 collect : function(dataIndex, allowNull, bypassFilter){
11724 var d = (bypassFilter === true && this.snapshot) ?
11725 this.snapshot.items : this.data.items;
11726 var v, sv, r = [], l = {};
11727 for(var i = 0, len = d.length; i < len; i++){
11728 v = d[i].data[dataIndex];
11730 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11739 * Revert to a view of the Record cache with no filtering applied.
11740 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11742 clearFilter : function(suppressEvent){
11743 if(this.snapshot && this.snapshot != this.data){
11744 this.data = this.snapshot;
11745 delete this.snapshot;
11746 if(suppressEvent !== true){
11747 this.fireEvent("datachanged", this);
11753 afterEdit : function(record){
11754 if(this.modified.indexOf(record) == -1){
11755 this.modified.push(record);
11757 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11761 afterReject : function(record){
11762 this.modified.remove(record);
11763 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11767 afterCommit : function(record){
11768 this.modified.remove(record);
11769 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11773 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11774 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11776 commitChanges : function(){
11777 var m = this.modified.slice(0);
11778 this.modified = [];
11779 for(var i = 0, len = m.length; i < len; i++){
11785 * Cancel outstanding changes on all changed records.
11787 rejectChanges : function(){
11788 var m = this.modified.slice(0);
11789 this.modified = [];
11790 for(var i = 0, len = m.length; i < len; i++){
11795 onMetaChange : function(meta, rtype, o){
11796 this.recordType = rtype;
11797 this.fields = rtype.prototype.fields;
11798 delete this.snapshot;
11799 this.sortInfo = meta.sortInfo || this.sortInfo;
11800 this.modified = [];
11801 this.fireEvent('metachange', this, this.reader.meta);
11804 moveIndex : function(data, type)
11806 var index = this.indexOf(data);
11808 var newIndex = index + type;
11812 this.insert(newIndex, data);
11817 * Ext JS Library 1.1.1
11818 * Copyright(c) 2006-2007, Ext JS, LLC.
11820 * Originally Released Under LGPL - original licence link has changed is not relivant.
11823 * <script type="text/javascript">
11827 * @class Roo.data.SimpleStore
11828 * @extends Roo.data.Store
11829 * Small helper class to make creating Stores from Array data easier.
11830 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11831 * @cfg {Array} fields An array of field definition objects, or field name strings.
11832 * @cfg {Array} data The multi-dimensional array of data
11834 * @param {Object} config
11836 Roo.data.SimpleStore = function(config){
11837 Roo.data.SimpleStore.superclass.constructor.call(this, {
11839 reader: new Roo.data.ArrayReader({
11842 Roo.data.Record.create(config.fields)
11844 proxy : new Roo.data.MemoryProxy(config.data)
11848 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11850 * Ext JS Library 1.1.1
11851 * Copyright(c) 2006-2007, Ext JS, LLC.
11853 * Originally Released Under LGPL - original licence link has changed is not relivant.
11856 * <script type="text/javascript">
11861 * @extends Roo.data.Store
11862 * @class Roo.data.JsonStore
11863 * Small helper class to make creating Stores for JSON data easier. <br/>
11865 var store = new Roo.data.JsonStore({
11866 url: 'get-images.php',
11868 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11871 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11872 * JsonReader and HttpProxy (unless inline data is provided).</b>
11873 * @cfg {Array} fields An array of field definition objects, or field name strings.
11875 * @param {Object} config
11877 Roo.data.JsonStore = function(c){
11878 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11879 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11880 reader: new Roo.data.JsonReader(c, c.fields)
11883 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11885 * Ext JS Library 1.1.1
11886 * Copyright(c) 2006-2007, Ext JS, LLC.
11888 * Originally Released Under LGPL - original licence link has changed is not relivant.
11891 * <script type="text/javascript">
11895 Roo.data.Field = function(config){
11896 if(typeof config == "string"){
11897 config = {name: config};
11899 Roo.apply(this, config);
11902 this.type = "auto";
11905 var st = Roo.data.SortTypes;
11906 // named sortTypes are supported, here we look them up
11907 if(typeof this.sortType == "string"){
11908 this.sortType = st[this.sortType];
11911 // set default sortType for strings and dates
11912 if(!this.sortType){
11915 this.sortType = st.asUCString;
11918 this.sortType = st.asDate;
11921 this.sortType = st.none;
11926 var stripRe = /[\$,%]/g;
11928 // prebuilt conversion function for this field, instead of
11929 // switching every time we're reading a value
11931 var cv, dateFormat = this.dateFormat;
11936 cv = function(v){ return v; };
11939 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11943 return v !== undefined && v !== null && v !== '' ?
11944 parseInt(String(v).replace(stripRe, ""), 10) : '';
11949 return v !== undefined && v !== null && v !== '' ?
11950 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11955 cv = function(v){ return v === true || v === "true" || v == 1; };
11962 if(v instanceof Date){
11966 if(dateFormat == "timestamp"){
11967 return new Date(v*1000);
11969 return Date.parseDate(v, dateFormat);
11971 var parsed = Date.parse(v);
11972 return parsed ? new Date(parsed) : null;
11981 Roo.data.Field.prototype = {
11989 * Ext JS Library 1.1.1
11990 * Copyright(c) 2006-2007, Ext JS, LLC.
11992 * Originally Released Under LGPL - original licence link has changed is not relivant.
11995 * <script type="text/javascript">
11998 // Base class for reading structured data from a data source. This class is intended to be
11999 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12002 * @class Roo.data.DataReader
12003 * Base class for reading structured data from a data source. This class is intended to be
12004 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12007 Roo.data.DataReader = function(meta, recordType){
12011 this.recordType = recordType instanceof Array ?
12012 Roo.data.Record.create(recordType) : recordType;
12015 Roo.data.DataReader.prototype = {
12017 * Create an empty record
12018 * @param {Object} data (optional) - overlay some values
12019 * @return {Roo.data.Record} record created.
12021 newRow : function(d) {
12023 this.recordType.prototype.fields.each(function(c) {
12025 case 'int' : da[c.name] = 0; break;
12026 case 'date' : da[c.name] = new Date(); break;
12027 case 'float' : da[c.name] = 0.0; break;
12028 case 'boolean' : da[c.name] = false; break;
12029 default : da[c.name] = ""; break;
12033 return new this.recordType(Roo.apply(da, d));
12038 * Ext JS Library 1.1.1
12039 * Copyright(c) 2006-2007, Ext JS, LLC.
12041 * Originally Released Under LGPL - original licence link has changed is not relivant.
12044 * <script type="text/javascript">
12048 * @class Roo.data.DataProxy
12049 * @extends Roo.data.Observable
12050 * This class is an abstract base class for implementations which provide retrieval of
12051 * unformatted data objects.<br>
12053 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12054 * (of the appropriate type which knows how to parse the data object) to provide a block of
12055 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12057 * Custom implementations must implement the load method as described in
12058 * {@link Roo.data.HttpProxy#load}.
12060 Roo.data.DataProxy = function(){
12063 * @event beforeload
12064 * Fires before a network request is made to retrieve a data object.
12065 * @param {Object} This DataProxy object.
12066 * @param {Object} params The params parameter to the load function.
12071 * Fires before the load method's callback is called.
12072 * @param {Object} This DataProxy object.
12073 * @param {Object} o The data object.
12074 * @param {Object} arg The callback argument object passed to the load function.
12078 * @event loadexception
12079 * Fires if an Exception occurs during data retrieval.
12080 * @param {Object} This DataProxy object.
12081 * @param {Object} o The data object.
12082 * @param {Object} arg The callback argument object passed to the load function.
12083 * @param {Object} e The Exception.
12085 loadexception : true
12087 Roo.data.DataProxy.superclass.constructor.call(this);
12090 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12093 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12097 * Ext JS Library 1.1.1
12098 * Copyright(c) 2006-2007, Ext JS, LLC.
12100 * Originally Released Under LGPL - original licence link has changed is not relivant.
12103 * <script type="text/javascript">
12106 * @class Roo.data.MemoryProxy
12107 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12108 * to the Reader when its load method is called.
12110 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12112 Roo.data.MemoryProxy = function(data){
12116 Roo.data.MemoryProxy.superclass.constructor.call(this);
12120 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12123 * Load data from the requested source (in this case an in-memory
12124 * data object passed to the constructor), read the data object into
12125 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12126 * process that block using the passed callback.
12127 * @param {Object} params This parameter is not used by the MemoryProxy class.
12128 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12129 * object into a block of Roo.data.Records.
12130 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12131 * The function must be passed <ul>
12132 * <li>The Record block object</li>
12133 * <li>The "arg" argument from the load function</li>
12134 * <li>A boolean success indicator</li>
12136 * @param {Object} scope The scope in which to call the callback
12137 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12139 load : function(params, reader, callback, scope, arg){
12140 params = params || {};
12143 result = reader.readRecords(this.data);
12145 this.fireEvent("loadexception", this, arg, null, e);
12146 callback.call(scope, null, arg, false);
12149 callback.call(scope, result, arg, true);
12153 update : function(params, records){
12158 * Ext JS Library 1.1.1
12159 * Copyright(c) 2006-2007, Ext JS, LLC.
12161 * Originally Released Under LGPL - original licence link has changed is not relivant.
12164 * <script type="text/javascript">
12167 * @class Roo.data.HttpProxy
12168 * @extends Roo.data.DataProxy
12169 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12170 * configured to reference a certain URL.<br><br>
12172 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12173 * from which the running page was served.<br><br>
12175 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12177 * Be aware that to enable the browser to parse an XML document, the server must set
12178 * the Content-Type header in the HTTP response to "text/xml".
12180 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12181 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12182 * will be used to make the request.
12184 Roo.data.HttpProxy = function(conn){
12185 Roo.data.HttpProxy.superclass.constructor.call(this);
12186 // is conn a conn config or a real conn?
12188 this.useAjax = !conn || !conn.events;
12192 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12193 // thse are take from connection...
12196 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12199 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12200 * extra parameters to each request made by this object. (defaults to undefined)
12203 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12204 * to each request made by this object. (defaults to undefined)
12207 * @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)
12210 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12213 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12219 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12223 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12224 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12225 * a finer-grained basis than the DataProxy events.
12227 getConnection : function(){
12228 return this.useAjax ? Roo.Ajax : this.conn;
12232 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12233 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12234 * process that block using the passed callback.
12235 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12236 * for the request to the remote server.
12237 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12238 * object into a block of Roo.data.Records.
12239 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12240 * The function must be passed <ul>
12241 * <li>The Record block object</li>
12242 * <li>The "arg" argument from the load function</li>
12243 * <li>A boolean success indicator</li>
12245 * @param {Object} scope The scope in which to call the callback
12246 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12248 load : function(params, reader, callback, scope, arg){
12249 if(this.fireEvent("beforeload", this, params) !== false){
12251 params : params || {},
12253 callback : callback,
12258 callback : this.loadResponse,
12262 Roo.applyIf(o, this.conn);
12263 if(this.activeRequest){
12264 Roo.Ajax.abort(this.activeRequest);
12266 this.activeRequest = Roo.Ajax.request(o);
12268 this.conn.request(o);
12271 callback.call(scope||this, null, arg, false);
12276 loadResponse : function(o, success, response){
12277 delete this.activeRequest;
12279 this.fireEvent("loadexception", this, o, response);
12280 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12285 result = o.reader.read(response);
12287 this.fireEvent("loadexception", this, o, response, e);
12288 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12292 this.fireEvent("load", this, o, o.request.arg);
12293 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12297 update : function(dataSet){
12302 updateResponse : function(dataSet){
12307 * Ext JS Library 1.1.1
12308 * Copyright(c) 2006-2007, Ext JS, LLC.
12310 * Originally Released Under LGPL - original licence link has changed is not relivant.
12313 * <script type="text/javascript">
12317 * @class Roo.data.ScriptTagProxy
12318 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12319 * other than the originating domain of the running page.<br><br>
12321 * <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
12322 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12324 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12325 * source code that is used as the source inside a <script> tag.<br><br>
12327 * In order for the browser to process the returned data, the server must wrap the data object
12328 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12329 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12330 * depending on whether the callback name was passed:
12333 boolean scriptTag = false;
12334 String cb = request.getParameter("callback");
12337 response.setContentType("text/javascript");
12339 response.setContentType("application/x-json");
12341 Writer out = response.getWriter();
12343 out.write(cb + "(");
12345 out.print(dataBlock.toJsonString());
12352 * @param {Object} config A configuration object.
12354 Roo.data.ScriptTagProxy = function(config){
12355 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12356 Roo.apply(this, config);
12357 this.head = document.getElementsByTagName("head")[0];
12360 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12362 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12364 * @cfg {String} url The URL from which to request the data object.
12367 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12371 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12372 * the server the name of the callback function set up by the load call to process the returned data object.
12373 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12374 * javascript output which calls this named function passing the data object as its only parameter.
12376 callbackParam : "callback",
12378 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12379 * name to the request.
12384 * Load data from the configured URL, read the data object into
12385 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12386 * process that block using the passed callback.
12387 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12388 * for the request to the remote server.
12389 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12390 * object into a block of Roo.data.Records.
12391 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12392 * The function must be passed <ul>
12393 * <li>The Record block object</li>
12394 * <li>The "arg" argument from the load function</li>
12395 * <li>A boolean success indicator</li>
12397 * @param {Object} scope The scope in which to call the callback
12398 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12400 load : function(params, reader, callback, scope, arg){
12401 if(this.fireEvent("beforeload", this, params) !== false){
12403 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12405 var url = this.url;
12406 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12408 url += "&_dc=" + (new Date().getTime());
12410 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12413 cb : "stcCallback"+transId,
12414 scriptId : "stcScript"+transId,
12418 callback : callback,
12424 window[trans.cb] = function(o){
12425 conn.handleResponse(o, trans);
12428 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12430 if(this.autoAbort !== false){
12434 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12436 var script = document.createElement("script");
12437 script.setAttribute("src", url);
12438 script.setAttribute("type", "text/javascript");
12439 script.setAttribute("id", trans.scriptId);
12440 this.head.appendChild(script);
12442 this.trans = trans;
12444 callback.call(scope||this, null, arg, false);
12449 isLoading : function(){
12450 return this.trans ? true : false;
12454 * Abort the current server request.
12456 abort : function(){
12457 if(this.isLoading()){
12458 this.destroyTrans(this.trans);
12463 destroyTrans : function(trans, isLoaded){
12464 this.head.removeChild(document.getElementById(trans.scriptId));
12465 clearTimeout(trans.timeoutId);
12467 window[trans.cb] = undefined;
12469 delete window[trans.cb];
12472 // if hasn't been loaded, wait for load to remove it to prevent script error
12473 window[trans.cb] = function(){
12474 window[trans.cb] = undefined;
12476 delete window[trans.cb];
12483 handleResponse : function(o, trans){
12484 this.trans = false;
12485 this.destroyTrans(trans, true);
12488 result = trans.reader.readRecords(o);
12490 this.fireEvent("loadexception", this, o, trans.arg, e);
12491 trans.callback.call(trans.scope||window, null, trans.arg, false);
12494 this.fireEvent("load", this, o, trans.arg);
12495 trans.callback.call(trans.scope||window, result, trans.arg, true);
12499 handleFailure : function(trans){
12500 this.trans = false;
12501 this.destroyTrans(trans, false);
12502 this.fireEvent("loadexception", this, null, trans.arg);
12503 trans.callback.call(trans.scope||window, null, trans.arg, false);
12507 * Ext JS Library 1.1.1
12508 * Copyright(c) 2006-2007, Ext JS, LLC.
12510 * Originally Released Under LGPL - original licence link has changed is not relivant.
12513 * <script type="text/javascript">
12517 * @class Roo.data.JsonReader
12518 * @extends Roo.data.DataReader
12519 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12520 * based on mappings in a provided Roo.data.Record constructor.
12522 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12523 * in the reply previously.
12528 var RecordDef = Roo.data.Record.create([
12529 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12530 {name: 'occupation'} // This field will use "occupation" as the mapping.
12532 var myReader = new Roo.data.JsonReader({
12533 totalProperty: "results", // The property which contains the total dataset size (optional)
12534 root: "rows", // The property which contains an Array of row objects
12535 id: "id" // The property within each row object that provides an ID for the record (optional)
12539 * This would consume a JSON file like this:
12541 { 'results': 2, 'rows': [
12542 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12543 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12546 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12547 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12548 * paged from the remote server.
12549 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12550 * @cfg {String} root name of the property which contains the Array of row objects.
12551 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12552 * @cfg {Array} fields Array of field definition objects
12554 * Create a new JsonReader
12555 * @param {Object} meta Metadata configuration options
12556 * @param {Object} recordType Either an Array of field definition objects,
12557 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12559 Roo.data.JsonReader = function(meta, recordType){
12562 // set some defaults:
12563 Roo.applyIf(meta, {
12564 totalProperty: 'total',
12565 successProperty : 'success',
12570 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12572 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12575 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12576 * Used by Store query builder to append _requestMeta to params.
12579 metaFromRemote : false,
12581 * This method is only used by a DataProxy which has retrieved data from a remote server.
12582 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12583 * @return {Object} data A data block which is used by an Roo.data.Store object as
12584 * a cache of Roo.data.Records.
12586 read : function(response){
12587 var json = response.responseText;
12589 var o = /* eval:var:o */ eval("("+json+")");
12591 throw {message: "JsonReader.read: Json object not found"};
12597 this.metaFromRemote = true;
12598 this.meta = o.metaData;
12599 this.recordType = Roo.data.Record.create(o.metaData.fields);
12600 this.onMetaChange(this.meta, this.recordType, o);
12602 return this.readRecords(o);
12605 // private function a store will implement
12606 onMetaChange : function(meta, recordType, o){
12613 simpleAccess: function(obj, subsc) {
12620 getJsonAccessor: function(){
12622 return function(expr) {
12624 return(re.test(expr))
12625 ? new Function("obj", "return obj." + expr)
12630 return Roo.emptyFn;
12635 * Create a data block containing Roo.data.Records from an XML document.
12636 * @param {Object} o An object which contains an Array of row objects in the property specified
12637 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12638 * which contains the total size of the dataset.
12639 * @return {Object} data A data block which is used by an Roo.data.Store object as
12640 * a cache of Roo.data.Records.
12642 readRecords : function(o){
12644 * After any data loads, the raw JSON data is available for further custom processing.
12648 var s = this.meta, Record = this.recordType,
12649 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12651 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12653 if(s.totalProperty) {
12654 this.getTotal = this.getJsonAccessor(s.totalProperty);
12656 if(s.successProperty) {
12657 this.getSuccess = this.getJsonAccessor(s.successProperty);
12659 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12661 var g = this.getJsonAccessor(s.id);
12662 this.getId = function(rec) {
12664 return (r === undefined || r === "") ? null : r;
12667 this.getId = function(){return null;};
12670 for(var jj = 0; jj < fl; jj++){
12672 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12673 this.ef[jj] = this.getJsonAccessor(map);
12677 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12678 if(s.totalProperty){
12679 var vt = parseInt(this.getTotal(o), 10);
12684 if(s.successProperty){
12685 var vs = this.getSuccess(o);
12686 if(vs === false || vs === 'false'){
12691 for(var i = 0; i < c; i++){
12694 var id = this.getId(n);
12695 for(var j = 0; j < fl; j++){
12697 var v = this.ef[j](n);
12699 Roo.log('missing convert for ' + f.name);
12703 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12705 var record = new Record(values, id);
12707 records[i] = record;
12713 totalRecords : totalRecords
12718 * Ext JS Library 1.1.1
12719 * Copyright(c) 2006-2007, Ext JS, LLC.
12721 * Originally Released Under LGPL - original licence link has changed is not relivant.
12724 * <script type="text/javascript">
12728 * @class Roo.data.ArrayReader
12729 * @extends Roo.data.DataReader
12730 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12731 * Each element of that Array represents a row of data fields. The
12732 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12733 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12737 var RecordDef = Roo.data.Record.create([
12738 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12739 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12741 var myReader = new Roo.data.ArrayReader({
12742 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12746 * This would consume an Array like this:
12748 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12750 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12752 * Create a new JsonReader
12753 * @param {Object} meta Metadata configuration options.
12754 * @param {Object} recordType Either an Array of field definition objects
12755 * as specified to {@link Roo.data.Record#create},
12756 * or an {@link Roo.data.Record} object
12757 * created using {@link Roo.data.Record#create}.
12759 Roo.data.ArrayReader = function(meta, recordType){
12760 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12763 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12765 * Create a data block containing Roo.data.Records from an XML document.
12766 * @param {Object} o An Array of row objects which represents the dataset.
12767 * @return {Object} data A data block which is used by an Roo.data.Store object as
12768 * a cache of Roo.data.Records.
12770 readRecords : function(o){
12771 var sid = this.meta ? this.meta.id : null;
12772 var recordType = this.recordType, fields = recordType.prototype.fields;
12775 for(var i = 0; i < root.length; i++){
12778 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12779 for(var j = 0, jlen = fields.length; j < jlen; j++){
12780 var f = fields.items[j];
12781 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12782 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12784 values[f.name] = v;
12786 var record = new recordType(values, id);
12788 records[records.length] = record;
12792 totalRecords : records.length
12801 * @class Roo.bootstrap.ComboBox
12802 * @extends Roo.bootstrap.TriggerField
12803 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12804 * @cfg {Boolean} append (true|false) default false
12805 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12806 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12807 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12808 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12809 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12810 * @cfg {Boolean} animate default true
12811 * @cfg {Boolean} emptyResultText only for touch device
12812 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12813 * @cfg {String} emptyTitle default ''
12815 * Create a new ComboBox.
12816 * @param {Object} config Configuration options
12818 Roo.bootstrap.ComboBox = function(config){
12819 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12823 * Fires when the dropdown list is expanded
12824 * @param {Roo.bootstrap.ComboBox} combo This combo box
12829 * Fires when the dropdown list is collapsed
12830 * @param {Roo.bootstrap.ComboBox} combo This combo box
12834 * @event beforeselect
12835 * Fires before a list item is selected. Return false to cancel the selection.
12836 * @param {Roo.bootstrap.ComboBox} combo This combo box
12837 * @param {Roo.data.Record} record The data record returned from the underlying store
12838 * @param {Number} index The index of the selected item in the dropdown list
12840 'beforeselect' : true,
12843 * Fires when a list item is selected
12844 * @param {Roo.bootstrap.ComboBox} combo This combo box
12845 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12846 * @param {Number} index The index of the selected item in the dropdown list
12850 * @event beforequery
12851 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12852 * The event object passed has these properties:
12853 * @param {Roo.bootstrap.ComboBox} combo This combo box
12854 * @param {String} query The query
12855 * @param {Boolean} forceAll true to force "all" query
12856 * @param {Boolean} cancel true to cancel the query
12857 * @param {Object} e The query event object
12859 'beforequery': true,
12862 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12863 * @param {Roo.bootstrap.ComboBox} combo This combo box
12868 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12869 * @param {Roo.bootstrap.ComboBox} combo This combo box
12870 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12875 * Fires when the remove value from the combobox array
12876 * @param {Roo.bootstrap.ComboBox} combo This combo box
12880 * @event afterremove
12881 * Fires when the remove value from the combobox array
12882 * @param {Roo.bootstrap.ComboBox} combo This combo box
12884 'afterremove' : true,
12886 * @event specialfilter
12887 * Fires when specialfilter
12888 * @param {Roo.bootstrap.ComboBox} combo This combo box
12890 'specialfilter' : true,
12893 * Fires when tick the element
12894 * @param {Roo.bootstrap.ComboBox} combo This combo box
12898 * @event touchviewdisplay
12899 * Fires when touch view require special display (default is using displayField)
12900 * @param {Roo.bootstrap.ComboBox} combo This combo box
12901 * @param {Object} cfg set html .
12903 'touchviewdisplay' : true
12908 this.tickItems = [];
12910 this.selectedIndex = -1;
12911 if(this.mode == 'local'){
12912 if(config.queryDelay === undefined){
12913 this.queryDelay = 10;
12915 if(config.minChars === undefined){
12921 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12924 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12925 * rendering into an Roo.Editor, defaults to false)
12928 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12929 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12932 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12935 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12936 * the dropdown list (defaults to undefined, with no header element)
12940 * @cfg {String/Roo.Template} tpl The template to use to render the output
12944 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12946 listWidth: undefined,
12948 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12949 * mode = 'remote' or 'text' if mode = 'local')
12951 displayField: undefined,
12954 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12955 * mode = 'remote' or 'value' if mode = 'local').
12956 * Note: use of a valueField requires the user make a selection
12957 * in order for a value to be mapped.
12959 valueField: undefined,
12961 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12966 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12967 * field's data value (defaults to the underlying DOM element's name)
12969 hiddenName: undefined,
12971 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12975 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12977 selectedClass: 'active',
12980 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12984 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12985 * anchor positions (defaults to 'tl-bl')
12987 listAlign: 'tl-bl?',
12989 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12993 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12994 * query specified by the allQuery config option (defaults to 'query')
12996 triggerAction: 'query',
12998 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12999 * (defaults to 4, does not apply if editable = false)
13003 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13004 * delay (typeAheadDelay) if it matches a known value (defaults to false)
13008 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13009 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13013 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13014 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
13018 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
13019 * when editable = true (defaults to false)
13021 selectOnFocus:false,
13023 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13025 queryParam: 'query',
13027 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13028 * when mode = 'remote' (defaults to 'Loading...')
13030 loadingText: 'Loading...',
13032 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13036 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13040 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13041 * traditional select (defaults to true)
13045 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13049 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13053 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13054 * listWidth has a higher value)
13058 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13059 * allow the user to set arbitrary text into the field (defaults to false)
13061 forceSelection:false,
13063 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13064 * if typeAhead = true (defaults to 250)
13066 typeAheadDelay : 250,
13068 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13069 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13071 valueNotFoundText : undefined,
13073 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13075 blockFocus : false,
13078 * @cfg {Boolean} disableClear Disable showing of clear button.
13080 disableClear : false,
13082 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13084 alwaysQuery : false,
13087 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13092 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13094 invalidClass : "has-warning",
13097 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13099 validClass : "has-success",
13102 * @cfg {Boolean} specialFilter (true|false) special filter default false
13104 specialFilter : false,
13107 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13109 mobileTouchView : true,
13112 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13114 useNativeIOS : false,
13117 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13119 mobile_restrict_height : false,
13121 ios_options : false,
13133 btnPosition : 'right',
13134 triggerList : true,
13135 showToggleBtn : true,
13137 emptyResultText: 'Empty',
13138 triggerText : 'Select',
13141 // element that contains real text value.. (when hidden is used..)
13143 getAutoCreate : function()
13148 * Render classic select for iso
13151 if(Roo.isIOS && this.useNativeIOS){
13152 cfg = this.getAutoCreateNativeIOS();
13160 if(Roo.isTouch && this.mobileTouchView){
13161 cfg = this.getAutoCreateTouchView();
13168 if(!this.tickable){
13169 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13174 * ComboBox with tickable selections
13177 var align = this.labelAlign || this.parentLabelAlign();
13180 cls : 'form-group roo-combobox-tickable' //input-group
13183 var btn_text_select = '';
13184 var btn_text_done = '';
13185 var btn_text_cancel = '';
13187 if (this.btn_text_show) {
13188 btn_text_select = 'Select';
13189 btn_text_done = 'Done';
13190 btn_text_cancel = 'Cancel';
13195 cls : 'tickable-buttons',
13200 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13201 //html : this.triggerText
13202 html: btn_text_select
13208 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13210 html: btn_text_done
13216 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13218 html: btn_text_cancel
13224 buttons.cn.unshift({
13226 cls: 'roo-select2-search-field-input'
13232 Roo.each(buttons.cn, function(c){
13234 c.cls += ' btn-' + _this.size;
13237 if (_this.disabled) {
13248 cls: 'form-hidden-field'
13252 cls: 'roo-select2-choices',
13256 cls: 'roo-select2-search-field',
13267 cls: 'roo-select2-container input-group roo-select2-container-multi',
13272 // cls: 'typeahead typeahead-long dropdown-menu',
13273 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13278 if(this.hasFeedback && !this.allowBlank){
13282 cls: 'glyphicon form-control-feedback'
13285 combobox.cn.push(feedback);
13289 if (align ==='left' && this.fieldLabel.length) {
13291 cfg.cls += ' roo-form-group-label-left';
13296 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13297 tooltip : 'This field is required'
13302 cls : 'control-label',
13303 html : this.fieldLabel
13315 var labelCfg = cfg.cn[1];
13316 var contentCfg = cfg.cn[2];
13319 if(this.indicatorpos == 'right'){
13325 cls : 'control-label',
13329 html : this.fieldLabel
13333 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13334 tooltip : 'This field is required'
13349 labelCfg = cfg.cn[0];
13350 contentCfg = cfg.cn[1];
13354 if(this.labelWidth > 12){
13355 labelCfg.style = "width: " + this.labelWidth + 'px';
13358 if(this.labelWidth < 13 && this.labelmd == 0){
13359 this.labelmd = this.labelWidth;
13362 if(this.labellg > 0){
13363 labelCfg.cls += ' col-lg-' + this.labellg;
13364 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13367 if(this.labelmd > 0){
13368 labelCfg.cls += ' col-md-' + this.labelmd;
13369 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13372 if(this.labelsm > 0){
13373 labelCfg.cls += ' col-sm-' + this.labelsm;
13374 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13377 if(this.labelxs > 0){
13378 labelCfg.cls += ' col-xs-' + this.labelxs;
13379 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13383 } else if ( this.fieldLabel.length) {
13384 // Roo.log(" label");
13388 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13389 tooltip : 'This field is required'
13393 //cls : 'input-group-addon',
13394 html : this.fieldLabel
13399 if(this.indicatorpos == 'right'){
13403 //cls : 'input-group-addon',
13404 html : this.fieldLabel
13408 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13409 tooltip : 'This field is required'
13418 // Roo.log(" no label && no align");
13425 ['xs','sm','md','lg'].map(function(size){
13426 if (settings[size]) {
13427 cfg.cls += ' col-' + size + '-' + settings[size];
13435 _initEventsCalled : false,
13438 initEvents: function()
13440 if (this._initEventsCalled) { // as we call render... prevent looping...
13443 this._initEventsCalled = true;
13446 throw "can not find store for combo";
13449 this.indicator = this.indicatorEl();
13451 this.store = Roo.factory(this.store, Roo.data);
13452 this.store.parent = this;
13454 // if we are building from html. then this element is so complex, that we can not really
13455 // use the rendered HTML.
13456 // so we have to trash and replace the previous code.
13457 if (Roo.XComponent.build_from_html) {
13458 // remove this element....
13459 var e = this.el.dom, k=0;
13460 while (e ) { e = e.previousSibling; ++k;}
13465 this.rendered = false;
13467 this.render(this.parent().getChildContainer(true), k);
13470 if(Roo.isIOS && this.useNativeIOS){
13471 this.initIOSView();
13479 if(Roo.isTouch && this.mobileTouchView){
13480 this.initTouchView();
13485 this.initTickableEvents();
13489 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13491 if(this.hiddenName){
13493 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13495 this.hiddenField.dom.value =
13496 this.hiddenValue !== undefined ? this.hiddenValue :
13497 this.value !== undefined ? this.value : '';
13499 // prevent input submission
13500 this.el.dom.removeAttribute('name');
13501 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13506 // this.el.dom.setAttribute('autocomplete', 'off');
13509 var cls = 'x-combo-list';
13511 //this.list = new Roo.Layer({
13512 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13518 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13519 _this.list.setWidth(lw);
13522 this.list.on('mouseover', this.onViewOver, this);
13523 this.list.on('mousemove', this.onViewMove, this);
13524 this.list.on('scroll', this.onViewScroll, this);
13527 this.list.swallowEvent('mousewheel');
13528 this.assetHeight = 0;
13531 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13532 this.assetHeight += this.header.getHeight();
13535 this.innerList = this.list.createChild({cls:cls+'-inner'});
13536 this.innerList.on('mouseover', this.onViewOver, this);
13537 this.innerList.on('mousemove', this.onViewMove, this);
13538 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13540 if(this.allowBlank && !this.pageSize && !this.disableClear){
13541 this.footer = this.list.createChild({cls:cls+'-ft'});
13542 this.pageTb = new Roo.Toolbar(this.footer);
13546 this.footer = this.list.createChild({cls:cls+'-ft'});
13547 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13548 {pageSize: this.pageSize});
13552 if (this.pageTb && this.allowBlank && !this.disableClear) {
13554 this.pageTb.add(new Roo.Toolbar.Fill(), {
13555 cls: 'x-btn-icon x-btn-clear',
13557 handler: function()
13560 _this.clearValue();
13561 _this.onSelect(false, -1);
13566 this.assetHeight += this.footer.getHeight();
13571 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13574 this.view = new Roo.View(this.list, this.tpl, {
13575 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13577 //this.view.wrapEl.setDisplayed(false);
13578 this.view.on('click', this.onViewClick, this);
13581 this.store.on('beforeload', this.onBeforeLoad, this);
13582 this.store.on('load', this.onLoad, this);
13583 this.store.on('loadexception', this.onLoadException, this);
13585 if(this.resizable){
13586 this.resizer = new Roo.Resizable(this.list, {
13587 pinned:true, handles:'se'
13589 this.resizer.on('resize', function(r, w, h){
13590 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13591 this.listWidth = w;
13592 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13593 this.restrictHeight();
13595 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13598 if(!this.editable){
13599 this.editable = true;
13600 this.setEditable(false);
13605 if (typeof(this.events.add.listeners) != 'undefined') {
13607 this.addicon = this.wrap.createChild(
13608 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13610 this.addicon.on('click', function(e) {
13611 this.fireEvent('add', this);
13614 if (typeof(this.events.edit.listeners) != 'undefined') {
13616 this.editicon = this.wrap.createChild(
13617 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13618 if (this.addicon) {
13619 this.editicon.setStyle('margin-left', '40px');
13621 this.editicon.on('click', function(e) {
13623 // we fire even if inothing is selected..
13624 this.fireEvent('edit', this, this.lastData );
13630 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13631 "up" : function(e){
13632 this.inKeyMode = true;
13636 "down" : function(e){
13637 if(!this.isExpanded()){
13638 this.onTriggerClick();
13640 this.inKeyMode = true;
13645 "enter" : function(e){
13646 // this.onViewClick();
13650 if(this.fireEvent("specialkey", this, e)){
13651 this.onViewClick(false);
13657 "esc" : function(e){
13661 "tab" : function(e){
13664 if(this.fireEvent("specialkey", this, e)){
13665 this.onViewClick(false);
13673 doRelay : function(foo, bar, hname){
13674 if(hname == 'down' || this.scope.isExpanded()){
13675 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13684 this.queryDelay = Math.max(this.queryDelay || 10,
13685 this.mode == 'local' ? 10 : 250);
13688 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13690 if(this.typeAhead){
13691 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13693 if(this.editable !== false){
13694 this.inputEl().on("keyup", this.onKeyUp, this);
13696 if(this.forceSelection){
13697 this.inputEl().on('blur', this.doForce, this);
13701 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13702 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13706 initTickableEvents: function()
13710 if(this.hiddenName){
13712 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13714 this.hiddenField.dom.value =
13715 this.hiddenValue !== undefined ? this.hiddenValue :
13716 this.value !== undefined ? this.value : '';
13718 // prevent input submission
13719 this.el.dom.removeAttribute('name');
13720 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13725 // this.list = this.el.select('ul.dropdown-menu',true).first();
13727 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13728 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13729 if(this.triggerList){
13730 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13733 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13734 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13736 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13737 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13739 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13740 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13742 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13743 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13744 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13747 this.cancelBtn.hide();
13752 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13753 _this.list.setWidth(lw);
13756 this.list.on('mouseover', this.onViewOver, this);
13757 this.list.on('mousemove', this.onViewMove, this);
13759 this.list.on('scroll', this.onViewScroll, this);
13762 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13763 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13766 this.view = new Roo.View(this.list, this.tpl, {
13771 selectedClass: this.selectedClass
13774 //this.view.wrapEl.setDisplayed(false);
13775 this.view.on('click', this.onViewClick, this);
13779 this.store.on('beforeload', this.onBeforeLoad, this);
13780 this.store.on('load', this.onLoad, this);
13781 this.store.on('loadexception', this.onLoadException, this);
13784 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13785 "up" : function(e){
13786 this.inKeyMode = true;
13790 "down" : function(e){
13791 this.inKeyMode = true;
13795 "enter" : function(e){
13796 if(this.fireEvent("specialkey", this, e)){
13797 this.onViewClick(false);
13803 "esc" : function(e){
13804 this.onTickableFooterButtonClick(e, false, false);
13807 "tab" : function(e){
13808 this.fireEvent("specialkey", this, e);
13810 this.onTickableFooterButtonClick(e, false, false);
13817 doRelay : function(e, fn, key){
13818 if(this.scope.isExpanded()){
13819 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13828 this.queryDelay = Math.max(this.queryDelay || 10,
13829 this.mode == 'local' ? 10 : 250);
13832 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13834 if(this.typeAhead){
13835 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13838 if(this.editable !== false){
13839 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13842 this.indicator = this.indicatorEl();
13844 if(this.indicator){
13845 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13846 this.indicator.hide();
13851 onDestroy : function(){
13853 this.view.setStore(null);
13854 this.view.el.removeAllListeners();
13855 this.view.el.remove();
13856 this.view.purgeListeners();
13859 this.list.dom.innerHTML = '';
13863 this.store.un('beforeload', this.onBeforeLoad, this);
13864 this.store.un('load', this.onLoad, this);
13865 this.store.un('loadexception', this.onLoadException, this);
13867 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13871 fireKey : function(e){
13872 if(e.isNavKeyPress() && !this.list.isVisible()){
13873 this.fireEvent("specialkey", this, e);
13878 onResize: function(w, h){
13879 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13881 // if(typeof w != 'number'){
13882 // // we do not handle it!?!?
13885 // var tw = this.trigger.getWidth();
13886 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13887 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13889 // this.inputEl().setWidth( this.adjustWidth('input', x));
13891 // //this.trigger.setStyle('left', x+'px');
13893 // if(this.list && this.listWidth === undefined){
13894 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13895 // this.list.setWidth(lw);
13896 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13904 * Allow or prevent the user from directly editing the field text. If false is passed,
13905 * the user will only be able to select from the items defined in the dropdown list. This method
13906 * is the runtime equivalent of setting the 'editable' config option at config time.
13907 * @param {Boolean} value True to allow the user to directly edit the field text
13909 setEditable : function(value){
13910 if(value == this.editable){
13913 this.editable = value;
13915 this.inputEl().dom.setAttribute('readOnly', true);
13916 this.inputEl().on('mousedown', this.onTriggerClick, this);
13917 this.inputEl().addClass('x-combo-noedit');
13919 this.inputEl().dom.setAttribute('readOnly', false);
13920 this.inputEl().un('mousedown', this.onTriggerClick, this);
13921 this.inputEl().removeClass('x-combo-noedit');
13927 onBeforeLoad : function(combo,opts){
13928 if(!this.hasFocus){
13932 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13934 this.restrictHeight();
13935 this.selectedIndex = -1;
13939 onLoad : function(){
13941 this.hasQuery = false;
13943 if(!this.hasFocus){
13947 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13948 this.loading.hide();
13951 if(this.store.getCount() > 0){
13954 this.restrictHeight();
13955 if(this.lastQuery == this.allQuery){
13956 if(this.editable && !this.tickable){
13957 this.inputEl().dom.select();
13961 !this.selectByValue(this.value, true) &&
13964 !this.store.lastOptions ||
13965 typeof(this.store.lastOptions.add) == 'undefined' ||
13966 this.store.lastOptions.add != true
13969 this.select(0, true);
13972 if(this.autoFocus){
13975 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13976 this.taTask.delay(this.typeAheadDelay);
13980 this.onEmptyResults();
13986 onLoadException : function()
13988 this.hasQuery = false;
13990 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13991 this.loading.hide();
13994 if(this.tickable && this.editable){
13999 // only causes errors at present
14000 //Roo.log(this.store.reader.jsonData);
14001 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14003 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14009 onTypeAhead : function(){
14010 if(this.store.getCount() > 0){
14011 var r = this.store.getAt(0);
14012 var newValue = r.data[this.displayField];
14013 var len = newValue.length;
14014 var selStart = this.getRawValue().length;
14016 if(selStart != len){
14017 this.setRawValue(newValue);
14018 this.selectText(selStart, newValue.length);
14024 onSelect : function(record, index){
14026 if(this.fireEvent('beforeselect', this, record, index) !== false){
14028 this.setFromData(index > -1 ? record.data : false);
14031 this.fireEvent('select', this, record, index);
14036 * Returns the currently selected field value or empty string if no value is set.
14037 * @return {String} value The selected value
14039 getValue : function()
14041 if(Roo.isIOS && this.useNativeIOS){
14042 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14046 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14049 if(this.valueField){
14050 return typeof this.value != 'undefined' ? this.value : '';
14052 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14056 getRawValue : function()
14058 if(Roo.isIOS && this.useNativeIOS){
14059 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14062 var v = this.inputEl().getValue();
14068 * Clears any text/value currently set in the field
14070 clearValue : function(){
14072 if(this.hiddenField){
14073 this.hiddenField.dom.value = '';
14076 this.setRawValue('');
14077 this.lastSelectionText = '';
14078 this.lastData = false;
14080 var close = this.closeTriggerEl();
14091 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14092 * will be displayed in the field. If the value does not match the data value of an existing item,
14093 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14094 * Otherwise the field will be blank (although the value will still be set).
14095 * @param {String} value The value to match
14097 setValue : function(v)
14099 if(Roo.isIOS && this.useNativeIOS){
14100 this.setIOSValue(v);
14110 if(this.valueField){
14111 var r = this.findRecord(this.valueField, v);
14113 text = r.data[this.displayField];
14114 }else if(this.valueNotFoundText !== undefined){
14115 text = this.valueNotFoundText;
14118 this.lastSelectionText = text;
14119 if(this.hiddenField){
14120 this.hiddenField.dom.value = v;
14122 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14125 var close = this.closeTriggerEl();
14128 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14134 * @property {Object} the last set data for the element
14139 * Sets the value of the field based on a object which is related to the record format for the store.
14140 * @param {Object} value the value to set as. or false on reset?
14142 setFromData : function(o){
14149 var dv = ''; // display value
14150 var vv = ''; // value value..
14152 if (this.displayField) {
14153 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14155 // this is an error condition!!!
14156 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14159 if(this.valueField){
14160 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14163 var close = this.closeTriggerEl();
14166 if(dv.length || vv * 1 > 0){
14168 this.blockFocus=true;
14174 if(this.hiddenField){
14175 this.hiddenField.dom.value = vv;
14177 this.lastSelectionText = dv;
14178 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14182 // no hidden field.. - we store the value in 'value', but still display
14183 // display field!!!!
14184 this.lastSelectionText = dv;
14185 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14192 reset : function(){
14193 // overridden so that last data is reset..
14200 this.setValue(this.originalValue);
14201 //this.clearInvalid();
14202 this.lastData = false;
14204 this.view.clearSelections();
14210 findRecord : function(prop, value){
14212 if(this.store.getCount() > 0){
14213 this.store.each(function(r){
14214 if(r.data[prop] == value){
14224 getName: function()
14226 // returns hidden if it's set..
14227 if (!this.rendered) {return ''};
14228 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14232 onViewMove : function(e, t){
14233 this.inKeyMode = false;
14237 onViewOver : function(e, t){
14238 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14241 var item = this.view.findItemFromChild(t);
14244 var index = this.view.indexOf(item);
14245 this.select(index, false);
14250 onViewClick : function(view, doFocus, el, e)
14252 var index = this.view.getSelectedIndexes()[0];
14254 var r = this.store.getAt(index);
14258 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14265 Roo.each(this.tickItems, function(v,k){
14267 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14269 _this.tickItems.splice(k, 1);
14271 if(typeof(e) == 'undefined' && view == false){
14272 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14284 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14285 this.tickItems.push(r.data);
14288 if(typeof(e) == 'undefined' && view == false){
14289 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14296 this.onSelect(r, index);
14298 if(doFocus !== false && !this.blockFocus){
14299 this.inputEl().focus();
14304 restrictHeight : function(){
14305 //this.innerList.dom.style.height = '';
14306 //var inner = this.innerList.dom;
14307 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14308 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14309 //this.list.beginUpdate();
14310 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14311 this.list.alignTo(this.inputEl(), this.listAlign);
14312 this.list.alignTo(this.inputEl(), this.listAlign);
14313 //this.list.endUpdate();
14317 onEmptyResults : function(){
14319 if(this.tickable && this.editable){
14320 this.hasFocus = false;
14321 this.restrictHeight();
14329 * Returns true if the dropdown list is expanded, else false.
14331 isExpanded : function(){
14332 return this.list.isVisible();
14336 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14337 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14338 * @param {String} value The data value of the item to select
14339 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14340 * selected item if it is not currently in view (defaults to true)
14341 * @return {Boolean} True if the value matched an item in the list, else false
14343 selectByValue : function(v, scrollIntoView){
14344 if(v !== undefined && v !== null){
14345 var r = this.findRecord(this.valueField || this.displayField, v);
14347 this.select(this.store.indexOf(r), scrollIntoView);
14355 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14356 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14357 * @param {Number} index The zero-based index of the list item to select
14358 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14359 * selected item if it is not currently in view (defaults to true)
14361 select : function(index, scrollIntoView){
14362 this.selectedIndex = index;
14363 this.view.select(index);
14364 if(scrollIntoView !== false){
14365 var el = this.view.getNode(index);
14367 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14370 this.list.scrollChildIntoView(el, false);
14376 selectNext : function(){
14377 var ct = this.store.getCount();
14379 if(this.selectedIndex == -1){
14381 }else if(this.selectedIndex < ct-1){
14382 this.select(this.selectedIndex+1);
14388 selectPrev : function(){
14389 var ct = this.store.getCount();
14391 if(this.selectedIndex == -1){
14393 }else if(this.selectedIndex != 0){
14394 this.select(this.selectedIndex-1);
14400 onKeyUp : function(e){
14401 if(this.editable !== false && !e.isSpecialKey()){
14402 this.lastKey = e.getKey();
14403 this.dqTask.delay(this.queryDelay);
14408 validateBlur : function(){
14409 return !this.list || !this.list.isVisible();
14413 initQuery : function(){
14415 var v = this.getRawValue();
14417 if(this.tickable && this.editable){
14418 v = this.tickableInputEl().getValue();
14425 doForce : function(){
14426 if(this.inputEl().dom.value.length > 0){
14427 this.inputEl().dom.value =
14428 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14434 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14435 * query allowing the query action to be canceled if needed.
14436 * @param {String} query The SQL query to execute
14437 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14438 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14439 * saved in the current store (defaults to false)
14441 doQuery : function(q, forceAll){
14443 if(q === undefined || q === null){
14448 forceAll: forceAll,
14452 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14457 forceAll = qe.forceAll;
14458 if(forceAll === true || (q.length >= this.minChars)){
14460 this.hasQuery = true;
14462 if(this.lastQuery != q || this.alwaysQuery){
14463 this.lastQuery = q;
14464 if(this.mode == 'local'){
14465 this.selectedIndex = -1;
14467 this.store.clearFilter();
14470 if(this.specialFilter){
14471 this.fireEvent('specialfilter', this);
14476 this.store.filter(this.displayField, q);
14479 this.store.fireEvent("datachanged", this.store);
14486 this.store.baseParams[this.queryParam] = q;
14488 var options = {params : this.getParams(q)};
14491 options.add = true;
14492 options.params.start = this.page * this.pageSize;
14495 this.store.load(options);
14498 * this code will make the page width larger, at the beginning, the list not align correctly,
14499 * we should expand the list on onLoad
14500 * so command out it
14505 this.selectedIndex = -1;
14510 this.loadNext = false;
14514 getParams : function(q){
14516 //p[this.queryParam] = q;
14520 p.limit = this.pageSize;
14526 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14528 collapse : function(){
14529 if(!this.isExpanded()){
14535 this.hasFocus = false;
14539 this.cancelBtn.hide();
14540 this.trigger.show();
14543 this.tickableInputEl().dom.value = '';
14544 this.tickableInputEl().blur();
14549 Roo.get(document).un('mousedown', this.collapseIf, this);
14550 Roo.get(document).un('mousewheel', this.collapseIf, this);
14551 if (!this.editable) {
14552 Roo.get(document).un('keydown', this.listKeyPress, this);
14554 this.fireEvent('collapse', this);
14560 collapseIf : function(e){
14561 var in_combo = e.within(this.el);
14562 var in_list = e.within(this.list);
14563 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14565 if (in_combo || in_list || is_list) {
14566 //e.stopPropagation();
14571 this.onTickableFooterButtonClick(e, false, false);
14579 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14581 expand : function(){
14583 if(this.isExpanded() || !this.hasFocus){
14587 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14588 this.list.setWidth(lw);
14594 this.restrictHeight();
14598 this.tickItems = Roo.apply([], this.item);
14601 this.cancelBtn.show();
14602 this.trigger.hide();
14605 this.tickableInputEl().focus();
14610 Roo.get(document).on('mousedown', this.collapseIf, this);
14611 Roo.get(document).on('mousewheel', this.collapseIf, this);
14612 if (!this.editable) {
14613 Roo.get(document).on('keydown', this.listKeyPress, this);
14616 this.fireEvent('expand', this);
14620 // Implements the default empty TriggerField.onTriggerClick function
14621 onTriggerClick : function(e)
14623 Roo.log('trigger click');
14625 if(this.disabled || !this.triggerList){
14630 this.loadNext = false;
14632 if(this.isExpanded()){
14634 if (!this.blockFocus) {
14635 this.inputEl().focus();
14639 this.hasFocus = true;
14640 if(this.triggerAction == 'all') {
14641 this.doQuery(this.allQuery, true);
14643 this.doQuery(this.getRawValue());
14645 if (!this.blockFocus) {
14646 this.inputEl().focus();
14651 onTickableTriggerClick : function(e)
14658 this.loadNext = false;
14659 this.hasFocus = true;
14661 if(this.triggerAction == 'all') {
14662 this.doQuery(this.allQuery, true);
14664 this.doQuery(this.getRawValue());
14668 onSearchFieldClick : function(e)
14670 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14671 this.onTickableFooterButtonClick(e, false, false);
14675 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14680 this.loadNext = false;
14681 this.hasFocus = true;
14683 if(this.triggerAction == 'all') {
14684 this.doQuery(this.allQuery, true);
14686 this.doQuery(this.getRawValue());
14690 listKeyPress : function(e)
14692 //Roo.log('listkeypress');
14693 // scroll to first matching element based on key pres..
14694 if (e.isSpecialKey()) {
14697 var k = String.fromCharCode(e.getKey()).toUpperCase();
14700 var csel = this.view.getSelectedNodes();
14701 var cselitem = false;
14703 var ix = this.view.indexOf(csel[0]);
14704 cselitem = this.store.getAt(ix);
14705 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14711 this.store.each(function(v) {
14713 // start at existing selection.
14714 if (cselitem.id == v.id) {
14720 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14721 match = this.store.indexOf(v);
14727 if (match === false) {
14728 return true; // no more action?
14731 this.view.select(match);
14732 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14733 sn.scrollIntoView(sn.dom.parentNode, false);
14736 onViewScroll : function(e, t){
14738 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){
14742 this.hasQuery = true;
14744 this.loading = this.list.select('.loading', true).first();
14746 if(this.loading === null){
14747 this.list.createChild({
14749 cls: 'loading roo-select2-more-results roo-select2-active',
14750 html: 'Loading more results...'
14753 this.loading = this.list.select('.loading', true).first();
14755 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14757 this.loading.hide();
14760 this.loading.show();
14765 this.loadNext = true;
14767 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14772 addItem : function(o)
14774 var dv = ''; // display value
14776 if (this.displayField) {
14777 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14779 // this is an error condition!!!
14780 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14787 var choice = this.choices.createChild({
14789 cls: 'roo-select2-search-choice',
14798 cls: 'roo-select2-search-choice-close fa fa-times',
14803 }, this.searchField);
14805 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14807 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14815 this.inputEl().dom.value = '';
14820 onRemoveItem : function(e, _self, o)
14822 e.preventDefault();
14824 this.lastItem = Roo.apply([], this.item);
14826 var index = this.item.indexOf(o.data) * 1;
14829 Roo.log('not this item?!');
14833 this.item.splice(index, 1);
14838 this.fireEvent('remove', this, e);
14844 syncValue : function()
14846 if(!this.item.length){
14853 Roo.each(this.item, function(i){
14854 if(_this.valueField){
14855 value.push(i[_this.valueField]);
14862 this.value = value.join(',');
14864 if(this.hiddenField){
14865 this.hiddenField.dom.value = this.value;
14868 this.store.fireEvent("datachanged", this.store);
14873 clearItem : function()
14875 if(!this.multiple){
14881 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14889 if(this.tickable && !Roo.isTouch){
14890 this.view.refresh();
14894 inputEl: function ()
14896 if(Roo.isIOS && this.useNativeIOS){
14897 return this.el.select('select.roo-ios-select', true).first();
14900 if(Roo.isTouch && this.mobileTouchView){
14901 return this.el.select('input.form-control',true).first();
14905 return this.searchField;
14908 return this.el.select('input.form-control',true).first();
14911 onTickableFooterButtonClick : function(e, btn, el)
14913 e.preventDefault();
14915 this.lastItem = Roo.apply([], this.item);
14917 if(btn && btn.name == 'cancel'){
14918 this.tickItems = Roo.apply([], this.item);
14927 Roo.each(this.tickItems, function(o){
14935 validate : function()
14937 if(this.getVisibilityEl().hasClass('hidden')){
14941 var v = this.getRawValue();
14944 v = this.getValue();
14947 if(this.disabled || this.allowBlank || v.length){
14952 this.markInvalid();
14956 tickableInputEl : function()
14958 if(!this.tickable || !this.editable){
14959 return this.inputEl();
14962 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14966 getAutoCreateTouchView : function()
14971 cls: 'form-group' //input-group
14977 type : this.inputType,
14978 cls : 'form-control x-combo-noedit',
14979 autocomplete: 'new-password',
14980 placeholder : this.placeholder || '',
14985 input.name = this.name;
14989 input.cls += ' input-' + this.size;
14992 if (this.disabled) {
14993 input.disabled = true;
15004 inputblock.cls += ' input-group';
15006 inputblock.cn.unshift({
15008 cls : 'input-group-addon',
15013 if(this.removable && !this.multiple){
15014 inputblock.cls += ' roo-removable';
15016 inputblock.cn.push({
15019 cls : 'roo-combo-removable-btn close'
15023 if(this.hasFeedback && !this.allowBlank){
15025 inputblock.cls += ' has-feedback';
15027 inputblock.cn.push({
15029 cls: 'glyphicon form-control-feedback'
15036 inputblock.cls += (this.before) ? '' : ' input-group';
15038 inputblock.cn.push({
15040 cls : 'input-group-addon',
15051 cls: 'form-hidden-field'
15065 cls: 'form-hidden-field'
15069 cls: 'roo-select2-choices',
15073 cls: 'roo-select2-search-field',
15086 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15092 if(!this.multiple && this.showToggleBtn){
15099 if (this.caret != false) {
15102 cls: 'fa fa-' + this.caret
15109 cls : 'input-group-addon btn dropdown-toggle',
15114 cls: 'combobox-clear',
15128 combobox.cls += ' roo-select2-container-multi';
15131 var align = this.labelAlign || this.parentLabelAlign();
15133 if (align ==='left' && this.fieldLabel.length) {
15138 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15139 tooltip : 'This field is required'
15143 cls : 'control-label',
15144 html : this.fieldLabel
15155 var labelCfg = cfg.cn[1];
15156 var contentCfg = cfg.cn[2];
15159 if(this.indicatorpos == 'right'){
15164 cls : 'control-label',
15168 html : this.fieldLabel
15172 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15173 tooltip : 'This field is required'
15186 labelCfg = cfg.cn[0];
15187 contentCfg = cfg.cn[1];
15192 if(this.labelWidth > 12){
15193 labelCfg.style = "width: " + this.labelWidth + 'px';
15196 if(this.labelWidth < 13 && this.labelmd == 0){
15197 this.labelmd = this.labelWidth;
15200 if(this.labellg > 0){
15201 labelCfg.cls += ' col-lg-' + this.labellg;
15202 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15205 if(this.labelmd > 0){
15206 labelCfg.cls += ' col-md-' + this.labelmd;
15207 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15210 if(this.labelsm > 0){
15211 labelCfg.cls += ' col-sm-' + this.labelsm;
15212 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15215 if(this.labelxs > 0){
15216 labelCfg.cls += ' col-xs-' + this.labelxs;
15217 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15221 } else if ( this.fieldLabel.length) {
15225 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15226 tooltip : 'This field is required'
15230 cls : 'control-label',
15231 html : this.fieldLabel
15242 if(this.indicatorpos == 'right'){
15246 cls : 'control-label',
15247 html : this.fieldLabel,
15251 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15252 tooltip : 'This field is required'
15269 var settings = this;
15271 ['xs','sm','md','lg'].map(function(size){
15272 if (settings[size]) {
15273 cfg.cls += ' col-' + size + '-' + settings[size];
15280 initTouchView : function()
15282 this.renderTouchView();
15284 this.touchViewEl.on('scroll', function(){
15285 this.el.dom.scrollTop = 0;
15288 this.originalValue = this.getValue();
15290 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15292 this.inputEl().on("click", this.showTouchView, this);
15293 if (this.triggerEl) {
15294 this.triggerEl.on("click", this.showTouchView, this);
15298 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15299 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15301 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15303 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15304 this.store.on('load', this.onTouchViewLoad, this);
15305 this.store.on('loadexception', this.onTouchViewLoadException, this);
15307 if(this.hiddenName){
15309 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15311 this.hiddenField.dom.value =
15312 this.hiddenValue !== undefined ? this.hiddenValue :
15313 this.value !== undefined ? this.value : '';
15315 this.el.dom.removeAttribute('name');
15316 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15320 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15321 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15324 if(this.removable && !this.multiple){
15325 var close = this.closeTriggerEl();
15327 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15328 close.on('click', this.removeBtnClick, this, close);
15332 * fix the bug in Safari iOS8
15334 this.inputEl().on("focus", function(e){
15335 document.activeElement.blur();
15338 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15345 renderTouchView : function()
15347 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15348 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15350 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15351 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15353 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15354 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15355 this.touchViewBodyEl.setStyle('overflow', 'auto');
15357 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15358 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15360 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15361 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15365 showTouchView : function()
15371 this.touchViewHeaderEl.hide();
15373 if(this.modalTitle.length){
15374 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15375 this.touchViewHeaderEl.show();
15378 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15379 this.touchViewEl.show();
15381 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15383 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15384 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15386 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15388 if(this.modalTitle.length){
15389 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15392 this.touchViewBodyEl.setHeight(bodyHeight);
15396 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15398 this.touchViewEl.addClass('in');
15401 if(this._touchViewMask){
15402 Roo.get(document.body).addClass("x-body-masked");
15403 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15404 this._touchViewMask.setStyle('z-index', 10000);
15405 this._touchViewMask.addClass('show');
15408 this.doTouchViewQuery();
15412 hideTouchView : function()
15414 this.touchViewEl.removeClass('in');
15418 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15420 this.touchViewEl.setStyle('display', 'none');
15423 if(this._touchViewMask){
15424 this._touchViewMask.removeClass('show');
15425 Roo.get(document.body).removeClass("x-body-masked");
15429 setTouchViewValue : function()
15436 Roo.each(this.tickItems, function(o){
15441 this.hideTouchView();
15444 doTouchViewQuery : function()
15453 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15457 if(!this.alwaysQuery || this.mode == 'local'){
15458 this.onTouchViewLoad();
15465 onTouchViewBeforeLoad : function(combo,opts)
15471 onTouchViewLoad : function()
15473 if(this.store.getCount() < 1){
15474 this.onTouchViewEmptyResults();
15478 this.clearTouchView();
15480 var rawValue = this.getRawValue();
15482 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15484 this.tickItems = [];
15486 this.store.data.each(function(d, rowIndex){
15487 var row = this.touchViewListGroup.createChild(template);
15489 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15490 row.addClass(d.data.cls);
15493 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15496 html : d.data[this.displayField]
15499 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15500 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15503 row.removeClass('selected');
15504 if(!this.multiple && this.valueField &&
15505 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15508 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15509 row.addClass('selected');
15512 if(this.multiple && this.valueField &&
15513 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15517 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15518 this.tickItems.push(d.data);
15521 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15525 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15527 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15529 if(this.modalTitle.length){
15530 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15533 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15535 if(this.mobile_restrict_height && listHeight < bodyHeight){
15536 this.touchViewBodyEl.setHeight(listHeight);
15541 if(firstChecked && listHeight > bodyHeight){
15542 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15547 onTouchViewLoadException : function()
15549 this.hideTouchView();
15552 onTouchViewEmptyResults : function()
15554 this.clearTouchView();
15556 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15558 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15562 clearTouchView : function()
15564 this.touchViewListGroup.dom.innerHTML = '';
15567 onTouchViewClick : function(e, el, o)
15569 e.preventDefault();
15572 var rowIndex = o.rowIndex;
15574 var r = this.store.getAt(rowIndex);
15576 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15578 if(!this.multiple){
15579 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15580 c.dom.removeAttribute('checked');
15583 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15585 this.setFromData(r.data);
15587 var close = this.closeTriggerEl();
15593 this.hideTouchView();
15595 this.fireEvent('select', this, r, rowIndex);
15600 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15601 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15602 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15606 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15607 this.addItem(r.data);
15608 this.tickItems.push(r.data);
15612 getAutoCreateNativeIOS : function()
15615 cls: 'form-group' //input-group,
15620 cls : 'roo-ios-select'
15624 combobox.name = this.name;
15627 if (this.disabled) {
15628 combobox.disabled = true;
15631 var settings = this;
15633 ['xs','sm','md','lg'].map(function(size){
15634 if (settings[size]) {
15635 cfg.cls += ' col-' + size + '-' + settings[size];
15645 initIOSView : function()
15647 this.store.on('load', this.onIOSViewLoad, this);
15652 onIOSViewLoad : function()
15654 if(this.store.getCount() < 1){
15658 this.clearIOSView();
15660 if(this.allowBlank) {
15662 var default_text = '-- SELECT --';
15664 if(this.placeholder.length){
15665 default_text = this.placeholder;
15668 if(this.emptyTitle.length){
15669 default_text += ' - ' + this.emptyTitle + ' -';
15672 var opt = this.inputEl().createChild({
15675 html : default_text
15679 o[this.valueField] = 0;
15680 o[this.displayField] = default_text;
15682 this.ios_options.push({
15689 this.store.data.each(function(d, rowIndex){
15693 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15694 html = d.data[this.displayField];
15699 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15700 value = d.data[this.valueField];
15709 if(this.value == d.data[this.valueField]){
15710 option['selected'] = true;
15713 var opt = this.inputEl().createChild(option);
15715 this.ios_options.push({
15722 this.inputEl().on('change', function(){
15723 this.fireEvent('select', this);
15728 clearIOSView: function()
15730 this.inputEl().dom.innerHTML = '';
15732 this.ios_options = [];
15735 setIOSValue: function(v)
15739 if(!this.ios_options){
15743 Roo.each(this.ios_options, function(opts){
15745 opts.el.dom.removeAttribute('selected');
15747 if(opts.data[this.valueField] != v){
15751 opts.el.dom.setAttribute('selected', true);
15757 * @cfg {Boolean} grow
15761 * @cfg {Number} growMin
15765 * @cfg {Number} growMax
15774 Roo.apply(Roo.bootstrap.ComboBox, {
15778 cls: 'modal-header',
15800 cls: 'list-group-item',
15804 cls: 'roo-combobox-list-group-item-value'
15808 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15822 listItemCheckbox : {
15824 cls: 'list-group-item',
15828 cls: 'roo-combobox-list-group-item-value'
15832 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15848 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15853 cls: 'modal-footer',
15861 cls: 'col-xs-6 text-left',
15864 cls: 'btn btn-danger roo-touch-view-cancel',
15870 cls: 'col-xs-6 text-right',
15873 cls: 'btn btn-success roo-touch-view-ok',
15884 Roo.apply(Roo.bootstrap.ComboBox, {
15886 touchViewTemplate : {
15888 cls: 'modal fade roo-combobox-touch-view',
15892 cls: 'modal-dialog',
15893 style : 'position:fixed', // we have to fix position....
15897 cls: 'modal-content',
15899 Roo.bootstrap.ComboBox.header,
15900 Roo.bootstrap.ComboBox.body,
15901 Roo.bootstrap.ComboBox.footer
15910 * Ext JS Library 1.1.1
15911 * Copyright(c) 2006-2007, Ext JS, LLC.
15913 * Originally Released Under LGPL - original licence link has changed is not relivant.
15916 * <script type="text/javascript">
15921 * @extends Roo.util.Observable
15922 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15923 * This class also supports single and multi selection modes. <br>
15924 * Create a data model bound view:
15926 var store = new Roo.data.Store(...);
15928 var view = new Roo.View({
15930 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15932 singleSelect: true,
15933 selectedClass: "ydataview-selected",
15937 // listen for node click?
15938 view.on("click", function(vw, index, node, e){
15939 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15943 dataModel.load("foobar.xml");
15945 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15947 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15948 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15950 * Note: old style constructor is still suported (container, template, config)
15953 * Create a new View
15954 * @param {Object} config The config object
15957 Roo.View = function(config, depreciated_tpl, depreciated_config){
15959 this.parent = false;
15961 if (typeof(depreciated_tpl) == 'undefined') {
15962 // new way.. - universal constructor.
15963 Roo.apply(this, config);
15964 this.el = Roo.get(this.el);
15967 this.el = Roo.get(config);
15968 this.tpl = depreciated_tpl;
15969 Roo.apply(this, depreciated_config);
15971 this.wrapEl = this.el.wrap().wrap();
15972 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15975 if(typeof(this.tpl) == "string"){
15976 this.tpl = new Roo.Template(this.tpl);
15978 // support xtype ctors..
15979 this.tpl = new Roo.factory(this.tpl, Roo);
15983 this.tpl.compile();
15988 * @event beforeclick
15989 * Fires before a click is processed. Returns false to cancel the default action.
15990 * @param {Roo.View} this
15991 * @param {Number} index The index of the target node
15992 * @param {HTMLElement} node The target node
15993 * @param {Roo.EventObject} e The raw event object
15995 "beforeclick" : true,
15998 * Fires when a template node is clicked.
15999 * @param {Roo.View} this
16000 * @param {Number} index The index of the target node
16001 * @param {HTMLElement} node The target node
16002 * @param {Roo.EventObject} e The raw event object
16007 * Fires when a template node is double clicked.
16008 * @param {Roo.View} this
16009 * @param {Number} index The index of the target node
16010 * @param {HTMLElement} node The target node
16011 * @param {Roo.EventObject} e The raw event object
16015 * @event contextmenu
16016 * Fires when a template node is right clicked.
16017 * @param {Roo.View} this
16018 * @param {Number} index The index of the target node
16019 * @param {HTMLElement} node The target node
16020 * @param {Roo.EventObject} e The raw event object
16022 "contextmenu" : true,
16024 * @event selectionchange
16025 * Fires when the selected nodes change.
16026 * @param {Roo.View} this
16027 * @param {Array} selections Array of the selected nodes
16029 "selectionchange" : true,
16032 * @event beforeselect
16033 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16034 * @param {Roo.View} this
16035 * @param {HTMLElement} node The node to be selected
16036 * @param {Array} selections Array of currently selected nodes
16038 "beforeselect" : true,
16040 * @event preparedata
16041 * Fires on every row to render, to allow you to change the data.
16042 * @param {Roo.View} this
16043 * @param {Object} data to be rendered (change this)
16045 "preparedata" : true
16053 "click": this.onClick,
16054 "dblclick": this.onDblClick,
16055 "contextmenu": this.onContextMenu,
16059 this.selections = [];
16061 this.cmp = new Roo.CompositeElementLite([]);
16063 this.store = Roo.factory(this.store, Roo.data);
16064 this.setStore(this.store, true);
16067 if ( this.footer && this.footer.xtype) {
16069 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16071 this.footer.dataSource = this.store;
16072 this.footer.container = fctr;
16073 this.footer = Roo.factory(this.footer, Roo);
16074 fctr.insertFirst(this.el);
16076 // this is a bit insane - as the paging toolbar seems to detach the el..
16077 // dom.parentNode.parentNode.parentNode
16078 // they get detached?
16082 Roo.View.superclass.constructor.call(this);
16087 Roo.extend(Roo.View, Roo.util.Observable, {
16090 * @cfg {Roo.data.Store} store Data store to load data from.
16095 * @cfg {String|Roo.Element} el The container element.
16100 * @cfg {String|Roo.Template} tpl The template used by this View
16104 * @cfg {String} dataName the named area of the template to use as the data area
16105 * Works with domtemplates roo-name="name"
16109 * @cfg {String} selectedClass The css class to add to selected nodes
16111 selectedClass : "x-view-selected",
16113 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16118 * @cfg {String} text to display on mask (default Loading)
16122 * @cfg {Boolean} multiSelect Allow multiple selection
16124 multiSelect : false,
16126 * @cfg {Boolean} singleSelect Allow single selection
16128 singleSelect: false,
16131 * @cfg {Boolean} toggleSelect - selecting
16133 toggleSelect : false,
16136 * @cfg {Boolean} tickable - selecting
16141 * Returns the element this view is bound to.
16142 * @return {Roo.Element}
16144 getEl : function(){
16145 return this.wrapEl;
16151 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16153 refresh : function(){
16154 //Roo.log('refresh');
16157 // if we are using something like 'domtemplate', then
16158 // the what gets used is:
16159 // t.applySubtemplate(NAME, data, wrapping data..)
16160 // the outer template then get' applied with
16161 // the store 'extra data'
16162 // and the body get's added to the
16163 // roo-name="data" node?
16164 // <span class='roo-tpl-{name}'></span> ?????
16168 this.clearSelections();
16169 this.el.update("");
16171 var records = this.store.getRange();
16172 if(records.length < 1) {
16174 // is this valid?? = should it render a template??
16176 this.el.update(this.emptyText);
16180 if (this.dataName) {
16181 this.el.update(t.apply(this.store.meta)); //????
16182 el = this.el.child('.roo-tpl-' + this.dataName);
16185 for(var i = 0, len = records.length; i < len; i++){
16186 var data = this.prepareData(records[i].data, i, records[i]);
16187 this.fireEvent("preparedata", this, data, i, records[i]);
16189 var d = Roo.apply({}, data);
16192 Roo.apply(d, {'roo-id' : Roo.id()});
16196 Roo.each(this.parent.item, function(item){
16197 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16200 Roo.apply(d, {'roo-data-checked' : 'checked'});
16204 html[html.length] = Roo.util.Format.trim(
16206 t.applySubtemplate(this.dataName, d, this.store.meta) :
16213 el.update(html.join(""));
16214 this.nodes = el.dom.childNodes;
16215 this.updateIndexes(0);
16220 * Function to override to reformat the data that is sent to
16221 * the template for each node.
16222 * DEPRICATED - use the preparedata event handler.
16223 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16224 * a JSON object for an UpdateManager bound view).
16226 prepareData : function(data, index, record)
16228 this.fireEvent("preparedata", this, data, index, record);
16232 onUpdate : function(ds, record){
16233 // Roo.log('on update');
16234 this.clearSelections();
16235 var index = this.store.indexOf(record);
16236 var n = this.nodes[index];
16237 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16238 n.parentNode.removeChild(n);
16239 this.updateIndexes(index, index);
16245 onAdd : function(ds, records, index)
16247 //Roo.log(['on Add', ds, records, index] );
16248 this.clearSelections();
16249 if(this.nodes.length == 0){
16253 var n = this.nodes[index];
16254 for(var i = 0, len = records.length; i < len; i++){
16255 var d = this.prepareData(records[i].data, i, records[i]);
16257 this.tpl.insertBefore(n, d);
16260 this.tpl.append(this.el, d);
16263 this.updateIndexes(index);
16266 onRemove : function(ds, record, index){
16267 // Roo.log('onRemove');
16268 this.clearSelections();
16269 var el = this.dataName ?
16270 this.el.child('.roo-tpl-' + this.dataName) :
16273 el.dom.removeChild(this.nodes[index]);
16274 this.updateIndexes(index);
16278 * Refresh an individual node.
16279 * @param {Number} index
16281 refreshNode : function(index){
16282 this.onUpdate(this.store, this.store.getAt(index));
16285 updateIndexes : function(startIndex, endIndex){
16286 var ns = this.nodes;
16287 startIndex = startIndex || 0;
16288 endIndex = endIndex || ns.length - 1;
16289 for(var i = startIndex; i <= endIndex; i++){
16290 ns[i].nodeIndex = i;
16295 * Changes the data store this view uses and refresh the view.
16296 * @param {Store} store
16298 setStore : function(store, initial){
16299 if(!initial && this.store){
16300 this.store.un("datachanged", this.refresh);
16301 this.store.un("add", this.onAdd);
16302 this.store.un("remove", this.onRemove);
16303 this.store.un("update", this.onUpdate);
16304 this.store.un("clear", this.refresh);
16305 this.store.un("beforeload", this.onBeforeLoad);
16306 this.store.un("load", this.onLoad);
16307 this.store.un("loadexception", this.onLoad);
16311 store.on("datachanged", this.refresh, this);
16312 store.on("add", this.onAdd, this);
16313 store.on("remove", this.onRemove, this);
16314 store.on("update", this.onUpdate, this);
16315 store.on("clear", this.refresh, this);
16316 store.on("beforeload", this.onBeforeLoad, this);
16317 store.on("load", this.onLoad, this);
16318 store.on("loadexception", this.onLoad, this);
16326 * onbeforeLoad - masks the loading area.
16329 onBeforeLoad : function(store,opts)
16331 //Roo.log('onBeforeLoad');
16333 this.el.update("");
16335 this.el.mask(this.mask ? this.mask : "Loading" );
16337 onLoad : function ()
16344 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16345 * @param {HTMLElement} node
16346 * @return {HTMLElement} The template node
16348 findItemFromChild : function(node){
16349 var el = this.dataName ?
16350 this.el.child('.roo-tpl-' + this.dataName,true) :
16353 if(!node || node.parentNode == el){
16356 var p = node.parentNode;
16357 while(p && p != el){
16358 if(p.parentNode == el){
16367 onClick : function(e){
16368 var item = this.findItemFromChild(e.getTarget());
16370 var index = this.indexOf(item);
16371 if(this.onItemClick(item, index, e) !== false){
16372 this.fireEvent("click", this, index, item, e);
16375 this.clearSelections();
16380 onContextMenu : function(e){
16381 var item = this.findItemFromChild(e.getTarget());
16383 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16388 onDblClick : function(e){
16389 var item = this.findItemFromChild(e.getTarget());
16391 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16395 onItemClick : function(item, index, e)
16397 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16400 if (this.toggleSelect) {
16401 var m = this.isSelected(item) ? 'unselect' : 'select';
16404 _t[m](item, true, false);
16407 if(this.multiSelect || this.singleSelect){
16408 if(this.multiSelect && e.shiftKey && this.lastSelection){
16409 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16411 this.select(item, this.multiSelect && e.ctrlKey);
16412 this.lastSelection = item;
16415 if(!this.tickable){
16416 e.preventDefault();
16424 * Get the number of selected nodes.
16427 getSelectionCount : function(){
16428 return this.selections.length;
16432 * Get the currently selected nodes.
16433 * @return {Array} An array of HTMLElements
16435 getSelectedNodes : function(){
16436 return this.selections;
16440 * Get the indexes of the selected nodes.
16443 getSelectedIndexes : function(){
16444 var indexes = [], s = this.selections;
16445 for(var i = 0, len = s.length; i < len; i++){
16446 indexes.push(s[i].nodeIndex);
16452 * Clear all selections
16453 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16455 clearSelections : function(suppressEvent){
16456 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16457 this.cmp.elements = this.selections;
16458 this.cmp.removeClass(this.selectedClass);
16459 this.selections = [];
16460 if(!suppressEvent){
16461 this.fireEvent("selectionchange", this, this.selections);
16467 * Returns true if the passed node is selected
16468 * @param {HTMLElement/Number} node The node or node index
16469 * @return {Boolean}
16471 isSelected : function(node){
16472 var s = this.selections;
16476 node = this.getNode(node);
16477 return s.indexOf(node) !== -1;
16482 * @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
16483 * @param {Boolean} keepExisting (optional) true to keep existing selections
16484 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16486 select : function(nodeInfo, keepExisting, suppressEvent){
16487 if(nodeInfo instanceof Array){
16489 this.clearSelections(true);
16491 for(var i = 0, len = nodeInfo.length; i < len; i++){
16492 this.select(nodeInfo[i], true, true);
16496 var node = this.getNode(nodeInfo);
16497 if(!node || this.isSelected(node)){
16498 return; // already selected.
16501 this.clearSelections(true);
16504 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16505 Roo.fly(node).addClass(this.selectedClass);
16506 this.selections.push(node);
16507 if(!suppressEvent){
16508 this.fireEvent("selectionchange", this, this.selections);
16516 * @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
16517 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16518 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16520 unselect : function(nodeInfo, keepExisting, suppressEvent)
16522 if(nodeInfo instanceof Array){
16523 Roo.each(this.selections, function(s) {
16524 this.unselect(s, nodeInfo);
16528 var node = this.getNode(nodeInfo);
16529 if(!node || !this.isSelected(node)){
16530 //Roo.log("not selected");
16531 return; // not selected.
16535 Roo.each(this.selections, function(s) {
16537 Roo.fly(node).removeClass(this.selectedClass);
16544 this.selections= ns;
16545 this.fireEvent("selectionchange", this, this.selections);
16549 * Gets a template node.
16550 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16551 * @return {HTMLElement} The node or null if it wasn't found
16553 getNode : function(nodeInfo){
16554 if(typeof nodeInfo == "string"){
16555 return document.getElementById(nodeInfo);
16556 }else if(typeof nodeInfo == "number"){
16557 return this.nodes[nodeInfo];
16563 * Gets a range template nodes.
16564 * @param {Number} startIndex
16565 * @param {Number} endIndex
16566 * @return {Array} An array of nodes
16568 getNodes : function(start, end){
16569 var ns = this.nodes;
16570 start = start || 0;
16571 end = typeof end == "undefined" ? ns.length - 1 : end;
16574 for(var i = start; i <= end; i++){
16578 for(var i = start; i >= end; i--){
16586 * Finds the index of the passed node
16587 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16588 * @return {Number} The index of the node or -1
16590 indexOf : function(node){
16591 node = this.getNode(node);
16592 if(typeof node.nodeIndex == "number"){
16593 return node.nodeIndex;
16595 var ns = this.nodes;
16596 for(var i = 0, len = ns.length; i < len; i++){
16607 * based on jquery fullcalendar
16611 Roo.bootstrap = Roo.bootstrap || {};
16613 * @class Roo.bootstrap.Calendar
16614 * @extends Roo.bootstrap.Component
16615 * Bootstrap Calendar class
16616 * @cfg {Boolean} loadMask (true|false) default false
16617 * @cfg {Object} header generate the user specific header of the calendar, default false
16620 * Create a new Container
16621 * @param {Object} config The config object
16626 Roo.bootstrap.Calendar = function(config){
16627 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16631 * Fires when a date is selected
16632 * @param {DatePicker} this
16633 * @param {Date} date The selected date
16637 * @event monthchange
16638 * Fires when the displayed month changes
16639 * @param {DatePicker} this
16640 * @param {Date} date The selected month
16642 'monthchange': true,
16644 * @event evententer
16645 * Fires when mouse over an event
16646 * @param {Calendar} this
16647 * @param {event} Event
16649 'evententer': true,
16651 * @event eventleave
16652 * Fires when the mouse leaves an
16653 * @param {Calendar} this
16656 'eventleave': true,
16658 * @event eventclick
16659 * Fires when the mouse click an
16660 * @param {Calendar} this
16669 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16672 * @cfg {Number} startDay
16673 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16681 getAutoCreate : function(){
16684 var fc_button = function(name, corner, style, content ) {
16685 return Roo.apply({},{
16687 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16689 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16692 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16703 style : 'width:100%',
16710 cls : 'fc-header-left',
16712 fc_button('prev', 'left', 'arrow', '‹' ),
16713 fc_button('next', 'right', 'arrow', '›' ),
16714 { tag: 'span', cls: 'fc-header-space' },
16715 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16723 cls : 'fc-header-center',
16727 cls: 'fc-header-title',
16730 html : 'month / year'
16738 cls : 'fc-header-right',
16740 /* fc_button('month', 'left', '', 'month' ),
16741 fc_button('week', '', '', 'week' ),
16742 fc_button('day', 'right', '', 'day' )
16754 header = this.header;
16757 var cal_heads = function() {
16759 // fixme - handle this.
16761 for (var i =0; i < Date.dayNames.length; i++) {
16762 var d = Date.dayNames[i];
16765 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16766 html : d.substring(0,3)
16770 ret[0].cls += ' fc-first';
16771 ret[6].cls += ' fc-last';
16774 var cal_cell = function(n) {
16777 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16782 cls: 'fc-day-number',
16786 cls: 'fc-day-content',
16790 style: 'position: relative;' // height: 17px;
16802 var cal_rows = function() {
16805 for (var r = 0; r < 6; r++) {
16812 for (var i =0; i < Date.dayNames.length; i++) {
16813 var d = Date.dayNames[i];
16814 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16817 row.cn[0].cls+=' fc-first';
16818 row.cn[0].cn[0].style = 'min-height:90px';
16819 row.cn[6].cls+=' fc-last';
16823 ret[0].cls += ' fc-first';
16824 ret[4].cls += ' fc-prev-last';
16825 ret[5].cls += ' fc-last';
16832 cls: 'fc-border-separate',
16833 style : 'width:100%',
16841 cls : 'fc-first fc-last',
16859 cls : 'fc-content',
16860 style : "position: relative;",
16863 cls : 'fc-view fc-view-month fc-grid',
16864 style : 'position: relative',
16865 unselectable : 'on',
16868 cls : 'fc-event-container',
16869 style : 'position:absolute;z-index:8;top:0;left:0;'
16887 initEvents : function()
16890 throw "can not find store for calendar";
16896 style: "text-align:center",
16900 style: "background-color:white;width:50%;margin:250 auto",
16904 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16915 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16917 var size = this.el.select('.fc-content', true).first().getSize();
16918 this.maskEl.setSize(size.width, size.height);
16919 this.maskEl.enableDisplayMode("block");
16920 if(!this.loadMask){
16921 this.maskEl.hide();
16924 this.store = Roo.factory(this.store, Roo.data);
16925 this.store.on('load', this.onLoad, this);
16926 this.store.on('beforeload', this.onBeforeLoad, this);
16930 this.cells = this.el.select('.fc-day',true);
16931 //Roo.log(this.cells);
16932 this.textNodes = this.el.query('.fc-day-number');
16933 this.cells.addClassOnOver('fc-state-hover');
16935 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16936 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16937 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16938 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16940 this.on('monthchange', this.onMonthChange, this);
16942 this.update(new Date().clearTime());
16945 resize : function() {
16946 var sz = this.el.getSize();
16948 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16949 this.el.select('.fc-day-content div',true).setHeight(34);
16954 showPrevMonth : function(e){
16955 this.update(this.activeDate.add("mo", -1));
16957 showToday : function(e){
16958 this.update(new Date().clearTime());
16961 showNextMonth : function(e){
16962 this.update(this.activeDate.add("mo", 1));
16966 showPrevYear : function(){
16967 this.update(this.activeDate.add("y", -1));
16971 showNextYear : function(){
16972 this.update(this.activeDate.add("y", 1));
16977 update : function(date)
16979 var vd = this.activeDate;
16980 this.activeDate = date;
16981 // if(vd && this.el){
16982 // var t = date.getTime();
16983 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16984 // Roo.log('using add remove');
16986 // this.fireEvent('monthchange', this, date);
16988 // this.cells.removeClass("fc-state-highlight");
16989 // this.cells.each(function(c){
16990 // if(c.dateValue == t){
16991 // c.addClass("fc-state-highlight");
16992 // setTimeout(function(){
16993 // try{c.dom.firstChild.focus();}catch(e){}
17003 var days = date.getDaysInMonth();
17005 var firstOfMonth = date.getFirstDateOfMonth();
17006 var startingPos = firstOfMonth.getDay()-this.startDay;
17008 if(startingPos < this.startDay){
17012 var pm = date.add(Date.MONTH, -1);
17013 var prevStart = pm.getDaysInMonth()-startingPos;
17015 this.cells = this.el.select('.fc-day',true);
17016 this.textNodes = this.el.query('.fc-day-number');
17017 this.cells.addClassOnOver('fc-state-hover');
17019 var cells = this.cells.elements;
17020 var textEls = this.textNodes;
17022 Roo.each(cells, function(cell){
17023 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17026 days += startingPos;
17028 // convert everything to numbers so it's fast
17029 var day = 86400000;
17030 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17033 //Roo.log(prevStart);
17035 var today = new Date().clearTime().getTime();
17036 var sel = date.clearTime().getTime();
17037 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17038 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17039 var ddMatch = this.disabledDatesRE;
17040 var ddText = this.disabledDatesText;
17041 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17042 var ddaysText = this.disabledDaysText;
17043 var format = this.format;
17045 var setCellClass = function(cal, cell){
17049 //Roo.log('set Cell Class');
17051 var t = d.getTime();
17055 cell.dateValue = t;
17057 cell.className += " fc-today";
17058 cell.className += " fc-state-highlight";
17059 cell.title = cal.todayText;
17062 // disable highlight in other month..
17063 //cell.className += " fc-state-highlight";
17068 cell.className = " fc-state-disabled";
17069 cell.title = cal.minText;
17073 cell.className = " fc-state-disabled";
17074 cell.title = cal.maxText;
17078 if(ddays.indexOf(d.getDay()) != -1){
17079 cell.title = ddaysText;
17080 cell.className = " fc-state-disabled";
17083 if(ddMatch && format){
17084 var fvalue = d.dateFormat(format);
17085 if(ddMatch.test(fvalue)){
17086 cell.title = ddText.replace("%0", fvalue);
17087 cell.className = " fc-state-disabled";
17091 if (!cell.initialClassName) {
17092 cell.initialClassName = cell.dom.className;
17095 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17100 for(; i < startingPos; i++) {
17101 textEls[i].innerHTML = (++prevStart);
17102 d.setDate(d.getDate()+1);
17104 cells[i].className = "fc-past fc-other-month";
17105 setCellClass(this, cells[i]);
17110 for(; i < days; i++){
17111 intDay = i - startingPos + 1;
17112 textEls[i].innerHTML = (intDay);
17113 d.setDate(d.getDate()+1);
17115 cells[i].className = ''; // "x-date-active";
17116 setCellClass(this, cells[i]);
17120 for(; i < 42; i++) {
17121 textEls[i].innerHTML = (++extraDays);
17122 d.setDate(d.getDate()+1);
17124 cells[i].className = "fc-future fc-other-month";
17125 setCellClass(this, cells[i]);
17128 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17130 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17132 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17133 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17135 if(totalRows != 6){
17136 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17137 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17140 this.fireEvent('monthchange', this, date);
17144 if(!this.internalRender){
17145 var main = this.el.dom.firstChild;
17146 var w = main.offsetWidth;
17147 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17148 Roo.fly(main).setWidth(w);
17149 this.internalRender = true;
17150 // opera does not respect the auto grow header center column
17151 // then, after it gets a width opera refuses to recalculate
17152 // without a second pass
17153 if(Roo.isOpera && !this.secondPass){
17154 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17155 this.secondPass = true;
17156 this.update.defer(10, this, [date]);
17163 findCell : function(dt) {
17164 dt = dt.clearTime().getTime();
17166 this.cells.each(function(c){
17167 //Roo.log("check " +c.dateValue + '?=' + dt);
17168 if(c.dateValue == dt){
17178 findCells : function(ev) {
17179 var s = ev.start.clone().clearTime().getTime();
17181 var e= ev.end.clone().clearTime().getTime();
17184 this.cells.each(function(c){
17185 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17187 if(c.dateValue > e){
17190 if(c.dateValue < s){
17199 // findBestRow: function(cells)
17203 // for (var i =0 ; i < cells.length;i++) {
17204 // ret = Math.max(cells[i].rows || 0,ret);
17211 addItem : function(ev)
17213 // look for vertical location slot in
17214 var cells = this.findCells(ev);
17216 // ev.row = this.findBestRow(cells);
17218 // work out the location.
17222 for(var i =0; i < cells.length; i++) {
17224 cells[i].row = cells[0].row;
17227 cells[i].row = cells[i].row + 1;
17237 if (crow.start.getY() == cells[i].getY()) {
17239 crow.end = cells[i];
17256 cells[0].events.push(ev);
17258 this.calevents.push(ev);
17261 clearEvents: function() {
17263 if(!this.calevents){
17267 Roo.each(this.cells.elements, function(c){
17273 Roo.each(this.calevents, function(e) {
17274 Roo.each(e.els, function(el) {
17275 el.un('mouseenter' ,this.onEventEnter, this);
17276 el.un('mouseleave' ,this.onEventLeave, this);
17281 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17287 renderEvents: function()
17291 this.cells.each(function(c) {
17300 if(c.row != c.events.length){
17301 r = 4 - (4 - (c.row - c.events.length));
17304 c.events = ev.slice(0, r);
17305 c.more = ev.slice(r);
17307 if(c.more.length && c.more.length == 1){
17308 c.events.push(c.more.pop());
17311 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17315 this.cells.each(function(c) {
17317 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17320 for (var e = 0; e < c.events.length; e++){
17321 var ev = c.events[e];
17322 var rows = ev.rows;
17324 for(var i = 0; i < rows.length; i++) {
17326 // how many rows should it span..
17329 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17330 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17332 unselectable : "on",
17335 cls: 'fc-event-inner',
17339 // cls: 'fc-event-time',
17340 // html : cells.length > 1 ? '' : ev.time
17344 cls: 'fc-event-title',
17345 html : String.format('{0}', ev.title)
17352 cls: 'ui-resizable-handle ui-resizable-e',
17353 html : '  '
17360 cfg.cls += ' fc-event-start';
17362 if ((i+1) == rows.length) {
17363 cfg.cls += ' fc-event-end';
17366 var ctr = _this.el.select('.fc-event-container',true).first();
17367 var cg = ctr.createChild(cfg);
17369 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17370 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17372 var r = (c.more.length) ? 1 : 0;
17373 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17374 cg.setWidth(ebox.right - sbox.x -2);
17376 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17377 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17378 cg.on('click', _this.onEventClick, _this, ev);
17389 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17390 style : 'position: absolute',
17391 unselectable : "on",
17394 cls: 'fc-event-inner',
17398 cls: 'fc-event-title',
17406 cls: 'ui-resizable-handle ui-resizable-e',
17407 html : '  '
17413 var ctr = _this.el.select('.fc-event-container',true).first();
17414 var cg = ctr.createChild(cfg);
17416 var sbox = c.select('.fc-day-content',true).first().getBox();
17417 var ebox = c.select('.fc-day-content',true).first().getBox();
17419 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17420 cg.setWidth(ebox.right - sbox.x -2);
17422 cg.on('click', _this.onMoreEventClick, _this, c.more);
17432 onEventEnter: function (e, el,event,d) {
17433 this.fireEvent('evententer', this, el, event);
17436 onEventLeave: function (e, el,event,d) {
17437 this.fireEvent('eventleave', this, el, event);
17440 onEventClick: function (e, el,event,d) {
17441 this.fireEvent('eventclick', this, el, event);
17444 onMonthChange: function () {
17448 onMoreEventClick: function(e, el, more)
17452 this.calpopover.placement = 'right';
17453 this.calpopover.setTitle('More');
17455 this.calpopover.setContent('');
17457 var ctr = this.calpopover.el.select('.popover-content', true).first();
17459 Roo.each(more, function(m){
17461 cls : 'fc-event-hori fc-event-draggable',
17464 var cg = ctr.createChild(cfg);
17466 cg.on('click', _this.onEventClick, _this, m);
17469 this.calpopover.show(el);
17474 onLoad: function ()
17476 this.calevents = [];
17479 if(this.store.getCount() > 0){
17480 this.store.data.each(function(d){
17483 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17484 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17485 time : d.data.start_time,
17486 title : d.data.title,
17487 description : d.data.description,
17488 venue : d.data.venue
17493 this.renderEvents();
17495 if(this.calevents.length && this.loadMask){
17496 this.maskEl.hide();
17500 onBeforeLoad: function()
17502 this.clearEvents();
17504 this.maskEl.show();
17518 * @class Roo.bootstrap.Popover
17519 * @extends Roo.bootstrap.Component
17520 * Bootstrap Popover class
17521 * @cfg {String} html contents of the popover (or false to use children..)
17522 * @cfg {String} title of popover (or false to hide)
17523 * @cfg {String} placement how it is placed
17524 * @cfg {String} trigger click || hover (or false to trigger manually)
17525 * @cfg {String} over what (parent or false to trigger manually.)
17526 * @cfg {Number} delay - delay before showing
17529 * Create a new Popover
17530 * @param {Object} config The config object
17533 Roo.bootstrap.Popover = function(config){
17534 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17540 * After the popover show
17542 * @param {Roo.bootstrap.Popover} this
17547 * After the popover hide
17549 * @param {Roo.bootstrap.Popover} this
17555 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17557 title: 'Fill in a title',
17560 placement : 'right',
17561 trigger : 'hover', // hover
17567 can_build_overlaid : false,
17569 getChildContainer : function()
17571 return this.el.select('.popover-content',true).first();
17574 getAutoCreate : function(){
17577 cls : 'popover roo-dynamic',
17578 style: 'display:block',
17584 cls : 'popover-inner',
17588 cls: 'popover-title',
17592 cls : 'popover-content',
17603 setTitle: function(str)
17606 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17608 setContent: function(str)
17611 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17613 // as it get's added to the bottom of the page.
17614 onRender : function(ct, position)
17616 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17618 var cfg = Roo.apply({}, this.getAutoCreate());
17622 cfg.cls += ' ' + this.cls;
17625 cfg.style = this.style;
17627 //Roo.log("adding to ");
17628 this.el = Roo.get(document.body).createChild(cfg, position);
17629 // Roo.log(this.el);
17634 initEvents : function()
17636 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17637 this.el.enableDisplayMode('block');
17639 if (this.over === false) {
17642 if (this.triggers === false) {
17645 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17646 var triggers = this.trigger ? this.trigger.split(' ') : [];
17647 Roo.each(triggers, function(trigger) {
17649 if (trigger == 'click') {
17650 on_el.on('click', this.toggle, this);
17651 } else if (trigger != 'manual') {
17652 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17653 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17655 on_el.on(eventIn ,this.enter, this);
17656 on_el.on(eventOut, this.leave, this);
17667 toggle : function () {
17668 this.hoverState == 'in' ? this.leave() : this.enter();
17671 enter : function () {
17673 clearTimeout(this.timeout);
17675 this.hoverState = 'in';
17677 if (!this.delay || !this.delay.show) {
17682 this.timeout = setTimeout(function () {
17683 if (_t.hoverState == 'in') {
17686 }, this.delay.show)
17689 leave : function() {
17690 clearTimeout(this.timeout);
17692 this.hoverState = 'out';
17694 if (!this.delay || !this.delay.hide) {
17699 this.timeout = setTimeout(function () {
17700 if (_t.hoverState == 'out') {
17703 }, this.delay.hide)
17706 show : function (on_el)
17709 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17713 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17714 if (this.html !== false) {
17715 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17717 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17718 if (!this.title.length) {
17719 this.el.select('.popover-title',true).hide();
17722 var placement = typeof this.placement == 'function' ?
17723 this.placement.call(this, this.el, on_el) :
17726 var autoToken = /\s?auto?\s?/i;
17727 var autoPlace = autoToken.test(placement);
17729 placement = placement.replace(autoToken, '') || 'top';
17733 //this.el.setXY([0,0]);
17735 this.el.dom.style.display='block';
17736 this.el.addClass(placement);
17738 //this.el.appendTo(on_el);
17740 var p = this.getPosition();
17741 var box = this.el.getBox();
17746 var align = Roo.bootstrap.Popover.alignment[placement];
17749 this.el.alignTo(on_el, align[0],align[1]);
17750 //var arrow = this.el.select('.arrow',true).first();
17751 //arrow.set(align[2],
17753 this.el.addClass('in');
17756 if (this.el.hasClass('fade')) {
17760 this.hoverState = 'in';
17762 this.fireEvent('show', this);
17767 this.el.setXY([0,0]);
17768 this.el.removeClass('in');
17770 this.hoverState = null;
17772 this.fireEvent('hide', this);
17777 Roo.bootstrap.Popover.alignment = {
17778 'left' : ['r-l', [-10,0], 'right'],
17779 'right' : ['l-r', [10,0], 'left'],
17780 'bottom' : ['t-b', [0,10], 'top'],
17781 'top' : [ 'b-t', [0,-10], 'bottom']
17792 * @class Roo.bootstrap.Progress
17793 * @extends Roo.bootstrap.Component
17794 * Bootstrap Progress class
17795 * @cfg {Boolean} striped striped of the progress bar
17796 * @cfg {Boolean} active animated of the progress bar
17800 * Create a new Progress
17801 * @param {Object} config The config object
17804 Roo.bootstrap.Progress = function(config){
17805 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17808 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17813 getAutoCreate : function(){
17821 cfg.cls += ' progress-striped';
17825 cfg.cls += ' active';
17844 * @class Roo.bootstrap.ProgressBar
17845 * @extends Roo.bootstrap.Component
17846 * Bootstrap ProgressBar class
17847 * @cfg {Number} aria_valuenow aria-value now
17848 * @cfg {Number} aria_valuemin aria-value min
17849 * @cfg {Number} aria_valuemax aria-value max
17850 * @cfg {String} label label for the progress bar
17851 * @cfg {String} panel (success | info | warning | danger )
17852 * @cfg {String} role role of the progress bar
17853 * @cfg {String} sr_only text
17857 * Create a new ProgressBar
17858 * @param {Object} config The config object
17861 Roo.bootstrap.ProgressBar = function(config){
17862 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17865 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17869 aria_valuemax : 100,
17875 getAutoCreate : function()
17880 cls: 'progress-bar',
17881 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17893 cfg.role = this.role;
17896 if(this.aria_valuenow){
17897 cfg['aria-valuenow'] = this.aria_valuenow;
17900 if(this.aria_valuemin){
17901 cfg['aria-valuemin'] = this.aria_valuemin;
17904 if(this.aria_valuemax){
17905 cfg['aria-valuemax'] = this.aria_valuemax;
17908 if(this.label && !this.sr_only){
17909 cfg.html = this.label;
17913 cfg.cls += ' progress-bar-' + this.panel;
17919 update : function(aria_valuenow)
17921 this.aria_valuenow = aria_valuenow;
17923 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17938 * @class Roo.bootstrap.TabGroup
17939 * @extends Roo.bootstrap.Column
17940 * Bootstrap Column class
17941 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17942 * @cfg {Boolean} carousel true to make the group behave like a carousel
17943 * @cfg {Boolean} bullets show bullets for the panels
17944 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17945 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17946 * @cfg {Boolean} showarrow (true|false) show arrow default true
17949 * Create a new TabGroup
17950 * @param {Object} config The config object
17953 Roo.bootstrap.TabGroup = function(config){
17954 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17956 this.navId = Roo.id();
17959 Roo.bootstrap.TabGroup.register(this);
17963 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17966 transition : false,
17971 slideOnTouch : false,
17974 getAutoCreate : function()
17976 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17978 cfg.cls += ' tab-content';
17980 if (this.carousel) {
17981 cfg.cls += ' carousel slide';
17984 cls : 'carousel-inner',
17988 if(this.bullets && !Roo.isTouch){
17991 cls : 'carousel-bullets',
17995 if(this.bullets_cls){
17996 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18003 cfg.cn[0].cn.push(bullets);
18006 if(this.showarrow){
18007 cfg.cn[0].cn.push({
18009 class : 'carousel-arrow',
18013 class : 'carousel-prev',
18017 class : 'fa fa-chevron-left'
18023 class : 'carousel-next',
18027 class : 'fa fa-chevron-right'
18040 initEvents: function()
18042 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18043 // this.el.on("touchstart", this.onTouchStart, this);
18046 if(this.autoslide){
18049 this.slideFn = window.setInterval(function() {
18050 _this.showPanelNext();
18054 if(this.showarrow){
18055 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18056 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18062 // onTouchStart : function(e, el, o)
18064 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18068 // this.showPanelNext();
18072 getChildContainer : function()
18074 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18078 * register a Navigation item
18079 * @param {Roo.bootstrap.NavItem} the navitem to add
18081 register : function(item)
18083 this.tabs.push( item);
18084 item.navId = this.navId; // not really needed..
18089 getActivePanel : function()
18092 Roo.each(this.tabs, function(t) {
18102 getPanelByName : function(n)
18105 Roo.each(this.tabs, function(t) {
18106 if (t.tabId == n) {
18114 indexOfPanel : function(p)
18117 Roo.each(this.tabs, function(t,i) {
18118 if (t.tabId == p.tabId) {
18127 * show a specific panel
18128 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18129 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18131 showPanel : function (pan)
18133 if(this.transition || typeof(pan) == 'undefined'){
18134 Roo.log("waiting for the transitionend");
18138 if (typeof(pan) == 'number') {
18139 pan = this.tabs[pan];
18142 if (typeof(pan) == 'string') {
18143 pan = this.getPanelByName(pan);
18146 var cur = this.getActivePanel();
18149 Roo.log('pan or acitve pan is undefined');
18153 if (pan.tabId == this.getActivePanel().tabId) {
18157 if (false === cur.fireEvent('beforedeactivate')) {
18161 if(this.bullets > 0 && !Roo.isTouch){
18162 this.setActiveBullet(this.indexOfPanel(pan));
18165 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18167 this.transition = true;
18168 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18169 var lr = dir == 'next' ? 'left' : 'right';
18170 pan.el.addClass(dir); // or prev
18171 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18172 cur.el.addClass(lr); // or right
18173 pan.el.addClass(lr);
18176 cur.el.on('transitionend', function() {
18177 Roo.log("trans end?");
18179 pan.el.removeClass([lr,dir]);
18180 pan.setActive(true);
18182 cur.el.removeClass([lr]);
18183 cur.setActive(false);
18185 _this.transition = false;
18187 }, this, { single: true } );
18192 cur.setActive(false);
18193 pan.setActive(true);
18198 showPanelNext : function()
18200 var i = this.indexOfPanel(this.getActivePanel());
18202 if (i >= this.tabs.length - 1 && !this.autoslide) {
18206 if (i >= this.tabs.length - 1 && this.autoslide) {
18210 this.showPanel(this.tabs[i+1]);
18213 showPanelPrev : function()
18215 var i = this.indexOfPanel(this.getActivePanel());
18217 if (i < 1 && !this.autoslide) {
18221 if (i < 1 && this.autoslide) {
18222 i = this.tabs.length;
18225 this.showPanel(this.tabs[i-1]);
18229 addBullet: function()
18231 if(!this.bullets || Roo.isTouch){
18234 var ctr = this.el.select('.carousel-bullets',true).first();
18235 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18236 var bullet = ctr.createChild({
18237 cls : 'bullet bullet-' + i
18238 },ctr.dom.lastChild);
18243 bullet.on('click', (function(e, el, o, ii, t){
18245 e.preventDefault();
18247 this.showPanel(ii);
18249 if(this.autoslide && this.slideFn){
18250 clearInterval(this.slideFn);
18251 this.slideFn = window.setInterval(function() {
18252 _this.showPanelNext();
18256 }).createDelegate(this, [i, bullet], true));
18261 setActiveBullet : function(i)
18267 Roo.each(this.el.select('.bullet', true).elements, function(el){
18268 el.removeClass('selected');
18271 var bullet = this.el.select('.bullet-' + i, true).first();
18277 bullet.addClass('selected');
18288 Roo.apply(Roo.bootstrap.TabGroup, {
18292 * register a Navigation Group
18293 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18295 register : function(navgrp)
18297 this.groups[navgrp.navId] = navgrp;
18301 * fetch a Navigation Group based on the navigation ID
18302 * if one does not exist , it will get created.
18303 * @param {string} the navgroup to add
18304 * @returns {Roo.bootstrap.NavGroup} the navgroup
18306 get: function(navId) {
18307 if (typeof(this.groups[navId]) == 'undefined') {
18308 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18310 return this.groups[navId] ;
18325 * @class Roo.bootstrap.TabPanel
18326 * @extends Roo.bootstrap.Component
18327 * Bootstrap TabPanel class
18328 * @cfg {Boolean} active panel active
18329 * @cfg {String} html panel content
18330 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18331 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18332 * @cfg {String} href click to link..
18336 * Create a new TabPanel
18337 * @param {Object} config The config object
18340 Roo.bootstrap.TabPanel = function(config){
18341 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18345 * Fires when the active status changes
18346 * @param {Roo.bootstrap.TabPanel} this
18347 * @param {Boolean} state the new state
18352 * @event beforedeactivate
18353 * Fires before a tab is de-activated - can be used to do validation on a form.
18354 * @param {Roo.bootstrap.TabPanel} this
18355 * @return {Boolean} false if there is an error
18358 'beforedeactivate': true
18361 this.tabId = this.tabId || Roo.id();
18365 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18373 getAutoCreate : function(){
18376 // item is needed for carousel - not sure if it has any effect otherwise
18377 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18378 html: this.html || ''
18382 cfg.cls += ' active';
18386 cfg.tabId = this.tabId;
18393 initEvents: function()
18395 var p = this.parent();
18397 this.navId = this.navId || p.navId;
18399 if (typeof(this.navId) != 'undefined') {
18400 // not really needed.. but just in case.. parent should be a NavGroup.
18401 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18405 var i = tg.tabs.length - 1;
18407 if(this.active && tg.bullets > 0 && i < tg.bullets){
18408 tg.setActiveBullet(i);
18412 this.el.on('click', this.onClick, this);
18415 this.el.on("touchstart", this.onTouchStart, this);
18416 this.el.on("touchmove", this.onTouchMove, this);
18417 this.el.on("touchend", this.onTouchEnd, this);
18422 onRender : function(ct, position)
18424 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18427 setActive : function(state)
18429 Roo.log("panel - set active " + this.tabId + "=" + state);
18431 this.active = state;
18433 this.el.removeClass('active');
18435 } else if (!this.el.hasClass('active')) {
18436 this.el.addClass('active');
18439 this.fireEvent('changed', this, state);
18442 onClick : function(e)
18444 e.preventDefault();
18446 if(!this.href.length){
18450 window.location.href = this.href;
18459 onTouchStart : function(e)
18461 this.swiping = false;
18463 this.startX = e.browserEvent.touches[0].clientX;
18464 this.startY = e.browserEvent.touches[0].clientY;
18467 onTouchMove : function(e)
18469 this.swiping = true;
18471 this.endX = e.browserEvent.touches[0].clientX;
18472 this.endY = e.browserEvent.touches[0].clientY;
18475 onTouchEnd : function(e)
18482 var tabGroup = this.parent();
18484 if(this.endX > this.startX){ // swiping right
18485 tabGroup.showPanelPrev();
18489 if(this.startX > this.endX){ // swiping left
18490 tabGroup.showPanelNext();
18509 * @class Roo.bootstrap.DateField
18510 * @extends Roo.bootstrap.Input
18511 * Bootstrap DateField class
18512 * @cfg {Number} weekStart default 0
18513 * @cfg {String} viewMode default empty, (months|years)
18514 * @cfg {String} minViewMode default empty, (months|years)
18515 * @cfg {Number} startDate default -Infinity
18516 * @cfg {Number} endDate default Infinity
18517 * @cfg {Boolean} todayHighlight default false
18518 * @cfg {Boolean} todayBtn default false
18519 * @cfg {Boolean} calendarWeeks default false
18520 * @cfg {Object} daysOfWeekDisabled default empty
18521 * @cfg {Boolean} singleMode default false (true | false)
18523 * @cfg {Boolean} keyboardNavigation default true
18524 * @cfg {String} language default en
18527 * Create a new DateField
18528 * @param {Object} config The config object
18531 Roo.bootstrap.DateField = function(config){
18532 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18536 * Fires when this field show.
18537 * @param {Roo.bootstrap.DateField} this
18538 * @param {Mixed} date The date value
18543 * Fires when this field hide.
18544 * @param {Roo.bootstrap.DateField} this
18545 * @param {Mixed} date The date value
18550 * Fires when select a date.
18551 * @param {Roo.bootstrap.DateField} this
18552 * @param {Mixed} date The date value
18556 * @event beforeselect
18557 * Fires when before select a date.
18558 * @param {Roo.bootstrap.DateField} this
18559 * @param {Mixed} date The date value
18561 beforeselect : true
18565 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18568 * @cfg {String} format
18569 * The default date format string which can be overriden for localization support. The format must be
18570 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18574 * @cfg {String} altFormats
18575 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18576 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18578 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18586 todayHighlight : false,
18592 keyboardNavigation: true,
18594 calendarWeeks: false,
18596 startDate: -Infinity,
18600 daysOfWeekDisabled: [],
18604 singleMode : false,
18606 UTCDate: function()
18608 return new Date(Date.UTC.apply(Date, arguments));
18611 UTCToday: function()
18613 var today = new Date();
18614 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18617 getDate: function() {
18618 var d = this.getUTCDate();
18619 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18622 getUTCDate: function() {
18626 setDate: function(d) {
18627 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18630 setUTCDate: function(d) {
18632 this.setValue(this.formatDate(this.date));
18635 onRender: function(ct, position)
18638 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18640 this.language = this.language || 'en';
18641 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18642 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18644 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18645 this.format = this.format || 'm/d/y';
18646 this.isInline = false;
18647 this.isInput = true;
18648 this.component = this.el.select('.add-on', true).first() || false;
18649 this.component = (this.component && this.component.length === 0) ? false : this.component;
18650 this.hasInput = this.component && this.inputEl().length;
18652 if (typeof(this.minViewMode === 'string')) {
18653 switch (this.minViewMode) {
18655 this.minViewMode = 1;
18658 this.minViewMode = 2;
18661 this.minViewMode = 0;
18666 if (typeof(this.viewMode === 'string')) {
18667 switch (this.viewMode) {
18680 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18682 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18684 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18686 this.picker().on('mousedown', this.onMousedown, this);
18687 this.picker().on('click', this.onClick, this);
18689 this.picker().addClass('datepicker-dropdown');
18691 this.startViewMode = this.viewMode;
18693 if(this.singleMode){
18694 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18695 v.setVisibilityMode(Roo.Element.DISPLAY);
18699 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18700 v.setStyle('width', '189px');
18704 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18705 if(!this.calendarWeeks){
18710 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18711 v.attr('colspan', function(i, val){
18712 return parseInt(val) + 1;
18717 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18719 this.setStartDate(this.startDate);
18720 this.setEndDate(this.endDate);
18722 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18729 if(this.isInline) {
18734 picker : function()
18736 return this.pickerEl;
18737 // return this.el.select('.datepicker', true).first();
18740 fillDow: function()
18742 var dowCnt = this.weekStart;
18751 if(this.calendarWeeks){
18759 while (dowCnt < this.weekStart + 7) {
18763 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18767 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18770 fillMonths: function()
18773 var months = this.picker().select('>.datepicker-months td', true).first();
18775 months.dom.innerHTML = '';
18781 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18784 months.createChild(month);
18791 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;
18793 if (this.date < this.startDate) {
18794 this.viewDate = new Date(this.startDate);
18795 } else if (this.date > this.endDate) {
18796 this.viewDate = new Date(this.endDate);
18798 this.viewDate = new Date(this.date);
18806 var d = new Date(this.viewDate),
18807 year = d.getUTCFullYear(),
18808 month = d.getUTCMonth(),
18809 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18810 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18811 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18812 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18813 currentDate = this.date && this.date.valueOf(),
18814 today = this.UTCToday();
18816 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18818 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18820 // this.picker.select('>tfoot th.today').
18821 // .text(dates[this.language].today)
18822 // .toggle(this.todayBtn !== false);
18824 this.updateNavArrows();
18827 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18829 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18831 prevMonth.setUTCDate(day);
18833 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18835 var nextMonth = new Date(prevMonth);
18837 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18839 nextMonth = nextMonth.valueOf();
18841 var fillMonths = false;
18843 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18845 while(prevMonth.valueOf() <= nextMonth) {
18848 if (prevMonth.getUTCDay() === this.weekStart) {
18850 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18858 if(this.calendarWeeks){
18859 // ISO 8601: First week contains first thursday.
18860 // ISO also states week starts on Monday, but we can be more abstract here.
18862 // Start of current week: based on weekstart/current date
18863 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18864 // Thursday of this week
18865 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18866 // First Thursday of year, year from thursday
18867 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18868 // Calendar week: ms between thursdays, div ms per day, div 7 days
18869 calWeek = (th - yth) / 864e5 / 7 + 1;
18871 fillMonths.cn.push({
18879 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18881 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18884 if (this.todayHighlight &&
18885 prevMonth.getUTCFullYear() == today.getFullYear() &&
18886 prevMonth.getUTCMonth() == today.getMonth() &&
18887 prevMonth.getUTCDate() == today.getDate()) {
18888 clsName += ' today';
18891 if (currentDate && prevMonth.valueOf() === currentDate) {
18892 clsName += ' active';
18895 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18896 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18897 clsName += ' disabled';
18900 fillMonths.cn.push({
18902 cls: 'day ' + clsName,
18903 html: prevMonth.getDate()
18906 prevMonth.setDate(prevMonth.getDate()+1);
18909 var currentYear = this.date && this.date.getUTCFullYear();
18910 var currentMonth = this.date && this.date.getUTCMonth();
18912 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18914 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18915 v.removeClass('active');
18917 if(currentYear === year && k === currentMonth){
18918 v.addClass('active');
18921 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18922 v.addClass('disabled');
18928 year = parseInt(year/10, 10) * 10;
18930 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18932 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18935 for (var i = -1; i < 11; i++) {
18936 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18938 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18946 showMode: function(dir)
18949 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18952 Roo.each(this.picker().select('>div',true).elements, function(v){
18953 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18956 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18961 if(this.isInline) {
18965 this.picker().removeClass(['bottom', 'top']);
18967 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18969 * place to the top of element!
18973 this.picker().addClass('top');
18974 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18979 this.picker().addClass('bottom');
18981 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18984 parseDate : function(value)
18986 if(!value || value instanceof Date){
18989 var v = Date.parseDate(value, this.format);
18990 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18991 v = Date.parseDate(value, 'Y-m-d');
18993 if(!v && this.altFormats){
18994 if(!this.altFormatsArray){
18995 this.altFormatsArray = this.altFormats.split("|");
18997 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18998 v = Date.parseDate(value, this.altFormatsArray[i]);
19004 formatDate : function(date, fmt)
19006 return (!date || !(date instanceof Date)) ?
19007 date : date.dateFormat(fmt || this.format);
19010 onFocus : function()
19012 Roo.bootstrap.DateField.superclass.onFocus.call(this);
19016 onBlur : function()
19018 Roo.bootstrap.DateField.superclass.onBlur.call(this);
19020 var d = this.inputEl().getValue();
19027 showPopup : function()
19029 this.picker().show();
19033 this.fireEvent('showpopup', this, this.date);
19036 hidePopup : function()
19038 if(this.isInline) {
19041 this.picker().hide();
19042 this.viewMode = this.startViewMode;
19045 this.fireEvent('hidepopup', this, this.date);
19049 onMousedown: function(e)
19051 e.stopPropagation();
19052 e.preventDefault();
19057 Roo.bootstrap.DateField.superclass.keyup.call(this);
19061 setValue: function(v)
19063 if(this.fireEvent('beforeselect', this, v) !== false){
19064 var d = new Date(this.parseDate(v) ).clearTime();
19066 if(isNaN(d.getTime())){
19067 this.date = this.viewDate = '';
19068 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19072 v = this.formatDate(d);
19074 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19076 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19080 this.fireEvent('select', this, this.date);
19084 getValue: function()
19086 return this.formatDate(this.date);
19089 fireKey: function(e)
19091 if (!this.picker().isVisible()){
19092 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19098 var dateChanged = false,
19100 newDate, newViewDate;
19105 e.preventDefault();
19109 if (!this.keyboardNavigation) {
19112 dir = e.keyCode == 37 ? -1 : 1;
19115 newDate = this.moveYear(this.date, dir);
19116 newViewDate = this.moveYear(this.viewDate, dir);
19117 } else if (e.shiftKey){
19118 newDate = this.moveMonth(this.date, dir);
19119 newViewDate = this.moveMonth(this.viewDate, dir);
19121 newDate = new Date(this.date);
19122 newDate.setUTCDate(this.date.getUTCDate() + dir);
19123 newViewDate = new Date(this.viewDate);
19124 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19126 if (this.dateWithinRange(newDate)){
19127 this.date = newDate;
19128 this.viewDate = newViewDate;
19129 this.setValue(this.formatDate(this.date));
19131 e.preventDefault();
19132 dateChanged = true;
19137 if (!this.keyboardNavigation) {
19140 dir = e.keyCode == 38 ? -1 : 1;
19142 newDate = this.moveYear(this.date, dir);
19143 newViewDate = this.moveYear(this.viewDate, dir);
19144 } else if (e.shiftKey){
19145 newDate = this.moveMonth(this.date, dir);
19146 newViewDate = this.moveMonth(this.viewDate, dir);
19148 newDate = new Date(this.date);
19149 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19150 newViewDate = new Date(this.viewDate);
19151 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19153 if (this.dateWithinRange(newDate)){
19154 this.date = newDate;
19155 this.viewDate = newViewDate;
19156 this.setValue(this.formatDate(this.date));
19158 e.preventDefault();
19159 dateChanged = true;
19163 this.setValue(this.formatDate(this.date));
19165 e.preventDefault();
19168 this.setValue(this.formatDate(this.date));
19182 onClick: function(e)
19184 e.stopPropagation();
19185 e.preventDefault();
19187 var target = e.getTarget();
19189 if(target.nodeName.toLowerCase() === 'i'){
19190 target = Roo.get(target).dom.parentNode;
19193 var nodeName = target.nodeName;
19194 var className = target.className;
19195 var html = target.innerHTML;
19196 //Roo.log(nodeName);
19198 switch(nodeName.toLowerCase()) {
19200 switch(className) {
19206 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19207 switch(this.viewMode){
19209 this.viewDate = this.moveMonth(this.viewDate, dir);
19213 this.viewDate = this.moveYear(this.viewDate, dir);
19219 var date = new Date();
19220 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19222 this.setValue(this.formatDate(this.date));
19229 if (className.indexOf('disabled') < 0) {
19230 this.viewDate.setUTCDate(1);
19231 if (className.indexOf('month') > -1) {
19232 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19234 var year = parseInt(html, 10) || 0;
19235 this.viewDate.setUTCFullYear(year);
19239 if(this.singleMode){
19240 this.setValue(this.formatDate(this.viewDate));
19251 //Roo.log(className);
19252 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19253 var day = parseInt(html, 10) || 1;
19254 var year = this.viewDate.getUTCFullYear(),
19255 month = this.viewDate.getUTCMonth();
19257 if (className.indexOf('old') > -1) {
19264 } else if (className.indexOf('new') > -1) {
19272 //Roo.log([year,month,day]);
19273 this.date = this.UTCDate(year, month, day,0,0,0,0);
19274 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19276 //Roo.log(this.formatDate(this.date));
19277 this.setValue(this.formatDate(this.date));
19284 setStartDate: function(startDate)
19286 this.startDate = startDate || -Infinity;
19287 if (this.startDate !== -Infinity) {
19288 this.startDate = this.parseDate(this.startDate);
19291 this.updateNavArrows();
19294 setEndDate: function(endDate)
19296 this.endDate = endDate || Infinity;
19297 if (this.endDate !== Infinity) {
19298 this.endDate = this.parseDate(this.endDate);
19301 this.updateNavArrows();
19304 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19306 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19307 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19308 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19310 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19311 return parseInt(d, 10);
19314 this.updateNavArrows();
19317 updateNavArrows: function()
19319 if(this.singleMode){
19323 var d = new Date(this.viewDate),
19324 year = d.getUTCFullYear(),
19325 month = d.getUTCMonth();
19327 Roo.each(this.picker().select('.prev', true).elements, function(v){
19329 switch (this.viewMode) {
19332 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19338 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19345 Roo.each(this.picker().select('.next', true).elements, function(v){
19347 switch (this.viewMode) {
19350 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19356 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19364 moveMonth: function(date, dir)
19369 var new_date = new Date(date.valueOf()),
19370 day = new_date.getUTCDate(),
19371 month = new_date.getUTCMonth(),
19372 mag = Math.abs(dir),
19374 dir = dir > 0 ? 1 : -1;
19377 // If going back one month, make sure month is not current month
19378 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19380 return new_date.getUTCMonth() == month;
19382 // If going forward one month, make sure month is as expected
19383 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19385 return new_date.getUTCMonth() != new_month;
19387 new_month = month + dir;
19388 new_date.setUTCMonth(new_month);
19389 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19390 if (new_month < 0 || new_month > 11) {
19391 new_month = (new_month + 12) % 12;
19394 // For magnitudes >1, move one month at a time...
19395 for (var i=0; i<mag; i++) {
19396 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19397 new_date = this.moveMonth(new_date, dir);
19399 // ...then reset the day, keeping it in the new month
19400 new_month = new_date.getUTCMonth();
19401 new_date.setUTCDate(day);
19403 return new_month != new_date.getUTCMonth();
19406 // Common date-resetting loop -- if date is beyond end of month, make it
19409 new_date.setUTCDate(--day);
19410 new_date.setUTCMonth(new_month);
19415 moveYear: function(date, dir)
19417 return this.moveMonth(date, dir*12);
19420 dateWithinRange: function(date)
19422 return date >= this.startDate && date <= this.endDate;
19428 this.picker().remove();
19431 validateValue : function(value)
19433 if(this.getVisibilityEl().hasClass('hidden')){
19437 if(value.length < 1) {
19438 if(this.allowBlank){
19444 if(value.length < this.minLength){
19447 if(value.length > this.maxLength){
19451 var vt = Roo.form.VTypes;
19452 if(!vt[this.vtype](value, this)){
19456 if(typeof this.validator == "function"){
19457 var msg = this.validator(value);
19463 if(this.regex && !this.regex.test(value)){
19467 if(typeof(this.parseDate(value)) == 'undefined'){
19471 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19475 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19485 this.date = this.viewDate = '';
19487 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19492 Roo.apply(Roo.bootstrap.DateField, {
19503 html: '<i class="fa fa-arrow-left"/>'
19513 html: '<i class="fa fa-arrow-right"/>'
19555 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19556 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19557 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19558 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19559 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19572 navFnc: 'FullYear',
19577 navFnc: 'FullYear',
19582 Roo.apply(Roo.bootstrap.DateField, {
19586 cls: 'datepicker dropdown-menu roo-dynamic',
19590 cls: 'datepicker-days',
19594 cls: 'table-condensed',
19596 Roo.bootstrap.DateField.head,
19600 Roo.bootstrap.DateField.footer
19607 cls: 'datepicker-months',
19611 cls: 'table-condensed',
19613 Roo.bootstrap.DateField.head,
19614 Roo.bootstrap.DateField.content,
19615 Roo.bootstrap.DateField.footer
19622 cls: 'datepicker-years',
19626 cls: 'table-condensed',
19628 Roo.bootstrap.DateField.head,
19629 Roo.bootstrap.DateField.content,
19630 Roo.bootstrap.DateField.footer
19649 * @class Roo.bootstrap.TimeField
19650 * @extends Roo.bootstrap.Input
19651 * Bootstrap DateField class
19655 * Create a new TimeField
19656 * @param {Object} config The config object
19659 Roo.bootstrap.TimeField = function(config){
19660 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19664 * Fires when this field show.
19665 * @param {Roo.bootstrap.DateField} thisthis
19666 * @param {Mixed} date The date value
19671 * Fires when this field hide.
19672 * @param {Roo.bootstrap.DateField} this
19673 * @param {Mixed} date The date value
19678 * Fires when select a date.
19679 * @param {Roo.bootstrap.DateField} this
19680 * @param {Mixed} date The date value
19686 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19689 * @cfg {String} format
19690 * The default time format string which can be overriden for localization support. The format must be
19691 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19695 onRender: function(ct, position)
19698 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19700 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19702 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19704 this.pop = this.picker().select('>.datepicker-time',true).first();
19705 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19707 this.picker().on('mousedown', this.onMousedown, this);
19708 this.picker().on('click', this.onClick, this);
19710 this.picker().addClass('datepicker-dropdown');
19715 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19716 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19717 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19718 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19719 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19720 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19724 fireKey: function(e){
19725 if (!this.picker().isVisible()){
19726 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19732 e.preventDefault();
19740 this.onTogglePeriod();
19743 this.onIncrementMinutes();
19746 this.onDecrementMinutes();
19755 onClick: function(e) {
19756 e.stopPropagation();
19757 e.preventDefault();
19760 picker : function()
19762 return this.el.select('.datepicker', true).first();
19765 fillTime: function()
19767 var time = this.pop.select('tbody', true).first();
19769 time.dom.innerHTML = '';
19784 cls: 'hours-up glyphicon glyphicon-chevron-up'
19804 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19825 cls: 'timepicker-hour',
19840 cls: 'timepicker-minute',
19855 cls: 'btn btn-primary period',
19877 cls: 'hours-down glyphicon glyphicon-chevron-down'
19897 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19915 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19922 var hours = this.time.getHours();
19923 var minutes = this.time.getMinutes();
19936 hours = hours - 12;
19940 hours = '0' + hours;
19944 minutes = '0' + minutes;
19947 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19948 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19949 this.pop.select('button', true).first().dom.innerHTML = period;
19955 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19957 var cls = ['bottom'];
19959 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19966 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19971 this.picker().addClass(cls.join('-'));
19975 Roo.each(cls, function(c){
19977 _this.picker().setTop(_this.inputEl().getHeight());
19981 _this.picker().setTop(0 - _this.picker().getHeight());
19986 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19990 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19997 onFocus : function()
19999 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20003 onBlur : function()
20005 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20011 this.picker().show();
20016 this.fireEvent('show', this, this.date);
20021 this.picker().hide();
20024 this.fireEvent('hide', this, this.date);
20027 setTime : function()
20030 this.setValue(this.time.format(this.format));
20032 this.fireEvent('select', this, this.date);
20037 onMousedown: function(e){
20038 e.stopPropagation();
20039 e.preventDefault();
20042 onIncrementHours: function()
20044 Roo.log('onIncrementHours');
20045 this.time = this.time.add(Date.HOUR, 1);
20050 onDecrementHours: function()
20052 Roo.log('onDecrementHours');
20053 this.time = this.time.add(Date.HOUR, -1);
20057 onIncrementMinutes: function()
20059 Roo.log('onIncrementMinutes');
20060 this.time = this.time.add(Date.MINUTE, 1);
20064 onDecrementMinutes: function()
20066 Roo.log('onDecrementMinutes');
20067 this.time = this.time.add(Date.MINUTE, -1);
20071 onTogglePeriod: function()
20073 Roo.log('onTogglePeriod');
20074 this.time = this.time.add(Date.HOUR, 12);
20081 Roo.apply(Roo.bootstrap.TimeField, {
20111 cls: 'btn btn-info ok',
20123 Roo.apply(Roo.bootstrap.TimeField, {
20127 cls: 'datepicker dropdown-menu',
20131 cls: 'datepicker-time',
20135 cls: 'table-condensed',
20137 Roo.bootstrap.TimeField.content,
20138 Roo.bootstrap.TimeField.footer
20157 * @class Roo.bootstrap.MonthField
20158 * @extends Roo.bootstrap.Input
20159 * Bootstrap MonthField class
20161 * @cfg {String} language default en
20164 * Create a new MonthField
20165 * @param {Object} config The config object
20168 Roo.bootstrap.MonthField = function(config){
20169 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20174 * Fires when this field show.
20175 * @param {Roo.bootstrap.MonthField} this
20176 * @param {Mixed} date The date value
20181 * Fires when this field hide.
20182 * @param {Roo.bootstrap.MonthField} this
20183 * @param {Mixed} date The date value
20188 * Fires when select a date.
20189 * @param {Roo.bootstrap.MonthField} this
20190 * @param {String} oldvalue The old value
20191 * @param {String} newvalue The new value
20197 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20199 onRender: function(ct, position)
20202 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20204 this.language = this.language || 'en';
20205 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20206 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20208 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20209 this.isInline = false;
20210 this.isInput = true;
20211 this.component = this.el.select('.add-on', true).first() || false;
20212 this.component = (this.component && this.component.length === 0) ? false : this.component;
20213 this.hasInput = this.component && this.inputEL().length;
20215 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20217 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20219 this.picker().on('mousedown', this.onMousedown, this);
20220 this.picker().on('click', this.onClick, this);
20222 this.picker().addClass('datepicker-dropdown');
20224 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20225 v.setStyle('width', '189px');
20232 if(this.isInline) {
20238 setValue: function(v, suppressEvent)
20240 var o = this.getValue();
20242 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20246 if(suppressEvent !== true){
20247 this.fireEvent('select', this, o, v);
20252 getValue: function()
20257 onClick: function(e)
20259 e.stopPropagation();
20260 e.preventDefault();
20262 var target = e.getTarget();
20264 if(target.nodeName.toLowerCase() === 'i'){
20265 target = Roo.get(target).dom.parentNode;
20268 var nodeName = target.nodeName;
20269 var className = target.className;
20270 var html = target.innerHTML;
20272 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20276 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20278 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20284 picker : function()
20286 return this.pickerEl;
20289 fillMonths: function()
20292 var months = this.picker().select('>.datepicker-months td', true).first();
20294 months.dom.innerHTML = '';
20300 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20303 months.createChild(month);
20312 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20313 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20316 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20317 e.removeClass('active');
20319 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20320 e.addClass('active');
20327 if(this.isInline) {
20331 this.picker().removeClass(['bottom', 'top']);
20333 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20335 * place to the top of element!
20339 this.picker().addClass('top');
20340 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20345 this.picker().addClass('bottom');
20347 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20350 onFocus : function()
20352 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20356 onBlur : function()
20358 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20360 var d = this.inputEl().getValue();
20369 this.picker().show();
20370 this.picker().select('>.datepicker-months', true).first().show();
20374 this.fireEvent('show', this, this.date);
20379 if(this.isInline) {
20382 this.picker().hide();
20383 this.fireEvent('hide', this, this.date);
20387 onMousedown: function(e)
20389 e.stopPropagation();
20390 e.preventDefault();
20395 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20399 fireKey: function(e)
20401 if (!this.picker().isVisible()){
20402 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20413 e.preventDefault();
20417 dir = e.keyCode == 37 ? -1 : 1;
20419 this.vIndex = this.vIndex + dir;
20421 if(this.vIndex < 0){
20425 if(this.vIndex > 11){
20429 if(isNaN(this.vIndex)){
20433 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20439 dir = e.keyCode == 38 ? -1 : 1;
20441 this.vIndex = this.vIndex + dir * 4;
20443 if(this.vIndex < 0){
20447 if(this.vIndex > 11){
20451 if(isNaN(this.vIndex)){
20455 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20460 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20461 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20465 e.preventDefault();
20468 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20469 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20485 this.picker().remove();
20490 Roo.apply(Roo.bootstrap.MonthField, {
20509 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20510 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20515 Roo.apply(Roo.bootstrap.MonthField, {
20519 cls: 'datepicker dropdown-menu roo-dynamic',
20523 cls: 'datepicker-months',
20527 cls: 'table-condensed',
20529 Roo.bootstrap.DateField.content
20549 * @class Roo.bootstrap.CheckBox
20550 * @extends Roo.bootstrap.Input
20551 * Bootstrap CheckBox class
20553 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20554 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20555 * @cfg {String} boxLabel The text that appears beside the checkbox
20556 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20557 * @cfg {Boolean} checked initnal the element
20558 * @cfg {Boolean} inline inline the element (default false)
20559 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20560 * @cfg {String} tooltip label tooltip
20563 * Create a new CheckBox
20564 * @param {Object} config The config object
20567 Roo.bootstrap.CheckBox = function(config){
20568 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20573 * Fires when the element is checked or unchecked.
20574 * @param {Roo.bootstrap.CheckBox} this This input
20575 * @param {Boolean} checked The new checked value
20580 * Fires when the element is click.
20581 * @param {Roo.bootstrap.CheckBox} this This input
20588 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20590 inputType: 'checkbox',
20599 getAutoCreate : function()
20601 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20607 cfg.cls = 'form-group ' + this.inputType; //input-group
20610 cfg.cls += ' ' + this.inputType + '-inline';
20616 type : this.inputType,
20617 value : this.inputValue,
20618 cls : 'roo-' + this.inputType, //'form-box',
20619 placeholder : this.placeholder || ''
20623 if(this.inputType != 'radio'){
20627 cls : 'roo-hidden-value',
20628 value : this.checked ? this.inputValue : this.valueOff
20633 if (this.weight) { // Validity check?
20634 cfg.cls += " " + this.inputType + "-" + this.weight;
20637 if (this.disabled) {
20638 input.disabled=true;
20642 input.checked = this.checked;
20647 input.name = this.name;
20649 if(this.inputType != 'radio'){
20650 hidden.name = this.name;
20651 input.name = '_hidden_' + this.name;
20656 input.cls += ' input-' + this.size;
20661 ['xs','sm','md','lg'].map(function(size){
20662 if (settings[size]) {
20663 cfg.cls += ' col-' + size + '-' + settings[size];
20667 var inputblock = input;
20669 if (this.before || this.after) {
20672 cls : 'input-group',
20677 inputblock.cn.push({
20679 cls : 'input-group-addon',
20684 inputblock.cn.push(input);
20686 if(this.inputType != 'radio'){
20687 inputblock.cn.push(hidden);
20691 inputblock.cn.push({
20693 cls : 'input-group-addon',
20700 if (align ==='left' && this.fieldLabel.length) {
20701 // Roo.log("left and has label");
20706 cls : 'control-label',
20707 html : this.fieldLabel
20717 if(this.labelWidth > 12){
20718 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20721 if(this.labelWidth < 13 && this.labelmd == 0){
20722 this.labelmd = this.labelWidth;
20725 if(this.labellg > 0){
20726 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20727 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20730 if(this.labelmd > 0){
20731 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20732 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20735 if(this.labelsm > 0){
20736 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20737 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20740 if(this.labelxs > 0){
20741 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20742 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20745 } else if ( this.fieldLabel.length) {
20746 // Roo.log(" label");
20750 tag: this.boxLabel ? 'span' : 'label',
20752 cls: 'control-label box-input-label',
20753 //cls : 'input-group-addon',
20754 html : this.fieldLabel
20763 // Roo.log(" no label && no align");
20764 cfg.cn = [ inputblock ] ;
20770 var boxLabelCfg = {
20772 //'for': id, // box label is handled by onclick - so no for...
20774 html: this.boxLabel
20778 boxLabelCfg.tooltip = this.tooltip;
20781 cfg.cn.push(boxLabelCfg);
20784 if(this.inputType != 'radio'){
20785 cfg.cn.push(hidden);
20793 * return the real input element.
20795 inputEl: function ()
20797 return this.el.select('input.roo-' + this.inputType,true).first();
20799 hiddenEl: function ()
20801 return this.el.select('input.roo-hidden-value',true).first();
20804 labelEl: function()
20806 return this.el.select('label.control-label',true).first();
20808 /* depricated... */
20812 return this.labelEl();
20815 boxLabelEl: function()
20817 return this.el.select('label.box-label',true).first();
20820 initEvents : function()
20822 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20824 this.inputEl().on('click', this.onClick, this);
20826 if (this.boxLabel) {
20827 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20830 this.startValue = this.getValue();
20833 Roo.bootstrap.CheckBox.register(this);
20837 onClick : function(e)
20839 if(this.fireEvent('click', this, e) !== false){
20840 this.setChecked(!this.checked);
20845 setChecked : function(state,suppressEvent)
20847 this.startValue = this.getValue();
20849 if(this.inputType == 'radio'){
20851 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20852 e.dom.checked = false;
20855 this.inputEl().dom.checked = true;
20857 this.inputEl().dom.value = this.inputValue;
20859 if(suppressEvent !== true){
20860 this.fireEvent('check', this, true);
20868 this.checked = state;
20870 this.inputEl().dom.checked = state;
20873 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20875 if(suppressEvent !== true){
20876 this.fireEvent('check', this, state);
20882 getValue : function()
20884 if(this.inputType == 'radio'){
20885 return this.getGroupValue();
20888 return this.hiddenEl().dom.value;
20892 getGroupValue : function()
20894 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20898 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20901 setValue : function(v,suppressEvent)
20903 if(this.inputType == 'radio'){
20904 this.setGroupValue(v, suppressEvent);
20908 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20913 setGroupValue : function(v, suppressEvent)
20915 this.startValue = this.getValue();
20917 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20918 e.dom.checked = false;
20920 if(e.dom.value == v){
20921 e.dom.checked = true;
20925 if(suppressEvent !== true){
20926 this.fireEvent('check', this, true);
20934 validate : function()
20936 if(this.getVisibilityEl().hasClass('hidden')){
20942 (this.inputType == 'radio' && this.validateRadio()) ||
20943 (this.inputType == 'checkbox' && this.validateCheckbox())
20949 this.markInvalid();
20953 validateRadio : function()
20955 if(this.getVisibilityEl().hasClass('hidden')){
20959 if(this.allowBlank){
20965 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20966 if(!e.dom.checked){
20978 validateCheckbox : function()
20981 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20982 //return (this.getValue() == this.inputValue) ? true : false;
20985 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20993 for(var i in group){
20994 if(group[i].el.isVisible(true)){
21002 for(var i in group){
21007 r = (group[i].getValue() == group[i].inputValue) ? true : false;
21014 * Mark this field as valid
21016 markValid : function()
21020 this.fireEvent('valid', this);
21022 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21025 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21032 if(this.inputType == 'radio'){
21033 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21034 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21035 e.findParent('.form-group', false, true).addClass(_this.validClass);
21042 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21043 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21047 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21053 for(var i in group){
21054 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21055 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21060 * Mark this field as invalid
21061 * @param {String} msg The validation message
21063 markInvalid : function(msg)
21065 if(this.allowBlank){
21071 this.fireEvent('invalid', this, msg);
21073 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21076 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21080 label.markInvalid();
21083 if(this.inputType == 'radio'){
21084 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21085 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21086 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21093 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21094 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21098 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21104 for(var i in group){
21105 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21106 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21111 clearInvalid : function()
21113 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21115 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21117 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21119 if (label && label.iconEl) {
21120 label.iconEl.removeClass(label.validClass);
21121 label.iconEl.removeClass(label.invalidClass);
21125 disable : function()
21127 if(this.inputType != 'radio'){
21128 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21135 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21136 _this.getActionEl().addClass(this.disabledClass);
21137 e.dom.disabled = true;
21141 this.disabled = true;
21142 this.fireEvent("disable", this);
21146 enable : function()
21148 if(this.inputType != 'radio'){
21149 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21156 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21157 _this.getActionEl().removeClass(this.disabledClass);
21158 e.dom.disabled = false;
21162 this.disabled = false;
21163 this.fireEvent("enable", this);
21167 setBoxLabel : function(v)
21172 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21178 Roo.apply(Roo.bootstrap.CheckBox, {
21183 * register a CheckBox Group
21184 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21186 register : function(checkbox)
21188 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21189 this.groups[checkbox.groupId] = {};
21192 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21196 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21200 * fetch a CheckBox Group based on the group ID
21201 * @param {string} the group ID
21202 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21204 get: function(groupId) {
21205 if (typeof(this.groups[groupId]) == 'undefined') {
21209 return this.groups[groupId] ;
21222 * @class Roo.bootstrap.Radio
21223 * @extends Roo.bootstrap.Component
21224 * Bootstrap Radio class
21225 * @cfg {String} boxLabel - the label associated
21226 * @cfg {String} value - the value of radio
21229 * Create a new Radio
21230 * @param {Object} config The config object
21232 Roo.bootstrap.Radio = function(config){
21233 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21237 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21243 getAutoCreate : function()
21247 cls : 'form-group radio',
21252 html : this.boxLabel
21260 initEvents : function()
21262 this.parent().register(this);
21264 this.el.on('click', this.onClick, this);
21268 onClick : function(e)
21270 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21271 this.setChecked(true);
21275 setChecked : function(state, suppressEvent)
21277 this.parent().setValue(this.value, suppressEvent);
21281 setBoxLabel : function(v)
21286 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21301 * @class Roo.bootstrap.SecurePass
21302 * @extends Roo.bootstrap.Input
21303 * Bootstrap SecurePass class
21307 * Create a new SecurePass
21308 * @param {Object} config The config object
21311 Roo.bootstrap.SecurePass = function (config) {
21312 // these go here, so the translation tool can replace them..
21314 PwdEmpty: "Please type a password, and then retype it to confirm.",
21315 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21316 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21317 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21318 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21319 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21320 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21321 TooWeak: "Your password is Too Weak."
21323 this.meterLabel = "Password strength:";
21324 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21325 this.meterClass = [
21326 "roo-password-meter-tooweak",
21327 "roo-password-meter-weak",
21328 "roo-password-meter-medium",
21329 "roo-password-meter-strong",
21330 "roo-password-meter-grey"
21335 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21338 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21340 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21342 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21343 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21344 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21345 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21346 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21347 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21348 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21358 * @cfg {String/Object} Label for the strength meter (defaults to
21359 * 'Password strength:')
21364 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21365 * ['Weak', 'Medium', 'Strong'])
21368 pwdStrengths: false,
21381 initEvents: function ()
21383 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21385 if (this.el.is('input[type=password]') && Roo.isSafari) {
21386 this.el.on('keydown', this.SafariOnKeyDown, this);
21389 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21392 onRender: function (ct, position)
21394 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21395 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21396 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21398 this.trigger.createChild({
21403 cls: 'roo-password-meter-grey col-xs-12',
21406 //width: this.meterWidth + 'px'
21410 cls: 'roo-password-meter-text'
21416 if (this.hideTrigger) {
21417 this.trigger.setDisplayed(false);
21419 this.setSize(this.width || '', this.height || '');
21422 onDestroy: function ()
21424 if (this.trigger) {
21425 this.trigger.removeAllListeners();
21426 this.trigger.remove();
21429 this.wrap.remove();
21431 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21434 checkStrength: function ()
21436 var pwd = this.inputEl().getValue();
21437 if (pwd == this._lastPwd) {
21442 if (this.ClientSideStrongPassword(pwd)) {
21444 } else if (this.ClientSideMediumPassword(pwd)) {
21446 } else if (this.ClientSideWeakPassword(pwd)) {
21452 Roo.log('strength1: ' + strength);
21454 //var pm = this.trigger.child('div/div/div').dom;
21455 var pm = this.trigger.child('div/div');
21456 pm.removeClass(this.meterClass);
21457 pm.addClass(this.meterClass[strength]);
21460 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21462 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21464 this._lastPwd = pwd;
21468 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21470 this._lastPwd = '';
21472 var pm = this.trigger.child('div/div');
21473 pm.removeClass(this.meterClass);
21474 pm.addClass('roo-password-meter-grey');
21477 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21480 this.inputEl().dom.type='password';
21483 validateValue: function (value)
21486 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21489 if (value.length == 0) {
21490 if (this.allowBlank) {
21491 this.clearInvalid();
21495 this.markInvalid(this.errors.PwdEmpty);
21496 this.errorMsg = this.errors.PwdEmpty;
21504 if ('[\x21-\x7e]*'.match(value)) {
21505 this.markInvalid(this.errors.PwdBadChar);
21506 this.errorMsg = this.errors.PwdBadChar;
21509 if (value.length < 6) {
21510 this.markInvalid(this.errors.PwdShort);
21511 this.errorMsg = this.errors.PwdShort;
21514 if (value.length > 16) {
21515 this.markInvalid(this.errors.PwdLong);
21516 this.errorMsg = this.errors.PwdLong;
21520 if (this.ClientSideStrongPassword(value)) {
21522 } else if (this.ClientSideMediumPassword(value)) {
21524 } else if (this.ClientSideWeakPassword(value)) {
21531 if (strength < 2) {
21532 //this.markInvalid(this.errors.TooWeak);
21533 this.errorMsg = this.errors.TooWeak;
21538 console.log('strength2: ' + strength);
21540 //var pm = this.trigger.child('div/div/div').dom;
21542 var pm = this.trigger.child('div/div');
21543 pm.removeClass(this.meterClass);
21544 pm.addClass(this.meterClass[strength]);
21546 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21548 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21550 this.errorMsg = '';
21554 CharacterSetChecks: function (type)
21557 this.fResult = false;
21560 isctype: function (character, type)
21563 case this.kCapitalLetter:
21564 if (character >= 'A' && character <= 'Z') {
21569 case this.kSmallLetter:
21570 if (character >= 'a' && character <= 'z') {
21576 if (character >= '0' && character <= '9') {
21581 case this.kPunctuation:
21582 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21593 IsLongEnough: function (pwd, size)
21595 return !(pwd == null || isNaN(size) || pwd.length < size);
21598 SpansEnoughCharacterSets: function (word, nb)
21600 if (!this.IsLongEnough(word, nb))
21605 var characterSetChecks = new Array(
21606 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21607 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21610 for (var index = 0; index < word.length; ++index) {
21611 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21612 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21613 characterSetChecks[nCharSet].fResult = true;
21620 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21621 if (characterSetChecks[nCharSet].fResult) {
21626 if (nCharSets < nb) {
21632 ClientSideStrongPassword: function (pwd)
21634 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21637 ClientSideMediumPassword: function (pwd)
21639 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21642 ClientSideWeakPassword: function (pwd)
21644 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21647 })//<script type="text/javascript">
21650 * Based Ext JS Library 1.1.1
21651 * Copyright(c) 2006-2007, Ext JS, LLC.
21657 * @class Roo.HtmlEditorCore
21658 * @extends Roo.Component
21659 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21661 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21664 Roo.HtmlEditorCore = function(config){
21667 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21672 * @event initialize
21673 * Fires when the editor is fully initialized (including the iframe)
21674 * @param {Roo.HtmlEditorCore} this
21679 * Fires when the editor is first receives the focus. Any insertion must wait
21680 * until after this event.
21681 * @param {Roo.HtmlEditorCore} this
21685 * @event beforesync
21686 * Fires before the textarea is updated with content from the editor iframe. Return false
21687 * to cancel the sync.
21688 * @param {Roo.HtmlEditorCore} this
21689 * @param {String} html
21693 * @event beforepush
21694 * Fires before the iframe editor is updated with content from the textarea. Return false
21695 * to cancel the push.
21696 * @param {Roo.HtmlEditorCore} this
21697 * @param {String} html
21702 * Fires when the textarea is updated with content from the editor iframe.
21703 * @param {Roo.HtmlEditorCore} this
21704 * @param {String} html
21709 * Fires when the iframe editor is updated with content from the textarea.
21710 * @param {Roo.HtmlEditorCore} this
21711 * @param {String} html
21716 * @event editorevent
21717 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21718 * @param {Roo.HtmlEditorCore} this
21724 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21726 // defaults : white / black...
21727 this.applyBlacklists();
21734 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21738 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21744 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21749 * @cfg {Number} height (in pixels)
21753 * @cfg {Number} width (in pixels)
21758 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21761 stylesheets: false,
21766 // private properties
21767 validationEvent : false,
21769 initialized : false,
21771 sourceEditMode : false,
21772 onFocus : Roo.emptyFn,
21774 hideMode:'offsets',
21778 // blacklist + whitelisted elements..
21785 * Protected method that will not generally be called directly. It
21786 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21787 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21789 getDocMarkup : function(){
21793 // inherit styels from page...??
21794 if (this.stylesheets === false) {
21796 Roo.get(document.head).select('style').each(function(node) {
21797 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21800 Roo.get(document.head).select('link').each(function(node) {
21801 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21804 } else if (!this.stylesheets.length) {
21806 st = '<style type="text/css">' +
21807 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21810 st = '<style type="text/css">' +
21815 st += '<style type="text/css">' +
21816 'IMG { cursor: pointer } ' +
21819 var cls = 'roo-htmleditor-body';
21821 if(this.bodyCls.length){
21822 cls += ' ' + this.bodyCls;
21825 return '<html><head>' + st +
21826 //<style type="text/css">' +
21827 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21829 ' </head><body class="' + cls + '"></body></html>';
21833 onRender : function(ct, position)
21836 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21837 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21840 this.el.dom.style.border = '0 none';
21841 this.el.dom.setAttribute('tabIndex', -1);
21842 this.el.addClass('x-hidden hide');
21846 if(Roo.isIE){ // fix IE 1px bogus margin
21847 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21851 this.frameId = Roo.id();
21855 var iframe = this.owner.wrap.createChild({
21857 cls: 'form-control', // bootstrap..
21859 name: this.frameId,
21860 frameBorder : 'no',
21861 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21866 this.iframe = iframe.dom;
21868 this.assignDocWin();
21870 this.doc.designMode = 'on';
21873 this.doc.write(this.getDocMarkup());
21877 var task = { // must defer to wait for browser to be ready
21879 //console.log("run task?" + this.doc.readyState);
21880 this.assignDocWin();
21881 if(this.doc.body || this.doc.readyState == 'complete'){
21883 this.doc.designMode="on";
21887 Roo.TaskMgr.stop(task);
21888 this.initEditor.defer(10, this);
21895 Roo.TaskMgr.start(task);
21900 onResize : function(w, h)
21902 Roo.log('resize: ' +w + ',' + h );
21903 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21907 if(typeof w == 'number'){
21909 this.iframe.style.width = w + 'px';
21911 if(typeof h == 'number'){
21913 this.iframe.style.height = h + 'px';
21915 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21922 * Toggles the editor between standard and source edit mode.
21923 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21925 toggleSourceEdit : function(sourceEditMode){
21927 this.sourceEditMode = sourceEditMode === true;
21929 if(this.sourceEditMode){
21931 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21934 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21935 //this.iframe.className = '';
21938 //this.setSize(this.owner.wrap.getSize());
21939 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21946 * Protected method that will not generally be called directly. If you need/want
21947 * custom HTML cleanup, this is the method you should override.
21948 * @param {String} html The HTML to be cleaned
21949 * return {String} The cleaned HTML
21951 cleanHtml : function(html){
21952 html = String(html);
21953 if(html.length > 5){
21954 if(Roo.isSafari){ // strip safari nonsense
21955 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21958 if(html == ' '){
21965 * HTML Editor -> Textarea
21966 * Protected method that will not generally be called directly. Syncs the contents
21967 * of the editor iframe with the textarea.
21969 syncValue : function(){
21970 if(this.initialized){
21971 var bd = (this.doc.body || this.doc.documentElement);
21972 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21973 var html = bd.innerHTML;
21975 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21976 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21978 html = '<div style="'+m[0]+'">' + html + '</div>';
21981 html = this.cleanHtml(html);
21982 // fix up the special chars.. normaly like back quotes in word...
21983 // however we do not want to do this with chinese..
21984 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21985 var cc = b.charCodeAt();
21987 (cc >= 0x4E00 && cc < 0xA000 ) ||
21988 (cc >= 0x3400 && cc < 0x4E00 ) ||
21989 (cc >= 0xf900 && cc < 0xfb00 )
21995 if(this.owner.fireEvent('beforesync', this, html) !== false){
21996 this.el.dom.value = html;
21997 this.owner.fireEvent('sync', this, html);
22003 * Protected method that will not generally be called directly. Pushes the value of the textarea
22004 * into the iframe editor.
22006 pushValue : function(){
22007 if(this.initialized){
22008 var v = this.el.dom.value.trim();
22010 // if(v.length < 1){
22014 if(this.owner.fireEvent('beforepush', this, v) !== false){
22015 var d = (this.doc.body || this.doc.documentElement);
22017 this.cleanUpPaste();
22018 this.el.dom.value = d.innerHTML;
22019 this.owner.fireEvent('push', this, v);
22025 deferFocus : function(){
22026 this.focus.defer(10, this);
22030 focus : function(){
22031 if(this.win && !this.sourceEditMode){
22038 assignDocWin: function()
22040 var iframe = this.iframe;
22043 this.doc = iframe.contentWindow.document;
22044 this.win = iframe.contentWindow;
22046 // if (!Roo.get(this.frameId)) {
22049 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22050 // this.win = Roo.get(this.frameId).dom.contentWindow;
22052 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22056 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22057 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22062 initEditor : function(){
22063 //console.log("INIT EDITOR");
22064 this.assignDocWin();
22068 this.doc.designMode="on";
22070 this.doc.write(this.getDocMarkup());
22073 var dbody = (this.doc.body || this.doc.documentElement);
22074 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22075 // this copies styles from the containing element into thsi one..
22076 // not sure why we need all of this..
22077 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22079 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22080 //ss['background-attachment'] = 'fixed'; // w3c
22081 dbody.bgProperties = 'fixed'; // ie
22082 //Roo.DomHelper.applyStyles(dbody, ss);
22083 Roo.EventManager.on(this.doc, {
22084 //'mousedown': this.onEditorEvent,
22085 'mouseup': this.onEditorEvent,
22086 'dblclick': this.onEditorEvent,
22087 'click': this.onEditorEvent,
22088 'keyup': this.onEditorEvent,
22093 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22095 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22096 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22098 this.initialized = true;
22100 this.owner.fireEvent('initialize', this);
22105 onDestroy : function(){
22111 //for (var i =0; i < this.toolbars.length;i++) {
22112 // // fixme - ask toolbars for heights?
22113 // this.toolbars[i].onDestroy();
22116 //this.wrap.dom.innerHTML = '';
22117 //this.wrap.remove();
22122 onFirstFocus : function(){
22124 this.assignDocWin();
22127 this.activated = true;
22130 if(Roo.isGecko){ // prevent silly gecko errors
22132 var s = this.win.getSelection();
22133 if(!s.focusNode || s.focusNode.nodeType != 3){
22134 var r = s.getRangeAt(0);
22135 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22140 this.execCmd('useCSS', true);
22141 this.execCmd('styleWithCSS', false);
22144 this.owner.fireEvent('activate', this);
22148 adjustFont: function(btn){
22149 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22150 //if(Roo.isSafari){ // safari
22153 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22154 if(Roo.isSafari){ // safari
22155 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22156 v = (v < 10) ? 10 : v;
22157 v = (v > 48) ? 48 : v;
22158 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22163 v = Math.max(1, v+adjust);
22165 this.execCmd('FontSize', v );
22168 onEditorEvent : function(e)
22170 this.owner.fireEvent('editorevent', this, e);
22171 // this.updateToolbar();
22172 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22175 insertTag : function(tg)
22177 // could be a bit smarter... -> wrap the current selected tRoo..
22178 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22180 range = this.createRange(this.getSelection());
22181 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22182 wrappingNode.appendChild(range.extractContents());
22183 range.insertNode(wrappingNode);
22190 this.execCmd("formatblock", tg);
22194 insertText : function(txt)
22198 var range = this.createRange();
22199 range.deleteContents();
22200 //alert(Sender.getAttribute('label'));
22202 range.insertNode(this.doc.createTextNode(txt));
22208 * Executes a Midas editor command on the editor document and performs necessary focus and
22209 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22210 * @param {String} cmd The Midas command
22211 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22213 relayCmd : function(cmd, value){
22215 this.execCmd(cmd, value);
22216 this.owner.fireEvent('editorevent', this);
22217 //this.updateToolbar();
22218 this.owner.deferFocus();
22222 * Executes a Midas editor command directly on the editor document.
22223 * For visual commands, you should use {@link #relayCmd} instead.
22224 * <b>This should only be called after the editor is initialized.</b>
22225 * @param {String} cmd The Midas command
22226 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22228 execCmd : function(cmd, value){
22229 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22236 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22238 * @param {String} text | dom node..
22240 insertAtCursor : function(text)
22243 if(!this.activated){
22249 var r = this.doc.selection.createRange();
22260 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22264 // from jquery ui (MIT licenced)
22266 var win = this.win;
22268 if (win.getSelection && win.getSelection().getRangeAt) {
22269 range = win.getSelection().getRangeAt(0);
22270 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22271 range.insertNode(node);
22272 } else if (win.document.selection && win.document.selection.createRange) {
22273 // no firefox support
22274 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22275 win.document.selection.createRange().pasteHTML(txt);
22277 // no firefox support
22278 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22279 this.execCmd('InsertHTML', txt);
22288 mozKeyPress : function(e){
22290 var c = e.getCharCode(), cmd;
22293 c = String.fromCharCode(c).toLowerCase();
22307 this.cleanUpPaste.defer(100, this);
22315 e.preventDefault();
22323 fixKeys : function(){ // load time branching for fastest keydown performance
22325 return function(e){
22326 var k = e.getKey(), r;
22329 r = this.doc.selection.createRange();
22332 r.pasteHTML('    ');
22339 r = this.doc.selection.createRange();
22341 var target = r.parentElement();
22342 if(!target || target.tagName.toLowerCase() != 'li'){
22344 r.pasteHTML('<br />');
22350 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22351 this.cleanUpPaste.defer(100, this);
22357 }else if(Roo.isOpera){
22358 return function(e){
22359 var k = e.getKey();
22363 this.execCmd('InsertHTML','    ');
22366 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22367 this.cleanUpPaste.defer(100, this);
22372 }else if(Roo.isSafari){
22373 return function(e){
22374 var k = e.getKey();
22378 this.execCmd('InsertText','\t');
22382 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22383 this.cleanUpPaste.defer(100, this);
22391 getAllAncestors: function()
22393 var p = this.getSelectedNode();
22396 a.push(p); // push blank onto stack..
22397 p = this.getParentElement();
22401 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22405 a.push(this.doc.body);
22409 lastSelNode : false,
22412 getSelection : function()
22414 this.assignDocWin();
22415 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22418 getSelectedNode: function()
22420 // this may only work on Gecko!!!
22422 // should we cache this!!!!
22427 var range = this.createRange(this.getSelection()).cloneRange();
22430 var parent = range.parentElement();
22432 var testRange = range.duplicate();
22433 testRange.moveToElementText(parent);
22434 if (testRange.inRange(range)) {
22437 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22440 parent = parent.parentElement;
22445 // is ancestor a text element.
22446 var ac = range.commonAncestorContainer;
22447 if (ac.nodeType == 3) {
22448 ac = ac.parentNode;
22451 var ar = ac.childNodes;
22454 var other_nodes = [];
22455 var has_other_nodes = false;
22456 for (var i=0;i<ar.length;i++) {
22457 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22460 // fullly contained node.
22462 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22467 // probably selected..
22468 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22469 other_nodes.push(ar[i]);
22473 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22478 has_other_nodes = true;
22480 if (!nodes.length && other_nodes.length) {
22481 nodes= other_nodes;
22483 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22489 createRange: function(sel)
22491 // this has strange effects when using with
22492 // top toolbar - not sure if it's a great idea.
22493 //this.editor.contentWindow.focus();
22494 if (typeof sel != "undefined") {
22496 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22498 return this.doc.createRange();
22501 return this.doc.createRange();
22504 getParentElement: function()
22507 this.assignDocWin();
22508 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22510 var range = this.createRange(sel);
22513 var p = range.commonAncestorContainer;
22514 while (p.nodeType == 3) { // text node
22525 * Range intersection.. the hard stuff...
22529 * [ -- selected range --- ]
22533 * if end is before start or hits it. fail.
22534 * if start is after end or hits it fail.
22536 * if either hits (but other is outside. - then it's not
22542 // @see http://www.thismuchiknow.co.uk/?p=64.
22543 rangeIntersectsNode : function(range, node)
22545 var nodeRange = node.ownerDocument.createRange();
22547 nodeRange.selectNode(node);
22549 nodeRange.selectNodeContents(node);
22552 var rangeStartRange = range.cloneRange();
22553 rangeStartRange.collapse(true);
22555 var rangeEndRange = range.cloneRange();
22556 rangeEndRange.collapse(false);
22558 var nodeStartRange = nodeRange.cloneRange();
22559 nodeStartRange.collapse(true);
22561 var nodeEndRange = nodeRange.cloneRange();
22562 nodeEndRange.collapse(false);
22564 return rangeStartRange.compareBoundaryPoints(
22565 Range.START_TO_START, nodeEndRange) == -1 &&
22566 rangeEndRange.compareBoundaryPoints(
22567 Range.START_TO_START, nodeStartRange) == 1;
22571 rangeCompareNode : function(range, node)
22573 var nodeRange = node.ownerDocument.createRange();
22575 nodeRange.selectNode(node);
22577 nodeRange.selectNodeContents(node);
22581 range.collapse(true);
22583 nodeRange.collapse(true);
22585 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22586 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22588 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22590 var nodeIsBefore = ss == 1;
22591 var nodeIsAfter = ee == -1;
22593 if (nodeIsBefore && nodeIsAfter) {
22596 if (!nodeIsBefore && nodeIsAfter) {
22597 return 1; //right trailed.
22600 if (nodeIsBefore && !nodeIsAfter) {
22601 return 2; // left trailed.
22607 // private? - in a new class?
22608 cleanUpPaste : function()
22610 // cleans up the whole document..
22611 Roo.log('cleanuppaste');
22613 this.cleanUpChildren(this.doc.body);
22614 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22615 if (clean != this.doc.body.innerHTML) {
22616 this.doc.body.innerHTML = clean;
22621 cleanWordChars : function(input) {// change the chars to hex code
22622 var he = Roo.HtmlEditorCore;
22624 var output = input;
22625 Roo.each(he.swapCodes, function(sw) {
22626 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22628 output = output.replace(swapper, sw[1]);
22635 cleanUpChildren : function (n)
22637 if (!n.childNodes.length) {
22640 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22641 this.cleanUpChild(n.childNodes[i]);
22648 cleanUpChild : function (node)
22651 //console.log(node);
22652 if (node.nodeName == "#text") {
22653 // clean up silly Windows -- stuff?
22656 if (node.nodeName == "#comment") {
22657 node.parentNode.removeChild(node);
22658 // clean up silly Windows -- stuff?
22661 var lcname = node.tagName.toLowerCase();
22662 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22663 // whitelist of tags..
22665 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22667 node.parentNode.removeChild(node);
22672 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22674 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22675 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22677 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22678 // remove_keep_children = true;
22681 if (remove_keep_children) {
22682 this.cleanUpChildren(node);
22683 // inserts everything just before this node...
22684 while (node.childNodes.length) {
22685 var cn = node.childNodes[0];
22686 node.removeChild(cn);
22687 node.parentNode.insertBefore(cn, node);
22689 node.parentNode.removeChild(node);
22693 if (!node.attributes || !node.attributes.length) {
22694 this.cleanUpChildren(node);
22698 function cleanAttr(n,v)
22701 if (v.match(/^\./) || v.match(/^\//)) {
22704 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22707 if (v.match(/^#/)) {
22710 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22711 node.removeAttribute(n);
22715 var cwhite = this.cwhite;
22716 var cblack = this.cblack;
22718 function cleanStyle(n,v)
22720 if (v.match(/expression/)) { //XSS?? should we even bother..
22721 node.removeAttribute(n);
22725 var parts = v.split(/;/);
22728 Roo.each(parts, function(p) {
22729 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22733 var l = p.split(':').shift().replace(/\s+/g,'');
22734 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22736 if ( cwhite.length && cblack.indexOf(l) > -1) {
22737 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22738 //node.removeAttribute(n);
22742 // only allow 'c whitelisted system attributes'
22743 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22744 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22745 //node.removeAttribute(n);
22755 if (clean.length) {
22756 node.setAttribute(n, clean.join(';'));
22758 node.removeAttribute(n);
22764 for (var i = node.attributes.length-1; i > -1 ; i--) {
22765 var a = node.attributes[i];
22768 if (a.name.toLowerCase().substr(0,2)=='on') {
22769 node.removeAttribute(a.name);
22772 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22773 node.removeAttribute(a.name);
22776 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22777 cleanAttr(a.name,a.value); // fixme..
22780 if (a.name == 'style') {
22781 cleanStyle(a.name,a.value);
22784 /// clean up MS crap..
22785 // tecnically this should be a list of valid class'es..
22788 if (a.name == 'class') {
22789 if (a.value.match(/^Mso/)) {
22790 node.className = '';
22793 if (a.value.match(/^body$/)) {
22794 node.className = '';
22805 this.cleanUpChildren(node);
22811 * Clean up MS wordisms...
22813 cleanWord : function(node)
22818 this.cleanWord(this.doc.body);
22821 if (node.nodeName == "#text") {
22822 // clean up silly Windows -- stuff?
22825 if (node.nodeName == "#comment") {
22826 node.parentNode.removeChild(node);
22827 // clean up silly Windows -- stuff?
22831 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22832 node.parentNode.removeChild(node);
22836 // remove - but keep children..
22837 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22838 while (node.childNodes.length) {
22839 var cn = node.childNodes[0];
22840 node.removeChild(cn);
22841 node.parentNode.insertBefore(cn, node);
22843 node.parentNode.removeChild(node);
22844 this.iterateChildren(node, this.cleanWord);
22848 if (node.className.length) {
22850 var cn = node.className.split(/\W+/);
22852 Roo.each(cn, function(cls) {
22853 if (cls.match(/Mso[a-zA-Z]+/)) {
22858 node.className = cna.length ? cna.join(' ') : '';
22860 node.removeAttribute("class");
22864 if (node.hasAttribute("lang")) {
22865 node.removeAttribute("lang");
22868 if (node.hasAttribute("style")) {
22870 var styles = node.getAttribute("style").split(";");
22872 Roo.each(styles, function(s) {
22873 if (!s.match(/:/)) {
22876 var kv = s.split(":");
22877 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22880 // what ever is left... we allow.
22883 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22884 if (!nstyle.length) {
22885 node.removeAttribute('style');
22888 this.iterateChildren(node, this.cleanWord);
22894 * iterateChildren of a Node, calling fn each time, using this as the scole..
22895 * @param {DomNode} node node to iterate children of.
22896 * @param {Function} fn method of this class to call on each item.
22898 iterateChildren : function(node, fn)
22900 if (!node.childNodes.length) {
22903 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22904 fn.call(this, node.childNodes[i])
22910 * cleanTableWidths.
22912 * Quite often pasting from word etc.. results in tables with column and widths.
22913 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22916 cleanTableWidths : function(node)
22921 this.cleanTableWidths(this.doc.body);
22926 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22929 Roo.log(node.tagName);
22930 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22931 this.iterateChildren(node, this.cleanTableWidths);
22934 if (node.hasAttribute('width')) {
22935 node.removeAttribute('width');
22939 if (node.hasAttribute("style")) {
22942 var styles = node.getAttribute("style").split(";");
22944 Roo.each(styles, function(s) {
22945 if (!s.match(/:/)) {
22948 var kv = s.split(":");
22949 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22952 // what ever is left... we allow.
22955 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22956 if (!nstyle.length) {
22957 node.removeAttribute('style');
22961 this.iterateChildren(node, this.cleanTableWidths);
22969 domToHTML : function(currentElement, depth, nopadtext) {
22971 depth = depth || 0;
22972 nopadtext = nopadtext || false;
22974 if (!currentElement) {
22975 return this.domToHTML(this.doc.body);
22978 //Roo.log(currentElement);
22980 var allText = false;
22981 var nodeName = currentElement.nodeName;
22982 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22984 if (nodeName == '#text') {
22986 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22991 if (nodeName != 'BODY') {
22994 // Prints the node tagName, such as <A>, <IMG>, etc
22997 for(i = 0; i < currentElement.attributes.length;i++) {
22999 var aname = currentElement.attributes.item(i).name;
23000 if (!currentElement.attributes.item(i).value.length) {
23003 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23006 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23015 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23018 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23023 // Traverse the tree
23025 var currentElementChild = currentElement.childNodes.item(i);
23026 var allText = true;
23027 var innerHTML = '';
23029 while (currentElementChild) {
23030 // Formatting code (indent the tree so it looks nice on the screen)
23031 var nopad = nopadtext;
23032 if (lastnode == 'SPAN') {
23036 if (currentElementChild.nodeName == '#text') {
23037 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23038 toadd = nopadtext ? toadd : toadd.trim();
23039 if (!nopad && toadd.length > 80) {
23040 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23042 innerHTML += toadd;
23045 currentElementChild = currentElement.childNodes.item(i);
23051 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23053 // Recursively traverse the tree structure of the child node
23054 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23055 lastnode = currentElementChild.nodeName;
23057 currentElementChild=currentElement.childNodes.item(i);
23063 // The remaining code is mostly for formatting the tree
23064 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23069 ret+= "</"+tagName+">";
23075 applyBlacklists : function()
23077 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23078 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23082 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23083 if (b.indexOf(tag) > -1) {
23086 this.white.push(tag);
23090 Roo.each(w, function(tag) {
23091 if (b.indexOf(tag) > -1) {
23094 if (this.white.indexOf(tag) > -1) {
23097 this.white.push(tag);
23102 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23103 if (w.indexOf(tag) > -1) {
23106 this.black.push(tag);
23110 Roo.each(b, function(tag) {
23111 if (w.indexOf(tag) > -1) {
23114 if (this.black.indexOf(tag) > -1) {
23117 this.black.push(tag);
23122 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23123 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23127 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23128 if (b.indexOf(tag) > -1) {
23131 this.cwhite.push(tag);
23135 Roo.each(w, function(tag) {
23136 if (b.indexOf(tag) > -1) {
23139 if (this.cwhite.indexOf(tag) > -1) {
23142 this.cwhite.push(tag);
23147 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23148 if (w.indexOf(tag) > -1) {
23151 this.cblack.push(tag);
23155 Roo.each(b, function(tag) {
23156 if (w.indexOf(tag) > -1) {
23159 if (this.cblack.indexOf(tag) > -1) {
23162 this.cblack.push(tag);
23167 setStylesheets : function(stylesheets)
23169 if(typeof(stylesheets) == 'string'){
23170 Roo.get(this.iframe.contentDocument.head).createChild({
23172 rel : 'stylesheet',
23181 Roo.each(stylesheets, function(s) {
23186 Roo.get(_this.iframe.contentDocument.head).createChild({
23188 rel : 'stylesheet',
23197 removeStylesheets : function()
23201 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23206 setStyle : function(style)
23208 Roo.get(this.iframe.contentDocument.head).createChild({
23217 // hide stuff that is not compatible
23231 * @event specialkey
23235 * @cfg {String} fieldClass @hide
23238 * @cfg {String} focusClass @hide
23241 * @cfg {String} autoCreate @hide
23244 * @cfg {String} inputType @hide
23247 * @cfg {String} invalidClass @hide
23250 * @cfg {String} invalidText @hide
23253 * @cfg {String} msgFx @hide
23256 * @cfg {String} validateOnBlur @hide
23260 Roo.HtmlEditorCore.white = [
23261 'area', 'br', 'img', 'input', 'hr', 'wbr',
23263 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23264 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23265 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23266 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23267 'table', 'ul', 'xmp',
23269 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23272 'dir', 'menu', 'ol', 'ul', 'dl',
23278 Roo.HtmlEditorCore.black = [
23279 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23281 'base', 'basefont', 'bgsound', 'blink', 'body',
23282 'frame', 'frameset', 'head', 'html', 'ilayer',
23283 'iframe', 'layer', 'link', 'meta', 'object',
23284 'script', 'style' ,'title', 'xml' // clean later..
23286 Roo.HtmlEditorCore.clean = [
23287 'script', 'style', 'title', 'xml'
23289 Roo.HtmlEditorCore.remove = [
23294 Roo.HtmlEditorCore.ablack = [
23298 Roo.HtmlEditorCore.aclean = [
23299 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23303 Roo.HtmlEditorCore.pwhite= [
23304 'http', 'https', 'mailto'
23307 // white listed style attributes.
23308 Roo.HtmlEditorCore.cwhite= [
23309 // 'text-align', /// default is to allow most things..
23315 // black listed style attributes.
23316 Roo.HtmlEditorCore.cblack= [
23317 // 'font-size' -- this can be set by the project
23321 Roo.HtmlEditorCore.swapCodes =[
23340 * @class Roo.bootstrap.HtmlEditor
23341 * @extends Roo.bootstrap.TextArea
23342 * Bootstrap HtmlEditor class
23345 * Create a new HtmlEditor
23346 * @param {Object} config The config object
23349 Roo.bootstrap.HtmlEditor = function(config){
23350 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23351 if (!this.toolbars) {
23352 this.toolbars = [];
23355 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23358 * @event initialize
23359 * Fires when the editor is fully initialized (including the iframe)
23360 * @param {HtmlEditor} this
23365 * Fires when the editor is first receives the focus. Any insertion must wait
23366 * until after this event.
23367 * @param {HtmlEditor} this
23371 * @event beforesync
23372 * Fires before the textarea is updated with content from the editor iframe. Return false
23373 * to cancel the sync.
23374 * @param {HtmlEditor} this
23375 * @param {String} html
23379 * @event beforepush
23380 * Fires before the iframe editor is updated with content from the textarea. Return false
23381 * to cancel the push.
23382 * @param {HtmlEditor} this
23383 * @param {String} html
23388 * Fires when the textarea is updated with content from the editor iframe.
23389 * @param {HtmlEditor} this
23390 * @param {String} html
23395 * Fires when the iframe editor is updated with content from the textarea.
23396 * @param {HtmlEditor} this
23397 * @param {String} html
23401 * @event editmodechange
23402 * Fires when the editor switches edit modes
23403 * @param {HtmlEditor} this
23404 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23406 editmodechange: true,
23408 * @event editorevent
23409 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23410 * @param {HtmlEditor} this
23414 * @event firstfocus
23415 * Fires when on first focus - needed by toolbars..
23416 * @param {HtmlEditor} this
23421 * Auto save the htmlEditor value as a file into Events
23422 * @param {HtmlEditor} this
23426 * @event savedpreview
23427 * preview the saved version of htmlEditor
23428 * @param {HtmlEditor} this
23435 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23439 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23444 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23449 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23454 * @cfg {Number} height (in pixels)
23458 * @cfg {Number} width (in pixels)
23463 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23466 stylesheets: false,
23471 // private properties
23472 validationEvent : false,
23474 initialized : false,
23477 onFocus : Roo.emptyFn,
23479 hideMode:'offsets',
23481 tbContainer : false,
23485 toolbarContainer :function() {
23486 return this.wrap.select('.x-html-editor-tb',true).first();
23490 * Protected method that will not generally be called directly. It
23491 * is called when the editor creates its toolbar. Override this method if you need to
23492 * add custom toolbar buttons.
23493 * @param {HtmlEditor} editor
23495 createToolbar : function(){
23496 Roo.log('renewing');
23497 Roo.log("create toolbars");
23499 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23500 this.toolbars[0].render(this.toolbarContainer());
23504 // if (!editor.toolbars || !editor.toolbars.length) {
23505 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23508 // for (var i =0 ; i < editor.toolbars.length;i++) {
23509 // editor.toolbars[i] = Roo.factory(
23510 // typeof(editor.toolbars[i]) == 'string' ?
23511 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23512 // Roo.bootstrap.HtmlEditor);
23513 // editor.toolbars[i].init(editor);
23519 onRender : function(ct, position)
23521 // Roo.log("Call onRender: " + this.xtype);
23523 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23525 this.wrap = this.inputEl().wrap({
23526 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23529 this.editorcore.onRender(ct, position);
23531 if (this.resizable) {
23532 this.resizeEl = new Roo.Resizable(this.wrap, {
23536 minHeight : this.height,
23537 height: this.height,
23538 handles : this.resizable,
23541 resize : function(r, w, h) {
23542 _t.onResize(w,h); // -something
23548 this.createToolbar(this);
23551 if(!this.width && this.resizable){
23552 this.setSize(this.wrap.getSize());
23554 if (this.resizeEl) {
23555 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23556 // should trigger onReize..
23562 onResize : function(w, h)
23564 Roo.log('resize: ' +w + ',' + h );
23565 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23569 if(this.inputEl() ){
23570 if(typeof w == 'number'){
23571 var aw = w - this.wrap.getFrameWidth('lr');
23572 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23575 if(typeof h == 'number'){
23576 var tbh = -11; // fixme it needs to tool bar size!
23577 for (var i =0; i < this.toolbars.length;i++) {
23578 // fixme - ask toolbars for heights?
23579 tbh += this.toolbars[i].el.getHeight();
23580 //if (this.toolbars[i].footer) {
23581 // tbh += this.toolbars[i].footer.el.getHeight();
23589 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23590 ah -= 5; // knock a few pixes off for look..
23591 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23595 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23596 this.editorcore.onResize(ew,eh);
23601 * Toggles the editor between standard and source edit mode.
23602 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23604 toggleSourceEdit : function(sourceEditMode)
23606 this.editorcore.toggleSourceEdit(sourceEditMode);
23608 if(this.editorcore.sourceEditMode){
23609 Roo.log('editor - showing textarea');
23612 // Roo.log(this.syncValue());
23614 this.inputEl().removeClass(['hide', 'x-hidden']);
23615 this.inputEl().dom.removeAttribute('tabIndex');
23616 this.inputEl().focus();
23618 Roo.log('editor - hiding textarea');
23620 // Roo.log(this.pushValue());
23623 this.inputEl().addClass(['hide', 'x-hidden']);
23624 this.inputEl().dom.setAttribute('tabIndex', -1);
23625 //this.deferFocus();
23628 if(this.resizable){
23629 this.setSize(this.wrap.getSize());
23632 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23635 // private (for BoxComponent)
23636 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23638 // private (for BoxComponent)
23639 getResizeEl : function(){
23643 // private (for BoxComponent)
23644 getPositionEl : function(){
23649 initEvents : function(){
23650 this.originalValue = this.getValue();
23654 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23657 // markInvalid : Roo.emptyFn,
23659 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23662 // clearInvalid : Roo.emptyFn,
23664 setValue : function(v){
23665 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23666 this.editorcore.pushValue();
23671 deferFocus : function(){
23672 this.focus.defer(10, this);
23676 focus : function(){
23677 this.editorcore.focus();
23683 onDestroy : function(){
23689 for (var i =0; i < this.toolbars.length;i++) {
23690 // fixme - ask toolbars for heights?
23691 this.toolbars[i].onDestroy();
23694 this.wrap.dom.innerHTML = '';
23695 this.wrap.remove();
23700 onFirstFocus : function(){
23701 //Roo.log("onFirstFocus");
23702 this.editorcore.onFirstFocus();
23703 for (var i =0; i < this.toolbars.length;i++) {
23704 this.toolbars[i].onFirstFocus();
23710 syncValue : function()
23712 this.editorcore.syncValue();
23715 pushValue : function()
23717 this.editorcore.pushValue();
23721 // hide stuff that is not compatible
23735 * @event specialkey
23739 * @cfg {String} fieldClass @hide
23742 * @cfg {String} focusClass @hide
23745 * @cfg {String} autoCreate @hide
23748 * @cfg {String} inputType @hide
23751 * @cfg {String} invalidClass @hide
23754 * @cfg {String} invalidText @hide
23757 * @cfg {String} msgFx @hide
23760 * @cfg {String} validateOnBlur @hide
23769 Roo.namespace('Roo.bootstrap.htmleditor');
23771 * @class Roo.bootstrap.HtmlEditorToolbar1
23776 new Roo.bootstrap.HtmlEditor({
23779 new Roo.bootstrap.HtmlEditorToolbar1({
23780 disable : { fonts: 1 , format: 1, ..., ... , ...],
23786 * @cfg {Object} disable List of elements to disable..
23787 * @cfg {Array} btns List of additional buttons.
23791 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23794 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23797 Roo.apply(this, config);
23799 // default disabled, based on 'good practice'..
23800 this.disable = this.disable || {};
23801 Roo.applyIf(this.disable, {
23804 specialElements : true
23806 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23808 this.editor = config.editor;
23809 this.editorcore = config.editor.editorcore;
23811 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23813 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23814 // dont call parent... till later.
23816 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23821 editorcore : false,
23826 "h1","h2","h3","h4","h5","h6",
23828 "abbr", "acronym", "address", "cite", "samp", "var",
23832 onRender : function(ct, position)
23834 // Roo.log("Call onRender: " + this.xtype);
23836 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23838 this.el.dom.style.marginBottom = '0';
23840 var editorcore = this.editorcore;
23841 var editor= this.editor;
23844 var btn = function(id,cmd , toggle, handler, html){
23846 var event = toggle ? 'toggle' : 'click';
23851 xns: Roo.bootstrap,
23854 enableToggle:toggle !== false,
23856 pressed : toggle ? false : null,
23859 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23860 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23866 // var cb_box = function...
23871 xns: Roo.bootstrap,
23872 glyphicon : 'font',
23876 xns: Roo.bootstrap,
23880 Roo.each(this.formats, function(f) {
23881 style.menu.items.push({
23883 xns: Roo.bootstrap,
23884 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23889 editorcore.insertTag(this.tagname);
23896 children.push(style);
23898 btn('bold',false,true);
23899 btn('italic',false,true);
23900 btn('align-left', 'justifyleft',true);
23901 btn('align-center', 'justifycenter',true);
23902 btn('align-right' , 'justifyright',true);
23903 btn('link', false, false, function(btn) {
23904 //Roo.log("create link?");
23905 var url = prompt(this.createLinkText, this.defaultLinkValue);
23906 if(url && url != 'http:/'+'/'){
23907 this.editorcore.relayCmd('createlink', url);
23910 btn('list','insertunorderedlist',true);
23911 btn('pencil', false,true, function(btn){
23913 this.toggleSourceEdit(btn.pressed);
23916 if (this.editor.btns.length > 0) {
23917 for (var i = 0; i<this.editor.btns.length; i++) {
23918 children.push(this.editor.btns[i]);
23926 xns: Roo.bootstrap,
23931 xns: Roo.bootstrap,
23936 cog.menu.items.push({
23938 xns: Roo.bootstrap,
23939 html : Clean styles,
23944 editorcore.insertTag(this.tagname);
23953 this.xtype = 'NavSimplebar';
23955 for(var i=0;i< children.length;i++) {
23957 this.buttons.add(this.addxtypeChild(children[i]));
23961 editor.on('editorevent', this.updateToolbar, this);
23963 onBtnClick : function(id)
23965 this.editorcore.relayCmd(id);
23966 this.editorcore.focus();
23970 * Protected method that will not generally be called directly. It triggers
23971 * a toolbar update by reading the markup state of the current selection in the editor.
23973 updateToolbar: function(){
23975 if(!this.editorcore.activated){
23976 this.editor.onFirstFocus(); // is this neeed?
23980 var btns = this.buttons;
23981 var doc = this.editorcore.doc;
23982 btns.get('bold').setActive(doc.queryCommandState('bold'));
23983 btns.get('italic').setActive(doc.queryCommandState('italic'));
23984 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23986 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23987 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23988 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23990 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23991 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23994 var ans = this.editorcore.getAllAncestors();
23995 if (this.formatCombo) {
23998 var store = this.formatCombo.store;
23999 this.formatCombo.setValue("");
24000 for (var i =0; i < ans.length;i++) {
24001 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24003 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24011 // hides menus... - so this cant be on a menu...
24012 Roo.bootstrap.MenuMgr.hideAll();
24014 Roo.bootstrap.MenuMgr.hideAll();
24015 //this.editorsyncValue();
24017 onFirstFocus: function() {
24018 this.buttons.each(function(item){
24022 toggleSourceEdit : function(sourceEditMode){
24025 if(sourceEditMode){
24026 Roo.log("disabling buttons");
24027 this.buttons.each( function(item){
24028 if(item.cmd != 'pencil'){
24034 Roo.log("enabling buttons");
24035 if(this.editorcore.initialized){
24036 this.buttons.each( function(item){
24042 Roo.log("calling toggole on editor");
24043 // tell the editor that it's been pressed..
24044 this.editor.toggleSourceEdit(sourceEditMode);
24054 * @class Roo.bootstrap.Table.AbstractSelectionModel
24055 * @extends Roo.util.Observable
24056 * Abstract base class for grid SelectionModels. It provides the interface that should be
24057 * implemented by descendant classes. This class should not be directly instantiated.
24060 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24061 this.locked = false;
24062 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24066 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24067 /** @ignore Called by the grid automatically. Do not call directly. */
24068 init : function(grid){
24074 * Locks the selections.
24077 this.locked = true;
24081 * Unlocks the selections.
24083 unlock : function(){
24084 this.locked = false;
24088 * Returns true if the selections are locked.
24089 * @return {Boolean}
24091 isLocked : function(){
24092 return this.locked;
24096 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24097 * @class Roo.bootstrap.Table.RowSelectionModel
24098 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24099 * It supports multiple selections and keyboard selection/navigation.
24101 * @param {Object} config
24104 Roo.bootstrap.Table.RowSelectionModel = function(config){
24105 Roo.apply(this, config);
24106 this.selections = new Roo.util.MixedCollection(false, function(o){
24111 this.lastActive = false;
24115 * @event selectionchange
24116 * Fires when the selection changes
24117 * @param {SelectionModel} this
24119 "selectionchange" : true,
24121 * @event afterselectionchange
24122 * Fires after the selection changes (eg. by key press or clicking)
24123 * @param {SelectionModel} this
24125 "afterselectionchange" : true,
24127 * @event beforerowselect
24128 * Fires when a row is selected being selected, return false to cancel.
24129 * @param {SelectionModel} this
24130 * @param {Number} rowIndex The selected index
24131 * @param {Boolean} keepExisting False if other selections will be cleared
24133 "beforerowselect" : true,
24136 * Fires when a row is selected.
24137 * @param {SelectionModel} this
24138 * @param {Number} rowIndex The selected index
24139 * @param {Roo.data.Record} r The record
24141 "rowselect" : true,
24143 * @event rowdeselect
24144 * Fires when a row is deselected.
24145 * @param {SelectionModel} this
24146 * @param {Number} rowIndex The selected index
24148 "rowdeselect" : true
24150 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24151 this.locked = false;
24154 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24156 * @cfg {Boolean} singleSelect
24157 * True to allow selection of only one row at a time (defaults to false)
24159 singleSelect : false,
24162 initEvents : function()
24165 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24166 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24167 //}else{ // allow click to work like normal
24168 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24170 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24171 this.grid.on("rowclick", this.handleMouseDown, this);
24173 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24174 "up" : function(e){
24176 this.selectPrevious(e.shiftKey);
24177 }else if(this.last !== false && this.lastActive !== false){
24178 var last = this.last;
24179 this.selectRange(this.last, this.lastActive-1);
24180 this.grid.getView().focusRow(this.lastActive);
24181 if(last !== false){
24185 this.selectFirstRow();
24187 this.fireEvent("afterselectionchange", this);
24189 "down" : function(e){
24191 this.selectNext(e.shiftKey);
24192 }else if(this.last !== false && this.lastActive !== false){
24193 var last = this.last;
24194 this.selectRange(this.last, this.lastActive+1);
24195 this.grid.getView().focusRow(this.lastActive);
24196 if(last !== false){
24200 this.selectFirstRow();
24202 this.fireEvent("afterselectionchange", this);
24206 this.grid.store.on('load', function(){
24207 this.selections.clear();
24210 var view = this.grid.view;
24211 view.on("refresh", this.onRefresh, this);
24212 view.on("rowupdated", this.onRowUpdated, this);
24213 view.on("rowremoved", this.onRemove, this);
24218 onRefresh : function()
24220 var ds = this.grid.store, i, v = this.grid.view;
24221 var s = this.selections;
24222 s.each(function(r){
24223 if((i = ds.indexOfId(r.id)) != -1){
24232 onRemove : function(v, index, r){
24233 this.selections.remove(r);
24237 onRowUpdated : function(v, index, r){
24238 if(this.isSelected(r)){
24239 v.onRowSelect(index);
24245 * @param {Array} records The records to select
24246 * @param {Boolean} keepExisting (optional) True to keep existing selections
24248 selectRecords : function(records, keepExisting)
24251 this.clearSelections();
24253 var ds = this.grid.store;
24254 for(var i = 0, len = records.length; i < len; i++){
24255 this.selectRow(ds.indexOf(records[i]), true);
24260 * Gets the number of selected rows.
24263 getCount : function(){
24264 return this.selections.length;
24268 * Selects the first row in the grid.
24270 selectFirstRow : function(){
24275 * Select the last row.
24276 * @param {Boolean} keepExisting (optional) True to keep existing selections
24278 selectLastRow : function(keepExisting){
24279 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24280 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24284 * Selects the row immediately following the last selected row.
24285 * @param {Boolean} keepExisting (optional) True to keep existing selections
24287 selectNext : function(keepExisting)
24289 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24290 this.selectRow(this.last+1, keepExisting);
24291 this.grid.getView().focusRow(this.last);
24296 * Selects the row that precedes the last selected row.
24297 * @param {Boolean} keepExisting (optional) True to keep existing selections
24299 selectPrevious : function(keepExisting){
24301 this.selectRow(this.last-1, keepExisting);
24302 this.grid.getView().focusRow(this.last);
24307 * Returns the selected records
24308 * @return {Array} Array of selected records
24310 getSelections : function(){
24311 return [].concat(this.selections.items);
24315 * Returns the first selected record.
24318 getSelected : function(){
24319 return this.selections.itemAt(0);
24324 * Clears all selections.
24326 clearSelections : function(fast)
24332 var ds = this.grid.store;
24333 var s = this.selections;
24334 s.each(function(r){
24335 this.deselectRow(ds.indexOfId(r.id));
24339 this.selections.clear();
24346 * Selects all rows.
24348 selectAll : function(){
24352 this.selections.clear();
24353 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24354 this.selectRow(i, true);
24359 * Returns True if there is a selection.
24360 * @return {Boolean}
24362 hasSelection : function(){
24363 return this.selections.length > 0;
24367 * Returns True if the specified row is selected.
24368 * @param {Number/Record} record The record or index of the record to check
24369 * @return {Boolean}
24371 isSelected : function(index){
24372 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24373 return (r && this.selections.key(r.id) ? true : false);
24377 * Returns True if the specified record id is selected.
24378 * @param {String} id The id of record to check
24379 * @return {Boolean}
24381 isIdSelected : function(id){
24382 return (this.selections.key(id) ? true : false);
24387 handleMouseDBClick : function(e, t){
24391 handleMouseDown : function(e, t)
24393 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24394 if(this.isLocked() || rowIndex < 0 ){
24397 if(e.shiftKey && this.last !== false){
24398 var last = this.last;
24399 this.selectRange(last, rowIndex, e.ctrlKey);
24400 this.last = last; // reset the last
24404 var isSelected = this.isSelected(rowIndex);
24405 //Roo.log("select row:" + rowIndex);
24407 this.deselectRow(rowIndex);
24409 this.selectRow(rowIndex, true);
24413 if(e.button !== 0 && isSelected){
24414 alert('rowIndex 2: ' + rowIndex);
24415 view.focusRow(rowIndex);
24416 }else if(e.ctrlKey && isSelected){
24417 this.deselectRow(rowIndex);
24418 }else if(!isSelected){
24419 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24420 view.focusRow(rowIndex);
24424 this.fireEvent("afterselectionchange", this);
24427 handleDragableRowClick : function(grid, rowIndex, e)
24429 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24430 this.selectRow(rowIndex, false);
24431 grid.view.focusRow(rowIndex);
24432 this.fireEvent("afterselectionchange", this);
24437 * Selects multiple rows.
24438 * @param {Array} rows Array of the indexes of the row to select
24439 * @param {Boolean} keepExisting (optional) True to keep existing selections
24441 selectRows : function(rows, keepExisting){
24443 this.clearSelections();
24445 for(var i = 0, len = rows.length; i < len; i++){
24446 this.selectRow(rows[i], true);
24451 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24452 * @param {Number} startRow The index of the first row in the range
24453 * @param {Number} endRow The index of the last row in the range
24454 * @param {Boolean} keepExisting (optional) True to retain existing selections
24456 selectRange : function(startRow, endRow, keepExisting){
24461 this.clearSelections();
24463 if(startRow <= endRow){
24464 for(var i = startRow; i <= endRow; i++){
24465 this.selectRow(i, true);
24468 for(var i = startRow; i >= endRow; i--){
24469 this.selectRow(i, true);
24475 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24476 * @param {Number} startRow The index of the first row in the range
24477 * @param {Number} endRow The index of the last row in the range
24479 deselectRange : function(startRow, endRow, preventViewNotify){
24483 for(var i = startRow; i <= endRow; i++){
24484 this.deselectRow(i, preventViewNotify);
24490 * @param {Number} row The index of the row to select
24491 * @param {Boolean} keepExisting (optional) True to keep existing selections
24493 selectRow : function(index, keepExisting, preventViewNotify)
24495 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24498 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24499 if(!keepExisting || this.singleSelect){
24500 this.clearSelections();
24503 var r = this.grid.store.getAt(index);
24504 //console.log('selectRow - record id :' + r.id);
24506 this.selections.add(r);
24507 this.last = this.lastActive = index;
24508 if(!preventViewNotify){
24509 var proxy = new Roo.Element(
24510 this.grid.getRowDom(index)
24512 proxy.addClass('bg-info info');
24514 this.fireEvent("rowselect", this, index, r);
24515 this.fireEvent("selectionchange", this);
24521 * @param {Number} row The index of the row to deselect
24523 deselectRow : function(index, preventViewNotify)
24528 if(this.last == index){
24531 if(this.lastActive == index){
24532 this.lastActive = false;
24535 var r = this.grid.store.getAt(index);
24540 this.selections.remove(r);
24541 //.console.log('deselectRow - record id :' + r.id);
24542 if(!preventViewNotify){
24544 var proxy = new Roo.Element(
24545 this.grid.getRowDom(index)
24547 proxy.removeClass('bg-info info');
24549 this.fireEvent("rowdeselect", this, index);
24550 this.fireEvent("selectionchange", this);
24554 restoreLast : function(){
24556 this.last = this._last;
24561 acceptsNav : function(row, col, cm){
24562 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24566 onEditorKey : function(field, e){
24567 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24572 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24574 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24576 }else if(k == e.ENTER && !e.ctrlKey){
24580 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24582 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24584 }else if(k == e.ESC){
24588 g.startEditing(newCell[0], newCell[1]);
24594 * Ext JS Library 1.1.1
24595 * Copyright(c) 2006-2007, Ext JS, LLC.
24597 * Originally Released Under LGPL - original licence link has changed is not relivant.
24600 * <script type="text/javascript">
24604 * @class Roo.bootstrap.PagingToolbar
24605 * @extends Roo.bootstrap.NavSimplebar
24606 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24608 * Create a new PagingToolbar
24609 * @param {Object} config The config object
24610 * @param {Roo.data.Store} store
24612 Roo.bootstrap.PagingToolbar = function(config)
24614 // old args format still supported... - xtype is prefered..
24615 // created from xtype...
24617 this.ds = config.dataSource;
24619 if (config.store && !this.ds) {
24620 this.store= Roo.factory(config.store, Roo.data);
24621 this.ds = this.store;
24622 this.ds.xmodule = this.xmodule || false;
24625 this.toolbarItems = [];
24626 if (config.items) {
24627 this.toolbarItems = config.items;
24630 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24635 this.bind(this.ds);
24638 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24642 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24644 * @cfg {Roo.data.Store} dataSource
24645 * The underlying data store providing the paged data
24648 * @cfg {String/HTMLElement/Element} container
24649 * container The id or element that will contain the toolbar
24652 * @cfg {Boolean} displayInfo
24653 * True to display the displayMsg (defaults to false)
24656 * @cfg {Number} pageSize
24657 * The number of records to display per page (defaults to 20)
24661 * @cfg {String} displayMsg
24662 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24664 displayMsg : 'Displaying {0} - {1} of {2}',
24666 * @cfg {String} emptyMsg
24667 * The message to display when no records are found (defaults to "No data to display")
24669 emptyMsg : 'No data to display',
24671 * Customizable piece of the default paging text (defaults to "Page")
24674 beforePageText : "Page",
24676 * Customizable piece of the default paging text (defaults to "of %0")
24679 afterPageText : "of {0}",
24681 * Customizable piece of the default paging text (defaults to "First Page")
24684 firstText : "First Page",
24686 * Customizable piece of the default paging text (defaults to "Previous Page")
24689 prevText : "Previous Page",
24691 * Customizable piece of the default paging text (defaults to "Next Page")
24694 nextText : "Next Page",
24696 * Customizable piece of the default paging text (defaults to "Last Page")
24699 lastText : "Last Page",
24701 * Customizable piece of the default paging text (defaults to "Refresh")
24704 refreshText : "Refresh",
24708 onRender : function(ct, position)
24710 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24711 this.navgroup.parentId = this.id;
24712 this.navgroup.onRender(this.el, null);
24713 // add the buttons to the navgroup
24715 if(this.displayInfo){
24716 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24717 this.displayEl = this.el.select('.x-paging-info', true).first();
24718 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24719 // this.displayEl = navel.el.select('span',true).first();
24725 Roo.each(_this.buttons, function(e){ // this might need to use render????
24726 Roo.factory(e).render(_this.el);
24730 Roo.each(_this.toolbarItems, function(e) {
24731 _this.navgroup.addItem(e);
24735 this.first = this.navgroup.addItem({
24736 tooltip: this.firstText,
24738 icon : 'fa fa-step-backward',
24740 preventDefault: true,
24741 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24744 this.prev = this.navgroup.addItem({
24745 tooltip: this.prevText,
24747 icon : 'fa fa-backward',
24749 preventDefault: true,
24750 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24752 //this.addSeparator();
24755 var field = this.navgroup.addItem( {
24757 cls : 'x-paging-position',
24759 html : this.beforePageText +
24760 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24761 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24764 this.field = field.el.select('input', true).first();
24765 this.field.on("keydown", this.onPagingKeydown, this);
24766 this.field.on("focus", function(){this.dom.select();});
24769 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24770 //this.field.setHeight(18);
24771 //this.addSeparator();
24772 this.next = this.navgroup.addItem({
24773 tooltip: this.nextText,
24775 html : ' <i class="fa fa-forward">',
24777 preventDefault: true,
24778 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24780 this.last = this.navgroup.addItem({
24781 tooltip: this.lastText,
24782 icon : 'fa fa-step-forward',
24785 preventDefault: true,
24786 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24788 //this.addSeparator();
24789 this.loading = this.navgroup.addItem({
24790 tooltip: this.refreshText,
24791 icon: 'fa fa-refresh',
24792 preventDefault: true,
24793 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24799 updateInfo : function(){
24800 if(this.displayEl){
24801 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24802 var msg = count == 0 ?
24806 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24808 this.displayEl.update(msg);
24813 onLoad : function(ds, r, o)
24815 this.cursor = o.params.start ? o.params.start : 0;
24817 var d = this.getPageData(),
24822 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24823 this.field.dom.value = ap;
24824 this.first.setDisabled(ap == 1);
24825 this.prev.setDisabled(ap == 1);
24826 this.next.setDisabled(ap == ps);
24827 this.last.setDisabled(ap == ps);
24828 this.loading.enable();
24833 getPageData : function(){
24834 var total = this.ds.getTotalCount();
24837 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24838 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24843 onLoadError : function(){
24844 this.loading.enable();
24848 onPagingKeydown : function(e){
24849 var k = e.getKey();
24850 var d = this.getPageData();
24852 var v = this.field.dom.value, pageNum;
24853 if(!v || isNaN(pageNum = parseInt(v, 10))){
24854 this.field.dom.value = d.activePage;
24857 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24858 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24861 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))
24863 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24864 this.field.dom.value = pageNum;
24865 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24868 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24870 var v = this.field.dom.value, pageNum;
24871 var increment = (e.shiftKey) ? 10 : 1;
24872 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24875 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24876 this.field.dom.value = d.activePage;
24879 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24881 this.field.dom.value = parseInt(v, 10) + increment;
24882 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24883 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24890 beforeLoad : function(){
24892 this.loading.disable();
24897 onClick : function(which){
24906 ds.load({params:{start: 0, limit: this.pageSize}});
24909 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24912 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24915 var total = ds.getTotalCount();
24916 var extra = total % this.pageSize;
24917 var lastStart = extra ? (total - extra) : total-this.pageSize;
24918 ds.load({params:{start: lastStart, limit: this.pageSize}});
24921 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24927 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24928 * @param {Roo.data.Store} store The data store to unbind
24930 unbind : function(ds){
24931 ds.un("beforeload", this.beforeLoad, this);
24932 ds.un("load", this.onLoad, this);
24933 ds.un("loadexception", this.onLoadError, this);
24934 ds.un("remove", this.updateInfo, this);
24935 ds.un("add", this.updateInfo, this);
24936 this.ds = undefined;
24940 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24941 * @param {Roo.data.Store} store The data store to bind
24943 bind : function(ds){
24944 ds.on("beforeload", this.beforeLoad, this);
24945 ds.on("load", this.onLoad, this);
24946 ds.on("loadexception", this.onLoadError, this);
24947 ds.on("remove", this.updateInfo, this);
24948 ds.on("add", this.updateInfo, this);
24959 * @class Roo.bootstrap.MessageBar
24960 * @extends Roo.bootstrap.Component
24961 * Bootstrap MessageBar class
24962 * @cfg {String} html contents of the MessageBar
24963 * @cfg {String} weight (info | success | warning | danger) default info
24964 * @cfg {String} beforeClass insert the bar before the given class
24965 * @cfg {Boolean} closable (true | false) default false
24966 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24969 * Create a new Element
24970 * @param {Object} config The config object
24973 Roo.bootstrap.MessageBar = function(config){
24974 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24977 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24983 beforeClass: 'bootstrap-sticky-wrap',
24985 getAutoCreate : function(){
24989 cls: 'alert alert-dismissable alert-' + this.weight,
24994 html: this.html || ''
25000 cfg.cls += ' alert-messages-fixed';
25014 onRender : function(ct, position)
25016 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25019 var cfg = Roo.apply({}, this.getAutoCreate());
25023 cfg.cls += ' ' + this.cls;
25026 cfg.style = this.style;
25028 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25030 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25033 this.el.select('>button.close').on('click', this.hide, this);
25039 if (!this.rendered) {
25045 this.fireEvent('show', this);
25051 if (!this.rendered) {
25057 this.fireEvent('hide', this);
25060 update : function()
25062 // var e = this.el.dom.firstChild;
25064 // if(this.closable){
25065 // e = e.nextSibling;
25068 // e.data = this.html || '';
25070 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25086 * @class Roo.bootstrap.Graph
25087 * @extends Roo.bootstrap.Component
25088 * Bootstrap Graph class
25092 @cfg {String} graphtype bar | vbar | pie
25093 @cfg {number} g_x coodinator | centre x (pie)
25094 @cfg {number} g_y coodinator | centre y (pie)
25095 @cfg {number} g_r radius (pie)
25096 @cfg {number} g_height height of the chart (respected by all elements in the set)
25097 @cfg {number} g_width width of the chart (respected by all elements in the set)
25098 @cfg {Object} title The title of the chart
25101 -opts (object) options for the chart
25103 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25104 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25106 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.
25107 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25109 o stretch (boolean)
25111 -opts (object) options for the pie
25114 o startAngle (number)
25115 o endAngle (number)
25119 * Create a new Input
25120 * @param {Object} config The config object
25123 Roo.bootstrap.Graph = function(config){
25124 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25130 * The img click event for the img.
25131 * @param {Roo.EventObject} e
25137 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25148 //g_colors: this.colors,
25155 getAutoCreate : function(){
25166 onRender : function(ct,position){
25169 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25171 if (typeof(Raphael) == 'undefined') {
25172 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25176 this.raphael = Raphael(this.el.dom);
25178 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25179 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25180 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25181 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25183 r.text(160, 10, "Single Series Chart").attr(txtattr);
25184 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25185 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25186 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25188 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25189 r.barchart(330, 10, 300, 220, data1);
25190 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25191 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25194 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25195 // r.barchart(30, 30, 560, 250, xdata, {
25196 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25197 // axis : "0 0 1 1",
25198 // axisxlabels : xdata
25199 // //yvalues : cols,
25202 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25204 // this.load(null,xdata,{
25205 // axis : "0 0 1 1",
25206 // axisxlabels : xdata
25211 load : function(graphtype,xdata,opts)
25213 this.raphael.clear();
25215 graphtype = this.graphtype;
25220 var r = this.raphael,
25221 fin = function () {
25222 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25224 fout = function () {
25225 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25227 pfin = function() {
25228 this.sector.stop();
25229 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25232 this.label[0].stop();
25233 this.label[0].attr({ r: 7.5 });
25234 this.label[1].attr({ "font-weight": 800 });
25237 pfout = function() {
25238 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25241 this.label[0].animate({ r: 5 }, 500, "bounce");
25242 this.label[1].attr({ "font-weight": 400 });
25248 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25251 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25254 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25255 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25257 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25264 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25269 setTitle: function(o)
25274 initEvents: function() {
25277 this.el.on('click', this.onClick, this);
25281 onClick : function(e)
25283 Roo.log('img onclick');
25284 this.fireEvent('click', this, e);
25296 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25299 * @class Roo.bootstrap.dash.NumberBox
25300 * @extends Roo.bootstrap.Component
25301 * Bootstrap NumberBox class
25302 * @cfg {String} headline Box headline
25303 * @cfg {String} content Box content
25304 * @cfg {String} icon Box icon
25305 * @cfg {String} footer Footer text
25306 * @cfg {String} fhref Footer href
25309 * Create a new NumberBox
25310 * @param {Object} config The config object
25314 Roo.bootstrap.dash.NumberBox = function(config){
25315 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25319 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25328 getAutoCreate : function(){
25332 cls : 'small-box ',
25340 cls : 'roo-headline',
25341 html : this.headline
25345 cls : 'roo-content',
25346 html : this.content
25360 cls : 'ion ' + this.icon
25369 cls : 'small-box-footer',
25370 href : this.fhref || '#',
25374 cfg.cn.push(footer);
25381 onRender : function(ct,position){
25382 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25389 setHeadline: function (value)
25391 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25394 setFooter: function (value, href)
25396 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25399 this.el.select('a.small-box-footer',true).first().attr('href', href);
25404 setContent: function (value)
25406 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25409 initEvents: function()
25423 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25426 * @class Roo.bootstrap.dash.TabBox
25427 * @extends Roo.bootstrap.Component
25428 * Bootstrap TabBox class
25429 * @cfg {String} title Title of the TabBox
25430 * @cfg {String} icon Icon of the TabBox
25431 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25432 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25435 * Create a new TabBox
25436 * @param {Object} config The config object
25440 Roo.bootstrap.dash.TabBox = function(config){
25441 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25446 * When a pane is added
25447 * @param {Roo.bootstrap.dash.TabPane} pane
25451 * @event activatepane
25452 * When a pane is activated
25453 * @param {Roo.bootstrap.dash.TabPane} pane
25455 "activatepane" : true
25463 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25468 tabScrollable : false,
25470 getChildContainer : function()
25472 return this.el.select('.tab-content', true).first();
25475 getAutoCreate : function(){
25479 cls: 'pull-left header',
25487 cls: 'fa ' + this.icon
25493 cls: 'nav nav-tabs pull-right',
25499 if(this.tabScrollable){
25506 cls: 'nav nav-tabs pull-right',
25517 cls: 'nav-tabs-custom',
25522 cls: 'tab-content no-padding',
25530 initEvents : function()
25532 //Roo.log('add add pane handler');
25533 this.on('addpane', this.onAddPane, this);
25536 * Updates the box title
25537 * @param {String} html to set the title to.
25539 setTitle : function(value)
25541 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25543 onAddPane : function(pane)
25545 this.panes.push(pane);
25546 //Roo.log('addpane');
25548 // tabs are rendere left to right..
25549 if(!this.showtabs){
25553 var ctr = this.el.select('.nav-tabs', true).first();
25556 var existing = ctr.select('.nav-tab',true);
25557 var qty = existing.getCount();;
25560 var tab = ctr.createChild({
25562 cls : 'nav-tab' + (qty ? '' : ' active'),
25570 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25573 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25575 pane.el.addClass('active');
25580 onTabClick : function(ev,un,ob,pane)
25582 //Roo.log('tab - prev default');
25583 ev.preventDefault();
25586 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25587 pane.tab.addClass('active');
25588 //Roo.log(pane.title);
25589 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25590 // technically we should have a deactivate event.. but maybe add later.
25591 // and it should not de-activate the selected tab...
25592 this.fireEvent('activatepane', pane);
25593 pane.el.addClass('active');
25594 pane.fireEvent('activate');
25599 getActivePane : function()
25602 Roo.each(this.panes, function(p) {
25603 if(p.el.hasClass('active')){
25624 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25626 * @class Roo.bootstrap.TabPane
25627 * @extends Roo.bootstrap.Component
25628 * Bootstrap TabPane class
25629 * @cfg {Boolean} active (false | true) Default false
25630 * @cfg {String} title title of panel
25634 * Create a new TabPane
25635 * @param {Object} config The config object
25638 Roo.bootstrap.dash.TabPane = function(config){
25639 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25645 * When a pane is activated
25646 * @param {Roo.bootstrap.dash.TabPane} pane
25653 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25658 // the tabBox that this is attached to.
25661 getAutoCreate : function()
25669 cfg.cls += ' active';
25674 initEvents : function()
25676 //Roo.log('trigger add pane handler');
25677 this.parent().fireEvent('addpane', this)
25681 * Updates the tab title
25682 * @param {String} html to set the title to.
25684 setTitle: function(str)
25690 this.tab.select('a', true).first().dom.innerHTML = str;
25707 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25710 * @class Roo.bootstrap.menu.Menu
25711 * @extends Roo.bootstrap.Component
25712 * Bootstrap Menu class - container for Menu
25713 * @cfg {String} html Text of the menu
25714 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25715 * @cfg {String} icon Font awesome icon
25716 * @cfg {String} pos Menu align to (top | bottom) default bottom
25720 * Create a new Menu
25721 * @param {Object} config The config object
25725 Roo.bootstrap.menu.Menu = function(config){
25726 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25730 * @event beforeshow
25731 * Fires before this menu is displayed
25732 * @param {Roo.bootstrap.menu.Menu} this
25736 * @event beforehide
25737 * Fires before this menu is hidden
25738 * @param {Roo.bootstrap.menu.Menu} this
25743 * Fires after this menu is displayed
25744 * @param {Roo.bootstrap.menu.Menu} this
25749 * Fires after this menu is hidden
25750 * @param {Roo.bootstrap.menu.Menu} this
25755 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25756 * @param {Roo.bootstrap.menu.Menu} this
25757 * @param {Roo.EventObject} e
25764 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25768 weight : 'default',
25773 getChildContainer : function() {
25774 if(this.isSubMenu){
25778 return this.el.select('ul.dropdown-menu', true).first();
25781 getAutoCreate : function()
25786 cls : 'roo-menu-text',
25794 cls : 'fa ' + this.icon
25805 cls : 'dropdown-button btn btn-' + this.weight,
25810 cls : 'dropdown-toggle btn btn-' + this.weight,
25820 cls : 'dropdown-menu'
25826 if(this.pos == 'top'){
25827 cfg.cls += ' dropup';
25830 if(this.isSubMenu){
25833 cls : 'dropdown-menu'
25840 onRender : function(ct, position)
25842 this.isSubMenu = ct.hasClass('dropdown-submenu');
25844 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25847 initEvents : function()
25849 if(this.isSubMenu){
25853 this.hidden = true;
25855 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25856 this.triggerEl.on('click', this.onTriggerPress, this);
25858 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25859 this.buttonEl.on('click', this.onClick, this);
25865 if(this.isSubMenu){
25869 return this.el.select('ul.dropdown-menu', true).first();
25872 onClick : function(e)
25874 this.fireEvent("click", this, e);
25877 onTriggerPress : function(e)
25879 if (this.isVisible()) {
25886 isVisible : function(){
25887 return !this.hidden;
25892 this.fireEvent("beforeshow", this);
25894 this.hidden = false;
25895 this.el.addClass('open');
25897 Roo.get(document).on("mouseup", this.onMouseUp, this);
25899 this.fireEvent("show", this);
25906 this.fireEvent("beforehide", this);
25908 this.hidden = true;
25909 this.el.removeClass('open');
25911 Roo.get(document).un("mouseup", this.onMouseUp);
25913 this.fireEvent("hide", this);
25916 onMouseUp : function()
25930 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25933 * @class Roo.bootstrap.menu.Item
25934 * @extends Roo.bootstrap.Component
25935 * Bootstrap MenuItem class
25936 * @cfg {Boolean} submenu (true | false) default false
25937 * @cfg {String} html text of the item
25938 * @cfg {String} href the link
25939 * @cfg {Boolean} disable (true | false) default false
25940 * @cfg {Boolean} preventDefault (true | false) default true
25941 * @cfg {String} icon Font awesome icon
25942 * @cfg {String} pos Submenu align to (left | right) default right
25946 * Create a new Item
25947 * @param {Object} config The config object
25951 Roo.bootstrap.menu.Item = function(config){
25952 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25956 * Fires when the mouse is hovering over this menu
25957 * @param {Roo.bootstrap.menu.Item} this
25958 * @param {Roo.EventObject} e
25963 * Fires when the mouse exits this menu
25964 * @param {Roo.bootstrap.menu.Item} this
25965 * @param {Roo.EventObject} e
25971 * The raw click event for the entire grid.
25972 * @param {Roo.EventObject} e
25978 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25983 preventDefault: true,
25988 getAutoCreate : function()
25993 cls : 'roo-menu-item-text',
26001 cls : 'fa ' + this.icon
26010 href : this.href || '#',
26017 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26021 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26023 if(this.pos == 'left'){
26024 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26031 initEvents : function()
26033 this.el.on('mouseover', this.onMouseOver, this);
26034 this.el.on('mouseout', this.onMouseOut, this);
26036 this.el.select('a', true).first().on('click', this.onClick, this);
26040 onClick : function(e)
26042 if(this.preventDefault){
26043 e.preventDefault();
26046 this.fireEvent("click", this, e);
26049 onMouseOver : function(e)
26051 if(this.submenu && this.pos == 'left'){
26052 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26055 this.fireEvent("mouseover", this, e);
26058 onMouseOut : function(e)
26060 this.fireEvent("mouseout", this, e);
26072 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26075 * @class Roo.bootstrap.menu.Separator
26076 * @extends Roo.bootstrap.Component
26077 * Bootstrap Separator class
26080 * Create a new Separator
26081 * @param {Object} config The config object
26085 Roo.bootstrap.menu.Separator = function(config){
26086 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26089 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26091 getAutoCreate : function(){
26112 * @class Roo.bootstrap.Tooltip
26113 * Bootstrap Tooltip class
26114 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26115 * to determine which dom element triggers the tooltip.
26117 * It needs to add support for additional attributes like tooltip-position
26120 * Create a new Toolti
26121 * @param {Object} config The config object
26124 Roo.bootstrap.Tooltip = function(config){
26125 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26127 this.alignment = Roo.bootstrap.Tooltip.alignment;
26129 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26130 this.alignment = config.alignment;
26135 Roo.apply(Roo.bootstrap.Tooltip, {
26137 * @function init initialize tooltip monitoring.
26141 currentTip : false,
26142 currentRegion : false,
26148 Roo.get(document).on('mouseover', this.enter ,this);
26149 Roo.get(document).on('mouseout', this.leave, this);
26152 this.currentTip = new Roo.bootstrap.Tooltip();
26155 enter : function(ev)
26157 var dom = ev.getTarget();
26159 //Roo.log(['enter',dom]);
26160 var el = Roo.fly(dom);
26161 if (this.currentEl) {
26163 //Roo.log(this.currentEl);
26164 //Roo.log(this.currentEl.contains(dom));
26165 if (this.currentEl == el) {
26168 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26174 if (this.currentTip.el) {
26175 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26179 if(!el || el.dom == document){
26185 // you can not look for children, as if el is the body.. then everythign is the child..
26186 if (!el.attr('tooltip')) { //
26187 if (!el.select("[tooltip]").elements.length) {
26190 // is the mouse over this child...?
26191 bindEl = el.select("[tooltip]").first();
26192 var xy = ev.getXY();
26193 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26194 //Roo.log("not in region.");
26197 //Roo.log("child element over..");
26200 this.currentEl = bindEl;
26201 this.currentTip.bind(bindEl);
26202 this.currentRegion = Roo.lib.Region.getRegion(dom);
26203 this.currentTip.enter();
26206 leave : function(ev)
26208 var dom = ev.getTarget();
26209 //Roo.log(['leave',dom]);
26210 if (!this.currentEl) {
26215 if (dom != this.currentEl.dom) {
26218 var xy = ev.getXY();
26219 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26222 // only activate leave if mouse cursor is outside... bounding box..
26227 if (this.currentTip) {
26228 this.currentTip.leave();
26230 //Roo.log('clear currentEl');
26231 this.currentEl = false;
26236 'left' : ['r-l', [-2,0], 'right'],
26237 'right' : ['l-r', [2,0], 'left'],
26238 'bottom' : ['t-b', [0,2], 'top'],
26239 'top' : [ 'b-t', [0,-2], 'bottom']
26245 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26250 delay : null, // can be { show : 300 , hide: 500}
26254 hoverState : null, //???
26256 placement : 'bottom',
26260 getAutoCreate : function(){
26267 cls : 'tooltip-arrow'
26270 cls : 'tooltip-inner'
26277 bind : function(el)
26283 enter : function () {
26285 if (this.timeout != null) {
26286 clearTimeout(this.timeout);
26289 this.hoverState = 'in';
26290 //Roo.log("enter - show");
26291 if (!this.delay || !this.delay.show) {
26296 this.timeout = setTimeout(function () {
26297 if (_t.hoverState == 'in') {
26300 }, this.delay.show);
26304 clearTimeout(this.timeout);
26306 this.hoverState = 'out';
26307 if (!this.delay || !this.delay.hide) {
26313 this.timeout = setTimeout(function () {
26314 //Roo.log("leave - timeout");
26316 if (_t.hoverState == 'out') {
26318 Roo.bootstrap.Tooltip.currentEl = false;
26323 show : function (msg)
26326 this.render(document.body);
26329 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26331 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26333 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26335 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26337 var placement = typeof this.placement == 'function' ?
26338 this.placement.call(this, this.el, on_el) :
26341 var autoToken = /\s?auto?\s?/i;
26342 var autoPlace = autoToken.test(placement);
26344 placement = placement.replace(autoToken, '') || 'top';
26348 //this.el.setXY([0,0]);
26350 //this.el.dom.style.display='block';
26352 //this.el.appendTo(on_el);
26354 var p = this.getPosition();
26355 var box = this.el.getBox();
26361 var align = this.alignment[placement];
26363 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26365 if(placement == 'top' || placement == 'bottom'){
26367 placement = 'right';
26370 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26371 placement = 'left';
26374 var scroll = Roo.select('body', true).first().getScroll();
26376 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26380 align = this.alignment[placement];
26383 this.el.alignTo(this.bindEl, align[0],align[1]);
26384 //var arrow = this.el.select('.arrow',true).first();
26385 //arrow.set(align[2],
26387 this.el.addClass(placement);
26389 this.el.addClass('in fade');
26391 this.hoverState = null;
26393 if (this.el.hasClass('fade')) {
26404 //this.el.setXY([0,0]);
26405 this.el.removeClass('in');
26421 * @class Roo.bootstrap.LocationPicker
26422 * @extends Roo.bootstrap.Component
26423 * Bootstrap LocationPicker class
26424 * @cfg {Number} latitude Position when init default 0
26425 * @cfg {Number} longitude Position when init default 0
26426 * @cfg {Number} zoom default 15
26427 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26428 * @cfg {Boolean} mapTypeControl default false
26429 * @cfg {Boolean} disableDoubleClickZoom default false
26430 * @cfg {Boolean} scrollwheel default true
26431 * @cfg {Boolean} streetViewControl default false
26432 * @cfg {Number} radius default 0
26433 * @cfg {String} locationName
26434 * @cfg {Boolean} draggable default true
26435 * @cfg {Boolean} enableAutocomplete default false
26436 * @cfg {Boolean} enableReverseGeocode default true
26437 * @cfg {String} markerTitle
26440 * Create a new LocationPicker
26441 * @param {Object} config The config object
26445 Roo.bootstrap.LocationPicker = function(config){
26447 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26452 * Fires when the picker initialized.
26453 * @param {Roo.bootstrap.LocationPicker} this
26454 * @param {Google Location} location
26458 * @event positionchanged
26459 * Fires when the picker position changed.
26460 * @param {Roo.bootstrap.LocationPicker} this
26461 * @param {Google Location} location
26463 positionchanged : true,
26466 * Fires when the map resize.
26467 * @param {Roo.bootstrap.LocationPicker} this
26472 * Fires when the map show.
26473 * @param {Roo.bootstrap.LocationPicker} this
26478 * Fires when the map hide.
26479 * @param {Roo.bootstrap.LocationPicker} this
26484 * Fires when click the map.
26485 * @param {Roo.bootstrap.LocationPicker} this
26486 * @param {Map event} e
26490 * @event mapRightClick
26491 * Fires when right click the map.
26492 * @param {Roo.bootstrap.LocationPicker} this
26493 * @param {Map event} e
26495 mapRightClick : true,
26497 * @event markerClick
26498 * Fires when click the marker.
26499 * @param {Roo.bootstrap.LocationPicker} this
26500 * @param {Map event} e
26502 markerClick : true,
26504 * @event markerRightClick
26505 * Fires when right click the marker.
26506 * @param {Roo.bootstrap.LocationPicker} this
26507 * @param {Map event} e
26509 markerRightClick : true,
26511 * @event OverlayViewDraw
26512 * Fires when OverlayView Draw
26513 * @param {Roo.bootstrap.LocationPicker} this
26515 OverlayViewDraw : true,
26517 * @event OverlayViewOnAdd
26518 * Fires when OverlayView Draw
26519 * @param {Roo.bootstrap.LocationPicker} this
26521 OverlayViewOnAdd : true,
26523 * @event OverlayViewOnRemove
26524 * Fires when OverlayView Draw
26525 * @param {Roo.bootstrap.LocationPicker} this
26527 OverlayViewOnRemove : true,
26529 * @event OverlayViewShow
26530 * Fires when OverlayView Draw
26531 * @param {Roo.bootstrap.LocationPicker} this
26532 * @param {Pixel} cpx
26534 OverlayViewShow : true,
26536 * @event OverlayViewHide
26537 * Fires when OverlayView Draw
26538 * @param {Roo.bootstrap.LocationPicker} this
26540 OverlayViewHide : true,
26542 * @event loadexception
26543 * Fires when load google lib failed.
26544 * @param {Roo.bootstrap.LocationPicker} this
26546 loadexception : true
26551 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26553 gMapContext: false,
26559 mapTypeControl: false,
26560 disableDoubleClickZoom: false,
26562 streetViewControl: false,
26566 enableAutocomplete: false,
26567 enableReverseGeocode: true,
26570 getAutoCreate: function()
26575 cls: 'roo-location-picker'
26581 initEvents: function(ct, position)
26583 if(!this.el.getWidth() || this.isApplied()){
26587 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26592 initial: function()
26594 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26595 this.fireEvent('loadexception', this);
26599 if(!this.mapTypeId){
26600 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26603 this.gMapContext = this.GMapContext();
26605 this.initOverlayView();
26607 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26611 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26612 _this.setPosition(_this.gMapContext.marker.position);
26615 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26616 _this.fireEvent('mapClick', this, event);
26620 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26621 _this.fireEvent('mapRightClick', this, event);
26625 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26626 _this.fireEvent('markerClick', this, event);
26630 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26631 _this.fireEvent('markerRightClick', this, event);
26635 this.setPosition(this.gMapContext.location);
26637 this.fireEvent('initial', this, this.gMapContext.location);
26640 initOverlayView: function()
26644 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26648 _this.fireEvent('OverlayViewDraw', _this);
26653 _this.fireEvent('OverlayViewOnAdd', _this);
26656 onRemove: function()
26658 _this.fireEvent('OverlayViewOnRemove', _this);
26661 show: function(cpx)
26663 _this.fireEvent('OverlayViewShow', _this, cpx);
26668 _this.fireEvent('OverlayViewHide', _this);
26674 fromLatLngToContainerPixel: function(event)
26676 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26679 isApplied: function()
26681 return this.getGmapContext() == false ? false : true;
26684 getGmapContext: function()
26686 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26689 GMapContext: function()
26691 var position = new google.maps.LatLng(this.latitude, this.longitude);
26693 var _map = new google.maps.Map(this.el.dom, {
26696 mapTypeId: this.mapTypeId,
26697 mapTypeControl: this.mapTypeControl,
26698 disableDoubleClickZoom: this.disableDoubleClickZoom,
26699 scrollwheel: this.scrollwheel,
26700 streetViewControl: this.streetViewControl,
26701 locationName: this.locationName,
26702 draggable: this.draggable,
26703 enableAutocomplete: this.enableAutocomplete,
26704 enableReverseGeocode: this.enableReverseGeocode
26707 var _marker = new google.maps.Marker({
26708 position: position,
26710 title: this.markerTitle,
26711 draggable: this.draggable
26718 location: position,
26719 radius: this.radius,
26720 locationName: this.locationName,
26721 addressComponents: {
26722 formatted_address: null,
26723 addressLine1: null,
26724 addressLine2: null,
26726 streetNumber: null,
26730 stateOrProvince: null
26733 domContainer: this.el.dom,
26734 geodecoder: new google.maps.Geocoder()
26738 drawCircle: function(center, radius, options)
26740 if (this.gMapContext.circle != null) {
26741 this.gMapContext.circle.setMap(null);
26745 options = Roo.apply({}, options, {
26746 strokeColor: "#0000FF",
26747 strokeOpacity: .35,
26749 fillColor: "#0000FF",
26753 options.map = this.gMapContext.map;
26754 options.radius = radius;
26755 options.center = center;
26756 this.gMapContext.circle = new google.maps.Circle(options);
26757 return this.gMapContext.circle;
26763 setPosition: function(location)
26765 this.gMapContext.location = location;
26766 this.gMapContext.marker.setPosition(location);
26767 this.gMapContext.map.panTo(location);
26768 this.drawCircle(location, this.gMapContext.radius, {});
26772 if (this.gMapContext.settings.enableReverseGeocode) {
26773 this.gMapContext.geodecoder.geocode({
26774 latLng: this.gMapContext.location
26775 }, function(results, status) {
26777 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26778 _this.gMapContext.locationName = results[0].formatted_address;
26779 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26781 _this.fireEvent('positionchanged', this, location);
26788 this.fireEvent('positionchanged', this, location);
26793 google.maps.event.trigger(this.gMapContext.map, "resize");
26795 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26797 this.fireEvent('resize', this);
26800 setPositionByLatLng: function(latitude, longitude)
26802 this.setPosition(new google.maps.LatLng(latitude, longitude));
26805 getCurrentPosition: function()
26808 latitude: this.gMapContext.location.lat(),
26809 longitude: this.gMapContext.location.lng()
26813 getAddressName: function()
26815 return this.gMapContext.locationName;
26818 getAddressComponents: function()
26820 return this.gMapContext.addressComponents;
26823 address_component_from_google_geocode: function(address_components)
26827 for (var i = 0; i < address_components.length; i++) {
26828 var component = address_components[i];
26829 if (component.types.indexOf("postal_code") >= 0) {
26830 result.postalCode = component.short_name;
26831 } else if (component.types.indexOf("street_number") >= 0) {
26832 result.streetNumber = component.short_name;
26833 } else if (component.types.indexOf("route") >= 0) {
26834 result.streetName = component.short_name;
26835 } else if (component.types.indexOf("neighborhood") >= 0) {
26836 result.city = component.short_name;
26837 } else if (component.types.indexOf("locality") >= 0) {
26838 result.city = component.short_name;
26839 } else if (component.types.indexOf("sublocality") >= 0) {
26840 result.district = component.short_name;
26841 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26842 result.stateOrProvince = component.short_name;
26843 } else if (component.types.indexOf("country") >= 0) {
26844 result.country = component.short_name;
26848 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26849 result.addressLine2 = "";
26853 setZoomLevel: function(zoom)
26855 this.gMapContext.map.setZoom(zoom);
26868 this.fireEvent('show', this);
26879 this.fireEvent('hide', this);
26884 Roo.apply(Roo.bootstrap.LocationPicker, {
26886 OverlayView : function(map, options)
26888 options = options || {};
26902 * @class Roo.bootstrap.Alert
26903 * @extends Roo.bootstrap.Component
26904 * Bootstrap Alert class
26905 * @cfg {String} title The title of alert
26906 * @cfg {String} html The content of alert
26907 * @cfg {String} weight ( success | info | warning | danger )
26908 * @cfg {String} faicon font-awesomeicon
26911 * Create a new alert
26912 * @param {Object} config The config object
26916 Roo.bootstrap.Alert = function(config){
26917 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26921 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26928 getAutoCreate : function()
26937 cls : 'roo-alert-icon'
26942 cls : 'roo-alert-title',
26947 cls : 'roo-alert-text',
26954 cfg.cn[0].cls += ' fa ' + this.faicon;
26958 cfg.cls += ' alert-' + this.weight;
26964 initEvents: function()
26966 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26969 setTitle : function(str)
26971 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26974 setText : function(str)
26976 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26979 setWeight : function(weight)
26982 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26985 this.weight = weight;
26987 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26990 setIcon : function(icon)
26993 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26996 this.faicon = icon;
26998 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27019 * @class Roo.bootstrap.UploadCropbox
27020 * @extends Roo.bootstrap.Component
27021 * Bootstrap UploadCropbox class
27022 * @cfg {String} emptyText show when image has been loaded
27023 * @cfg {String} rotateNotify show when image too small to rotate
27024 * @cfg {Number} errorTimeout default 3000
27025 * @cfg {Number} minWidth default 300
27026 * @cfg {Number} minHeight default 300
27027 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27028 * @cfg {Boolean} isDocument (true|false) default false
27029 * @cfg {String} url action url
27030 * @cfg {String} paramName default 'imageUpload'
27031 * @cfg {String} method default POST
27032 * @cfg {Boolean} loadMask (true|false) default true
27033 * @cfg {Boolean} loadingText default 'Loading...'
27036 * Create a new UploadCropbox
27037 * @param {Object} config The config object
27040 Roo.bootstrap.UploadCropbox = function(config){
27041 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27045 * @event beforeselectfile
27046 * Fire before select file
27047 * @param {Roo.bootstrap.UploadCropbox} this
27049 "beforeselectfile" : true,
27052 * Fire after initEvent
27053 * @param {Roo.bootstrap.UploadCropbox} this
27058 * Fire after initEvent
27059 * @param {Roo.bootstrap.UploadCropbox} this
27060 * @param {String} data
27065 * Fire when preparing the file data
27066 * @param {Roo.bootstrap.UploadCropbox} this
27067 * @param {Object} file
27072 * Fire when get exception
27073 * @param {Roo.bootstrap.UploadCropbox} this
27074 * @param {XMLHttpRequest} xhr
27076 "exception" : true,
27078 * @event beforeloadcanvas
27079 * Fire before load the canvas
27080 * @param {Roo.bootstrap.UploadCropbox} this
27081 * @param {String} src
27083 "beforeloadcanvas" : true,
27086 * Fire when trash image
27087 * @param {Roo.bootstrap.UploadCropbox} this
27092 * Fire when download the image
27093 * @param {Roo.bootstrap.UploadCropbox} this
27097 * @event footerbuttonclick
27098 * Fire when footerbuttonclick
27099 * @param {Roo.bootstrap.UploadCropbox} this
27100 * @param {String} type
27102 "footerbuttonclick" : true,
27106 * @param {Roo.bootstrap.UploadCropbox} this
27111 * Fire when rotate the image
27112 * @param {Roo.bootstrap.UploadCropbox} this
27113 * @param {String} pos
27118 * Fire when inspect the file
27119 * @param {Roo.bootstrap.UploadCropbox} this
27120 * @param {Object} file
27125 * Fire when xhr upload the file
27126 * @param {Roo.bootstrap.UploadCropbox} this
27127 * @param {Object} data
27132 * Fire when arrange the file data
27133 * @param {Roo.bootstrap.UploadCropbox} this
27134 * @param {Object} formData
27139 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27142 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27144 emptyText : 'Click to upload image',
27145 rotateNotify : 'Image is too small to rotate',
27146 errorTimeout : 3000,
27160 cropType : 'image/jpeg',
27162 canvasLoaded : false,
27163 isDocument : false,
27165 paramName : 'imageUpload',
27167 loadingText : 'Loading...',
27170 getAutoCreate : function()
27174 cls : 'roo-upload-cropbox',
27178 cls : 'roo-upload-cropbox-selector',
27183 cls : 'roo-upload-cropbox-body',
27184 style : 'cursor:pointer',
27188 cls : 'roo-upload-cropbox-preview'
27192 cls : 'roo-upload-cropbox-thumb'
27196 cls : 'roo-upload-cropbox-empty-notify',
27197 html : this.emptyText
27201 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27202 html : this.rotateNotify
27208 cls : 'roo-upload-cropbox-footer',
27211 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27221 onRender : function(ct, position)
27223 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27225 if (this.buttons.length) {
27227 Roo.each(this.buttons, function(bb) {
27229 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27231 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27237 this.maskEl = this.el;
27241 initEvents : function()
27243 this.urlAPI = (window.createObjectURL && window) ||
27244 (window.URL && URL.revokeObjectURL && URL) ||
27245 (window.webkitURL && webkitURL);
27247 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27248 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27250 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27251 this.selectorEl.hide();
27253 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27254 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27256 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27257 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27258 this.thumbEl.hide();
27260 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27261 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27263 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27264 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27265 this.errorEl.hide();
27267 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27268 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27269 this.footerEl.hide();
27271 this.setThumbBoxSize();
27277 this.fireEvent('initial', this);
27284 window.addEventListener("resize", function() { _this.resize(); } );
27286 this.bodyEl.on('click', this.beforeSelectFile, this);
27289 this.bodyEl.on('touchstart', this.onTouchStart, this);
27290 this.bodyEl.on('touchmove', this.onTouchMove, this);
27291 this.bodyEl.on('touchend', this.onTouchEnd, this);
27295 this.bodyEl.on('mousedown', this.onMouseDown, this);
27296 this.bodyEl.on('mousemove', this.onMouseMove, this);
27297 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27298 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27299 Roo.get(document).on('mouseup', this.onMouseUp, this);
27302 this.selectorEl.on('change', this.onFileSelected, this);
27308 this.baseScale = 1;
27310 this.baseRotate = 1;
27311 this.dragable = false;
27312 this.pinching = false;
27315 this.cropData = false;
27316 this.notifyEl.dom.innerHTML = this.emptyText;
27318 this.selectorEl.dom.value = '';
27322 resize : function()
27324 if(this.fireEvent('resize', this) != false){
27325 this.setThumbBoxPosition();
27326 this.setCanvasPosition();
27330 onFooterButtonClick : function(e, el, o, type)
27333 case 'rotate-left' :
27334 this.onRotateLeft(e);
27336 case 'rotate-right' :
27337 this.onRotateRight(e);
27340 this.beforeSelectFile(e);
27355 this.fireEvent('footerbuttonclick', this, type);
27358 beforeSelectFile : function(e)
27360 e.preventDefault();
27362 if(this.fireEvent('beforeselectfile', this) != false){
27363 this.selectorEl.dom.click();
27367 onFileSelected : function(e)
27369 e.preventDefault();
27371 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27375 var file = this.selectorEl.dom.files[0];
27377 if(this.fireEvent('inspect', this, file) != false){
27378 this.prepare(file);
27383 trash : function(e)
27385 this.fireEvent('trash', this);
27388 download : function(e)
27390 this.fireEvent('download', this);
27393 loadCanvas : function(src)
27395 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27399 this.imageEl = document.createElement('img');
27403 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27405 this.imageEl.src = src;
27409 onLoadCanvas : function()
27411 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27412 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27414 this.bodyEl.un('click', this.beforeSelectFile, this);
27416 this.notifyEl.hide();
27417 this.thumbEl.show();
27418 this.footerEl.show();
27420 this.baseRotateLevel();
27422 if(this.isDocument){
27423 this.setThumbBoxSize();
27426 this.setThumbBoxPosition();
27428 this.baseScaleLevel();
27434 this.canvasLoaded = true;
27437 this.maskEl.unmask();
27442 setCanvasPosition : function()
27444 if(!this.canvasEl){
27448 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27449 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27451 this.previewEl.setLeft(pw);
27452 this.previewEl.setTop(ph);
27456 onMouseDown : function(e)
27460 this.dragable = true;
27461 this.pinching = false;
27463 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27464 this.dragable = false;
27468 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27469 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27473 onMouseMove : function(e)
27477 if(!this.canvasLoaded){
27481 if (!this.dragable){
27485 var minX = Math.ceil(this.thumbEl.getLeft(true));
27486 var minY = Math.ceil(this.thumbEl.getTop(true));
27488 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27489 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27491 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27492 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27494 x = x - this.mouseX;
27495 y = y - this.mouseY;
27497 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27498 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27500 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27501 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27503 this.previewEl.setLeft(bgX);
27504 this.previewEl.setTop(bgY);
27506 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27507 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27510 onMouseUp : function(e)
27514 this.dragable = false;
27517 onMouseWheel : function(e)
27521 this.startScale = this.scale;
27523 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27525 if(!this.zoomable()){
27526 this.scale = this.startScale;
27535 zoomable : function()
27537 var minScale = this.thumbEl.getWidth() / this.minWidth;
27539 if(this.minWidth < this.minHeight){
27540 minScale = this.thumbEl.getHeight() / this.minHeight;
27543 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27544 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27548 (this.rotate == 0 || this.rotate == 180) &&
27550 width > this.imageEl.OriginWidth ||
27551 height > this.imageEl.OriginHeight ||
27552 (width < this.minWidth && height < this.minHeight)
27560 (this.rotate == 90 || this.rotate == 270) &&
27562 width > this.imageEl.OriginWidth ||
27563 height > this.imageEl.OriginHeight ||
27564 (width < this.minHeight && height < this.minWidth)
27571 !this.isDocument &&
27572 (this.rotate == 0 || this.rotate == 180) &&
27574 width < this.minWidth ||
27575 width > this.imageEl.OriginWidth ||
27576 height < this.minHeight ||
27577 height > this.imageEl.OriginHeight
27584 !this.isDocument &&
27585 (this.rotate == 90 || this.rotate == 270) &&
27587 width < this.minHeight ||
27588 width > this.imageEl.OriginWidth ||
27589 height < this.minWidth ||
27590 height > this.imageEl.OriginHeight
27600 onRotateLeft : function(e)
27602 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27604 var minScale = this.thumbEl.getWidth() / this.minWidth;
27606 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27607 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27609 this.startScale = this.scale;
27611 while (this.getScaleLevel() < minScale){
27613 this.scale = this.scale + 1;
27615 if(!this.zoomable()){
27620 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27621 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27626 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27633 this.scale = this.startScale;
27635 this.onRotateFail();
27640 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27642 if(this.isDocument){
27643 this.setThumbBoxSize();
27644 this.setThumbBoxPosition();
27645 this.setCanvasPosition();
27650 this.fireEvent('rotate', this, 'left');
27654 onRotateRight : function(e)
27656 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27658 var minScale = this.thumbEl.getWidth() / this.minWidth;
27660 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27661 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27663 this.startScale = this.scale;
27665 while (this.getScaleLevel() < minScale){
27667 this.scale = this.scale + 1;
27669 if(!this.zoomable()){
27674 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27675 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27680 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27687 this.scale = this.startScale;
27689 this.onRotateFail();
27694 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27696 if(this.isDocument){
27697 this.setThumbBoxSize();
27698 this.setThumbBoxPosition();
27699 this.setCanvasPosition();
27704 this.fireEvent('rotate', this, 'right');
27707 onRotateFail : function()
27709 this.errorEl.show(true);
27713 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27718 this.previewEl.dom.innerHTML = '';
27720 var canvasEl = document.createElement("canvas");
27722 var contextEl = canvasEl.getContext("2d");
27724 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27725 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27726 var center = this.imageEl.OriginWidth / 2;
27728 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27729 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27730 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27731 center = this.imageEl.OriginHeight / 2;
27734 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27736 contextEl.translate(center, center);
27737 contextEl.rotate(this.rotate * Math.PI / 180);
27739 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27741 this.canvasEl = document.createElement("canvas");
27743 this.contextEl = this.canvasEl.getContext("2d");
27745 switch (this.rotate) {
27748 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27749 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27751 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27756 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27757 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27759 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27760 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);
27764 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27769 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27770 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27772 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27773 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);
27777 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);
27782 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27783 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27785 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27786 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27790 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);
27797 this.previewEl.appendChild(this.canvasEl);
27799 this.setCanvasPosition();
27804 if(!this.canvasLoaded){
27808 var imageCanvas = document.createElement("canvas");
27810 var imageContext = imageCanvas.getContext("2d");
27812 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27813 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27815 var center = imageCanvas.width / 2;
27817 imageContext.translate(center, center);
27819 imageContext.rotate(this.rotate * Math.PI / 180);
27821 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27823 var canvas = document.createElement("canvas");
27825 var context = canvas.getContext("2d");
27827 canvas.width = this.minWidth;
27828 canvas.height = this.minHeight;
27830 switch (this.rotate) {
27833 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27834 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27836 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27837 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27839 var targetWidth = this.minWidth - 2 * x;
27840 var targetHeight = this.minHeight - 2 * y;
27844 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27845 scale = targetWidth / width;
27848 if(x > 0 && y == 0){
27849 scale = targetHeight / height;
27852 if(x > 0 && y > 0){
27853 scale = targetWidth / width;
27855 if(width < height){
27856 scale = targetHeight / height;
27860 context.scale(scale, scale);
27862 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27863 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27865 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27866 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27868 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27873 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27874 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27876 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27877 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27879 var targetWidth = this.minWidth - 2 * x;
27880 var targetHeight = this.minHeight - 2 * y;
27884 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27885 scale = targetWidth / width;
27888 if(x > 0 && y == 0){
27889 scale = targetHeight / height;
27892 if(x > 0 && y > 0){
27893 scale = targetWidth / width;
27895 if(width < height){
27896 scale = targetHeight / height;
27900 context.scale(scale, scale);
27902 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27903 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27905 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27906 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27908 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27910 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27915 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27916 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27918 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27919 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27921 var targetWidth = this.minWidth - 2 * x;
27922 var targetHeight = this.minHeight - 2 * y;
27926 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27927 scale = targetWidth / width;
27930 if(x > 0 && y == 0){
27931 scale = targetHeight / height;
27934 if(x > 0 && y > 0){
27935 scale = targetWidth / width;
27937 if(width < height){
27938 scale = targetHeight / height;
27942 context.scale(scale, scale);
27944 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27945 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27947 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27948 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27950 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27951 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27953 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27958 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27959 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27961 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27962 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27964 var targetWidth = this.minWidth - 2 * x;
27965 var targetHeight = this.minHeight - 2 * y;
27969 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27970 scale = targetWidth / width;
27973 if(x > 0 && y == 0){
27974 scale = targetHeight / height;
27977 if(x > 0 && y > 0){
27978 scale = targetWidth / width;
27980 if(width < height){
27981 scale = targetHeight / height;
27985 context.scale(scale, scale);
27987 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27988 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27990 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27991 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27993 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27995 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28002 this.cropData = canvas.toDataURL(this.cropType);
28004 if(this.fireEvent('crop', this, this.cropData) !== false){
28005 this.process(this.file, this.cropData);
28012 setThumbBoxSize : function()
28016 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28017 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28018 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28020 this.minWidth = width;
28021 this.minHeight = height;
28023 if(this.rotate == 90 || this.rotate == 270){
28024 this.minWidth = height;
28025 this.minHeight = width;
28030 width = Math.ceil(this.minWidth * height / this.minHeight);
28032 if(this.minWidth > this.minHeight){
28034 height = Math.ceil(this.minHeight * width / this.minWidth);
28037 this.thumbEl.setStyle({
28038 width : width + 'px',
28039 height : height + 'px'
28046 setThumbBoxPosition : function()
28048 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28049 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28051 this.thumbEl.setLeft(x);
28052 this.thumbEl.setTop(y);
28056 baseRotateLevel : function()
28058 this.baseRotate = 1;
28061 typeof(this.exif) != 'undefined' &&
28062 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28063 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28065 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28068 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28072 baseScaleLevel : function()
28076 if(this.isDocument){
28078 if(this.baseRotate == 6 || this.baseRotate == 8){
28080 height = this.thumbEl.getHeight();
28081 this.baseScale = height / this.imageEl.OriginWidth;
28083 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28084 width = this.thumbEl.getWidth();
28085 this.baseScale = width / this.imageEl.OriginHeight;
28091 height = this.thumbEl.getHeight();
28092 this.baseScale = height / this.imageEl.OriginHeight;
28094 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28095 width = this.thumbEl.getWidth();
28096 this.baseScale = width / this.imageEl.OriginWidth;
28102 if(this.baseRotate == 6 || this.baseRotate == 8){
28104 width = this.thumbEl.getHeight();
28105 this.baseScale = width / this.imageEl.OriginHeight;
28107 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28108 height = this.thumbEl.getWidth();
28109 this.baseScale = height / this.imageEl.OriginHeight;
28112 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28113 height = this.thumbEl.getWidth();
28114 this.baseScale = height / this.imageEl.OriginHeight;
28116 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28117 width = this.thumbEl.getHeight();
28118 this.baseScale = width / this.imageEl.OriginWidth;
28125 width = this.thumbEl.getWidth();
28126 this.baseScale = width / this.imageEl.OriginWidth;
28128 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28129 height = this.thumbEl.getHeight();
28130 this.baseScale = height / this.imageEl.OriginHeight;
28133 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28135 height = this.thumbEl.getHeight();
28136 this.baseScale = height / this.imageEl.OriginHeight;
28138 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28139 width = this.thumbEl.getWidth();
28140 this.baseScale = width / this.imageEl.OriginWidth;
28148 getScaleLevel : function()
28150 return this.baseScale * Math.pow(1.1, this.scale);
28153 onTouchStart : function(e)
28155 if(!this.canvasLoaded){
28156 this.beforeSelectFile(e);
28160 var touches = e.browserEvent.touches;
28166 if(touches.length == 1){
28167 this.onMouseDown(e);
28171 if(touches.length != 2){
28177 for(var i = 0, finger; finger = touches[i]; i++){
28178 coords.push(finger.pageX, finger.pageY);
28181 var x = Math.pow(coords[0] - coords[2], 2);
28182 var y = Math.pow(coords[1] - coords[3], 2);
28184 this.startDistance = Math.sqrt(x + y);
28186 this.startScale = this.scale;
28188 this.pinching = true;
28189 this.dragable = false;
28193 onTouchMove : function(e)
28195 if(!this.pinching && !this.dragable){
28199 var touches = e.browserEvent.touches;
28206 this.onMouseMove(e);
28212 for(var i = 0, finger; finger = touches[i]; i++){
28213 coords.push(finger.pageX, finger.pageY);
28216 var x = Math.pow(coords[0] - coords[2], 2);
28217 var y = Math.pow(coords[1] - coords[3], 2);
28219 this.endDistance = Math.sqrt(x + y);
28221 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28223 if(!this.zoomable()){
28224 this.scale = this.startScale;
28232 onTouchEnd : function(e)
28234 this.pinching = false;
28235 this.dragable = false;
28239 process : function(file, crop)
28242 this.maskEl.mask(this.loadingText);
28245 this.xhr = new XMLHttpRequest();
28247 file.xhr = this.xhr;
28249 this.xhr.open(this.method, this.url, true);
28252 "Accept": "application/json",
28253 "Cache-Control": "no-cache",
28254 "X-Requested-With": "XMLHttpRequest"
28257 for (var headerName in headers) {
28258 var headerValue = headers[headerName];
28260 this.xhr.setRequestHeader(headerName, headerValue);
28266 this.xhr.onload = function()
28268 _this.xhrOnLoad(_this.xhr);
28271 this.xhr.onerror = function()
28273 _this.xhrOnError(_this.xhr);
28276 var formData = new FormData();
28278 formData.append('returnHTML', 'NO');
28281 formData.append('crop', crop);
28284 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28285 formData.append(this.paramName, file, file.name);
28288 if(typeof(file.filename) != 'undefined'){
28289 formData.append('filename', file.filename);
28292 if(typeof(file.mimetype) != 'undefined'){
28293 formData.append('mimetype', file.mimetype);
28296 if(this.fireEvent('arrange', this, formData) != false){
28297 this.xhr.send(formData);
28301 xhrOnLoad : function(xhr)
28304 this.maskEl.unmask();
28307 if (xhr.readyState !== 4) {
28308 this.fireEvent('exception', this, xhr);
28312 var response = Roo.decode(xhr.responseText);
28314 if(!response.success){
28315 this.fireEvent('exception', this, xhr);
28319 var response = Roo.decode(xhr.responseText);
28321 this.fireEvent('upload', this, response);
28325 xhrOnError : function()
28328 this.maskEl.unmask();
28331 Roo.log('xhr on error');
28333 var response = Roo.decode(xhr.responseText);
28339 prepare : function(file)
28342 this.maskEl.mask(this.loadingText);
28348 if(typeof(file) === 'string'){
28349 this.loadCanvas(file);
28353 if(!file || !this.urlAPI){
28358 this.cropType = file.type;
28362 if(this.fireEvent('prepare', this, this.file) != false){
28364 var reader = new FileReader();
28366 reader.onload = function (e) {
28367 if (e.target.error) {
28368 Roo.log(e.target.error);
28372 var buffer = e.target.result,
28373 dataView = new DataView(buffer),
28375 maxOffset = dataView.byteLength - 4,
28379 if (dataView.getUint16(0) === 0xffd8) {
28380 while (offset < maxOffset) {
28381 markerBytes = dataView.getUint16(offset);
28383 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28384 markerLength = dataView.getUint16(offset + 2) + 2;
28385 if (offset + markerLength > dataView.byteLength) {
28386 Roo.log('Invalid meta data: Invalid segment size.');
28390 if(markerBytes == 0xffe1){
28391 _this.parseExifData(
28398 offset += markerLength;
28408 var url = _this.urlAPI.createObjectURL(_this.file);
28410 _this.loadCanvas(url);
28415 reader.readAsArrayBuffer(this.file);
28421 parseExifData : function(dataView, offset, length)
28423 var tiffOffset = offset + 10,
28427 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28428 // No Exif data, might be XMP data instead
28432 // Check for the ASCII code for "Exif" (0x45786966):
28433 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28434 // No Exif data, might be XMP data instead
28437 if (tiffOffset + 8 > dataView.byteLength) {
28438 Roo.log('Invalid Exif data: Invalid segment size.');
28441 // Check for the two null bytes:
28442 if (dataView.getUint16(offset + 8) !== 0x0000) {
28443 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28446 // Check the byte alignment:
28447 switch (dataView.getUint16(tiffOffset)) {
28449 littleEndian = true;
28452 littleEndian = false;
28455 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28458 // Check for the TIFF tag marker (0x002A):
28459 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28460 Roo.log('Invalid Exif data: Missing TIFF marker.');
28463 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28464 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28466 this.parseExifTags(
28469 tiffOffset + dirOffset,
28474 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28479 if (dirOffset + 6 > dataView.byteLength) {
28480 Roo.log('Invalid Exif data: Invalid directory offset.');
28483 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28484 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28485 if (dirEndOffset + 4 > dataView.byteLength) {
28486 Roo.log('Invalid Exif data: Invalid directory size.');
28489 for (i = 0; i < tagsNumber; i += 1) {
28493 dirOffset + 2 + 12 * i, // tag offset
28497 // Return the offset to the next directory:
28498 return dataView.getUint32(dirEndOffset, littleEndian);
28501 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28503 var tag = dataView.getUint16(offset, littleEndian);
28505 this.exif[tag] = this.getExifValue(
28509 dataView.getUint16(offset + 2, littleEndian), // tag type
28510 dataView.getUint32(offset + 4, littleEndian), // tag length
28515 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28517 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28526 Roo.log('Invalid Exif data: Invalid tag type.');
28530 tagSize = tagType.size * length;
28531 // Determine if the value is contained in the dataOffset bytes,
28532 // or if the value at the dataOffset is a pointer to the actual data:
28533 dataOffset = tagSize > 4 ?
28534 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28535 if (dataOffset + tagSize > dataView.byteLength) {
28536 Roo.log('Invalid Exif data: Invalid data offset.');
28539 if (length === 1) {
28540 return tagType.getValue(dataView, dataOffset, littleEndian);
28543 for (i = 0; i < length; i += 1) {
28544 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28547 if (tagType.ascii) {
28549 // Concatenate the chars:
28550 for (i = 0; i < values.length; i += 1) {
28552 // Ignore the terminating NULL byte(s):
28553 if (c === '\u0000') {
28565 Roo.apply(Roo.bootstrap.UploadCropbox, {
28567 'Orientation': 0x0112
28571 1: 0, //'top-left',
28573 3: 180, //'bottom-right',
28574 // 4: 'bottom-left',
28576 6: 90, //'right-top',
28577 // 7: 'right-bottom',
28578 8: 270 //'left-bottom'
28582 // byte, 8-bit unsigned int:
28584 getValue: function (dataView, dataOffset) {
28585 return dataView.getUint8(dataOffset);
28589 // ascii, 8-bit byte:
28591 getValue: function (dataView, dataOffset) {
28592 return String.fromCharCode(dataView.getUint8(dataOffset));
28597 // short, 16 bit int:
28599 getValue: function (dataView, dataOffset, littleEndian) {
28600 return dataView.getUint16(dataOffset, littleEndian);
28604 // long, 32 bit int:
28606 getValue: function (dataView, dataOffset, littleEndian) {
28607 return dataView.getUint32(dataOffset, littleEndian);
28611 // rational = two long values, first is numerator, second is denominator:
28613 getValue: function (dataView, dataOffset, littleEndian) {
28614 return dataView.getUint32(dataOffset, littleEndian) /
28615 dataView.getUint32(dataOffset + 4, littleEndian);
28619 // slong, 32 bit signed int:
28621 getValue: function (dataView, dataOffset, littleEndian) {
28622 return dataView.getInt32(dataOffset, littleEndian);
28626 // srational, two slongs, first is numerator, second is denominator:
28628 getValue: function (dataView, dataOffset, littleEndian) {
28629 return dataView.getInt32(dataOffset, littleEndian) /
28630 dataView.getInt32(dataOffset + 4, littleEndian);
28640 cls : 'btn-group roo-upload-cropbox-rotate-left',
28641 action : 'rotate-left',
28645 cls : 'btn btn-default',
28646 html : '<i class="fa fa-undo"></i>'
28652 cls : 'btn-group roo-upload-cropbox-picture',
28653 action : 'picture',
28657 cls : 'btn btn-default',
28658 html : '<i class="fa fa-picture-o"></i>'
28664 cls : 'btn-group roo-upload-cropbox-rotate-right',
28665 action : 'rotate-right',
28669 cls : 'btn btn-default',
28670 html : '<i class="fa fa-repeat"></i>'
28678 cls : 'btn-group roo-upload-cropbox-rotate-left',
28679 action : 'rotate-left',
28683 cls : 'btn btn-default',
28684 html : '<i class="fa fa-undo"></i>'
28690 cls : 'btn-group roo-upload-cropbox-download',
28691 action : 'download',
28695 cls : 'btn btn-default',
28696 html : '<i class="fa fa-download"></i>'
28702 cls : 'btn-group roo-upload-cropbox-crop',
28707 cls : 'btn btn-default',
28708 html : '<i class="fa fa-crop"></i>'
28714 cls : 'btn-group roo-upload-cropbox-trash',
28719 cls : 'btn btn-default',
28720 html : '<i class="fa fa-trash"></i>'
28726 cls : 'btn-group roo-upload-cropbox-rotate-right',
28727 action : 'rotate-right',
28731 cls : 'btn btn-default',
28732 html : '<i class="fa fa-repeat"></i>'
28740 cls : 'btn-group roo-upload-cropbox-rotate-left',
28741 action : 'rotate-left',
28745 cls : 'btn btn-default',
28746 html : '<i class="fa fa-undo"></i>'
28752 cls : 'btn-group roo-upload-cropbox-rotate-right',
28753 action : 'rotate-right',
28757 cls : 'btn btn-default',
28758 html : '<i class="fa fa-repeat"></i>'
28771 * @class Roo.bootstrap.DocumentManager
28772 * @extends Roo.bootstrap.Component
28773 * Bootstrap DocumentManager class
28774 * @cfg {String} paramName default 'imageUpload'
28775 * @cfg {String} toolTipName default 'filename'
28776 * @cfg {String} method default POST
28777 * @cfg {String} url action url
28778 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28779 * @cfg {Boolean} multiple multiple upload default true
28780 * @cfg {Number} thumbSize default 300
28781 * @cfg {String} fieldLabel
28782 * @cfg {Number} labelWidth default 4
28783 * @cfg {String} labelAlign (left|top) default left
28784 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28785 * @cfg {Number} labellg set the width of label (1-12)
28786 * @cfg {Number} labelmd set the width of label (1-12)
28787 * @cfg {Number} labelsm set the width of label (1-12)
28788 * @cfg {Number} labelxs set the width of label (1-12)
28791 * Create a new DocumentManager
28792 * @param {Object} config The config object
28795 Roo.bootstrap.DocumentManager = function(config){
28796 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28799 this.delegates = [];
28804 * Fire when initial the DocumentManager
28805 * @param {Roo.bootstrap.DocumentManager} this
28810 * inspect selected file
28811 * @param {Roo.bootstrap.DocumentManager} this
28812 * @param {File} file
28817 * Fire when xhr load exception
28818 * @param {Roo.bootstrap.DocumentManager} this
28819 * @param {XMLHttpRequest} xhr
28821 "exception" : true,
28823 * @event afterupload
28824 * Fire when xhr load exception
28825 * @param {Roo.bootstrap.DocumentManager} this
28826 * @param {XMLHttpRequest} xhr
28828 "afterupload" : true,
28831 * prepare the form data
28832 * @param {Roo.bootstrap.DocumentManager} this
28833 * @param {Object} formData
28838 * Fire when remove the file
28839 * @param {Roo.bootstrap.DocumentManager} this
28840 * @param {Object} file
28845 * Fire after refresh the file
28846 * @param {Roo.bootstrap.DocumentManager} this
28851 * Fire after click the image
28852 * @param {Roo.bootstrap.DocumentManager} this
28853 * @param {Object} file
28858 * Fire when upload a image and editable set to true
28859 * @param {Roo.bootstrap.DocumentManager} this
28860 * @param {Object} file
28864 * @event beforeselectfile
28865 * Fire before select file
28866 * @param {Roo.bootstrap.DocumentManager} this
28868 "beforeselectfile" : true,
28871 * Fire before process file
28872 * @param {Roo.bootstrap.DocumentManager} this
28873 * @param {Object} file
28877 * @event previewrendered
28878 * Fire when preview rendered
28879 * @param {Roo.bootstrap.DocumentManager} this
28880 * @param {Object} file
28882 "previewrendered" : true,
28885 "previewResize" : true
28890 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28899 paramName : 'imageUpload',
28900 toolTipName : 'filename',
28903 labelAlign : 'left',
28913 getAutoCreate : function()
28915 var managerWidget = {
28917 cls : 'roo-document-manager',
28921 cls : 'roo-document-manager-selector',
28926 cls : 'roo-document-manager-uploader',
28930 cls : 'roo-document-manager-upload-btn',
28931 html : '<i class="fa fa-plus"></i>'
28942 cls : 'column col-md-12',
28947 if(this.fieldLabel.length){
28952 cls : 'column col-md-12',
28953 html : this.fieldLabel
28957 cls : 'column col-md-12',
28962 if(this.labelAlign == 'left'){
28967 html : this.fieldLabel
28976 if(this.labelWidth > 12){
28977 content[0].style = "width: " + this.labelWidth + 'px';
28980 if(this.labelWidth < 13 && this.labelmd == 0){
28981 this.labelmd = this.labelWidth;
28984 if(this.labellg > 0){
28985 content[0].cls += ' col-lg-' + this.labellg;
28986 content[1].cls += ' col-lg-' + (12 - this.labellg);
28989 if(this.labelmd > 0){
28990 content[0].cls += ' col-md-' + this.labelmd;
28991 content[1].cls += ' col-md-' + (12 - this.labelmd);
28994 if(this.labelsm > 0){
28995 content[0].cls += ' col-sm-' + this.labelsm;
28996 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28999 if(this.labelxs > 0){
29000 content[0].cls += ' col-xs-' + this.labelxs;
29001 content[1].cls += ' col-xs-' + (12 - this.labelxs);
29009 cls : 'row clearfix',
29017 initEvents : function()
29019 this.managerEl = this.el.select('.roo-document-manager', true).first();
29020 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29022 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29023 this.selectorEl.hide();
29026 this.selectorEl.attr('multiple', 'multiple');
29029 this.selectorEl.on('change', this.onFileSelected, this);
29031 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29032 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29034 this.uploader.on('click', this.onUploaderClick, this);
29036 this.renderProgressDialog();
29040 window.addEventListener("resize", function() { _this.refresh(); } );
29042 this.fireEvent('initial', this);
29045 renderProgressDialog : function()
29049 this.progressDialog = new Roo.bootstrap.Modal({
29050 cls : 'roo-document-manager-progress-dialog',
29051 allow_close : false,
29061 btnclick : function() {
29062 _this.uploadCancel();
29068 this.progressDialog.render(Roo.get(document.body));
29070 this.progress = new Roo.bootstrap.Progress({
29071 cls : 'roo-document-manager-progress',
29076 this.progress.render(this.progressDialog.getChildContainer());
29078 this.progressBar = new Roo.bootstrap.ProgressBar({
29079 cls : 'roo-document-manager-progress-bar',
29082 aria_valuemax : 12,
29086 this.progressBar.render(this.progress.getChildContainer());
29089 onUploaderClick : function(e)
29091 e.preventDefault();
29093 if(this.fireEvent('beforeselectfile', this) != false){
29094 this.selectorEl.dom.click();
29099 onFileSelected : function(e)
29101 e.preventDefault();
29103 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29107 Roo.each(this.selectorEl.dom.files, function(file){
29108 if(this.fireEvent('inspect', this, file) != false){
29109 this.files.push(file);
29119 this.selectorEl.dom.value = '';
29121 if(!this.files || !this.files.length){
29125 if(this.boxes > 0 && this.files.length > this.boxes){
29126 this.files = this.files.slice(0, this.boxes);
29129 this.uploader.show();
29131 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29132 this.uploader.hide();
29141 Roo.each(this.files, function(file){
29143 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29144 var f = this.renderPreview(file);
29149 if(file.type.indexOf('image') != -1){
29150 this.delegates.push(
29152 _this.process(file);
29153 }).createDelegate(this)
29161 _this.process(file);
29162 }).createDelegate(this)
29167 this.files = files;
29169 this.delegates = this.delegates.concat(docs);
29171 if(!this.delegates.length){
29176 this.progressBar.aria_valuemax = this.delegates.length;
29183 arrange : function()
29185 if(!this.delegates.length){
29186 this.progressDialog.hide();
29191 var delegate = this.delegates.shift();
29193 this.progressDialog.show();
29195 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29197 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29202 refresh : function()
29204 this.uploader.show();
29206 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29207 this.uploader.hide();
29210 Roo.isTouch ? this.closable(false) : this.closable(true);
29212 this.fireEvent('refresh', this);
29215 onRemove : function(e, el, o)
29217 e.preventDefault();
29219 this.fireEvent('remove', this, o);
29223 remove : function(o)
29227 Roo.each(this.files, function(file){
29228 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29237 this.files = files;
29244 Roo.each(this.files, function(file){
29249 file.target.remove();
29258 onClick : function(e, el, o)
29260 e.preventDefault();
29262 this.fireEvent('click', this, o);
29266 closable : function(closable)
29268 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29270 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29282 xhrOnLoad : function(xhr)
29284 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29288 if (xhr.readyState !== 4) {
29290 this.fireEvent('exception', this, xhr);
29294 var response = Roo.decode(xhr.responseText);
29296 if(!response.success){
29298 this.fireEvent('exception', this, xhr);
29302 var file = this.renderPreview(response.data);
29304 this.files.push(file);
29308 this.fireEvent('afterupload', this, xhr);
29312 xhrOnError : function(xhr)
29314 Roo.log('xhr on error');
29316 var response = Roo.decode(xhr.responseText);
29323 process : function(file)
29325 if(this.fireEvent('process', this, file) !== false){
29326 if(this.editable && file.type.indexOf('image') != -1){
29327 this.fireEvent('edit', this, file);
29331 this.uploadStart(file, false);
29338 uploadStart : function(file, crop)
29340 this.xhr = new XMLHttpRequest();
29342 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29347 file.xhr = this.xhr;
29349 this.managerEl.createChild({
29351 cls : 'roo-document-manager-loading',
29355 tooltip : file.name,
29356 cls : 'roo-document-manager-thumb',
29357 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29363 this.xhr.open(this.method, this.url, true);
29366 "Accept": "application/json",
29367 "Cache-Control": "no-cache",
29368 "X-Requested-With": "XMLHttpRequest"
29371 for (var headerName in headers) {
29372 var headerValue = headers[headerName];
29374 this.xhr.setRequestHeader(headerName, headerValue);
29380 this.xhr.onload = function()
29382 _this.xhrOnLoad(_this.xhr);
29385 this.xhr.onerror = function()
29387 _this.xhrOnError(_this.xhr);
29390 var formData = new FormData();
29392 formData.append('returnHTML', 'NO');
29395 formData.append('crop', crop);
29398 formData.append(this.paramName, file, file.name);
29405 if(this.fireEvent('prepare', this, formData, options) != false){
29407 if(options.manually){
29411 this.xhr.send(formData);
29415 this.uploadCancel();
29418 uploadCancel : function()
29424 this.delegates = [];
29426 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29433 renderPreview : function(file)
29435 if(typeof(file.target) != 'undefined' && file.target){
29439 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29441 var previewEl = this.managerEl.createChild({
29443 cls : 'roo-document-manager-preview',
29447 tooltip : file[this.toolTipName],
29448 cls : 'roo-document-manager-thumb',
29449 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29454 html : '<i class="fa fa-times-circle"></i>'
29459 var close = previewEl.select('button.close', true).first();
29461 close.on('click', this.onRemove, this, file);
29463 file.target = previewEl;
29465 var image = previewEl.select('img', true).first();
29469 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29471 image.on('click', this.onClick, this, file);
29473 this.fireEvent('previewrendered', this, file);
29479 onPreviewLoad : function(file, image)
29481 if(typeof(file.target) == 'undefined' || !file.target){
29485 var width = image.dom.naturalWidth || image.dom.width;
29486 var height = image.dom.naturalHeight || image.dom.height;
29488 if(!this.previewResize) {
29492 if(width > height){
29493 file.target.addClass('wide');
29497 file.target.addClass('tall');
29502 uploadFromSource : function(file, crop)
29504 this.xhr = new XMLHttpRequest();
29506 this.managerEl.createChild({
29508 cls : 'roo-document-manager-loading',
29512 tooltip : file.name,
29513 cls : 'roo-document-manager-thumb',
29514 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29520 this.xhr.open(this.method, this.url, true);
29523 "Accept": "application/json",
29524 "Cache-Control": "no-cache",
29525 "X-Requested-With": "XMLHttpRequest"
29528 for (var headerName in headers) {
29529 var headerValue = headers[headerName];
29531 this.xhr.setRequestHeader(headerName, headerValue);
29537 this.xhr.onload = function()
29539 _this.xhrOnLoad(_this.xhr);
29542 this.xhr.onerror = function()
29544 _this.xhrOnError(_this.xhr);
29547 var formData = new FormData();
29549 formData.append('returnHTML', 'NO');
29551 formData.append('crop', crop);
29553 if(typeof(file.filename) != 'undefined'){
29554 formData.append('filename', file.filename);
29557 if(typeof(file.mimetype) != 'undefined'){
29558 formData.append('mimetype', file.mimetype);
29563 if(this.fireEvent('prepare', this, formData) != false){
29564 this.xhr.send(formData);
29574 * @class Roo.bootstrap.DocumentViewer
29575 * @extends Roo.bootstrap.Component
29576 * Bootstrap DocumentViewer class
29577 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29578 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29581 * Create a new DocumentViewer
29582 * @param {Object} config The config object
29585 Roo.bootstrap.DocumentViewer = function(config){
29586 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29591 * Fire after initEvent
29592 * @param {Roo.bootstrap.DocumentViewer} this
29598 * @param {Roo.bootstrap.DocumentViewer} this
29603 * Fire after download button
29604 * @param {Roo.bootstrap.DocumentViewer} this
29609 * Fire after trash button
29610 * @param {Roo.bootstrap.DocumentViewer} this
29617 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29619 showDownload : true,
29623 getAutoCreate : function()
29627 cls : 'roo-document-viewer',
29631 cls : 'roo-document-viewer-body',
29635 cls : 'roo-document-viewer-thumb',
29639 cls : 'roo-document-viewer-image'
29647 cls : 'roo-document-viewer-footer',
29650 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29654 cls : 'btn-group roo-document-viewer-download',
29658 cls : 'btn btn-default',
29659 html : '<i class="fa fa-download"></i>'
29665 cls : 'btn-group roo-document-viewer-trash',
29669 cls : 'btn btn-default',
29670 html : '<i class="fa fa-trash"></i>'
29683 initEvents : function()
29685 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29686 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29688 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29689 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29691 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29692 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29694 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29695 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29697 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29698 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29700 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29701 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29703 this.bodyEl.on('click', this.onClick, this);
29704 this.downloadBtn.on('click', this.onDownload, this);
29705 this.trashBtn.on('click', this.onTrash, this);
29707 this.downloadBtn.hide();
29708 this.trashBtn.hide();
29710 if(this.showDownload){
29711 this.downloadBtn.show();
29714 if(this.showTrash){
29715 this.trashBtn.show();
29718 if(!this.showDownload && !this.showTrash) {
29719 this.footerEl.hide();
29724 initial : function()
29726 this.fireEvent('initial', this);
29730 onClick : function(e)
29732 e.preventDefault();
29734 this.fireEvent('click', this);
29737 onDownload : function(e)
29739 e.preventDefault();
29741 this.fireEvent('download', this);
29744 onTrash : function(e)
29746 e.preventDefault();
29748 this.fireEvent('trash', this);
29760 * @class Roo.bootstrap.NavProgressBar
29761 * @extends Roo.bootstrap.Component
29762 * Bootstrap NavProgressBar class
29765 * Create a new nav progress bar
29766 * @param {Object} config The config object
29769 Roo.bootstrap.NavProgressBar = function(config){
29770 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29772 this.bullets = this.bullets || [];
29774 // Roo.bootstrap.NavProgressBar.register(this);
29778 * Fires when the active item changes
29779 * @param {Roo.bootstrap.NavProgressBar} this
29780 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29781 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29788 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29793 getAutoCreate : function()
29795 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29799 cls : 'roo-navigation-bar-group',
29803 cls : 'roo-navigation-top-bar'
29807 cls : 'roo-navigation-bullets-bar',
29811 cls : 'roo-navigation-bar'
29818 cls : 'roo-navigation-bottom-bar'
29828 initEvents: function()
29833 onRender : function(ct, position)
29835 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29837 if(this.bullets.length){
29838 Roo.each(this.bullets, function(b){
29847 addItem : function(cfg)
29849 var item = new Roo.bootstrap.NavProgressItem(cfg);
29851 item.parentId = this.id;
29852 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29855 var top = new Roo.bootstrap.Element({
29857 cls : 'roo-navigation-bar-text'
29860 var bottom = new Roo.bootstrap.Element({
29862 cls : 'roo-navigation-bar-text'
29865 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29866 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29868 var topText = new Roo.bootstrap.Element({
29870 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29873 var bottomText = new Roo.bootstrap.Element({
29875 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29878 topText.onRender(top.el, null);
29879 bottomText.onRender(bottom.el, null);
29882 item.bottomEl = bottom;
29885 this.barItems.push(item);
29890 getActive : function()
29892 var active = false;
29894 Roo.each(this.barItems, function(v){
29896 if (!v.isActive()) {
29908 setActiveItem : function(item)
29912 Roo.each(this.barItems, function(v){
29913 if (v.rid == item.rid) {
29917 if (v.isActive()) {
29918 v.setActive(false);
29923 item.setActive(true);
29925 this.fireEvent('changed', this, item, prev);
29928 getBarItem: function(rid)
29932 Roo.each(this.barItems, function(e) {
29933 if (e.rid != rid) {
29944 indexOfItem : function(item)
29948 Roo.each(this.barItems, function(v, i){
29950 if (v.rid != item.rid) {
29961 setActiveNext : function()
29963 var i = this.indexOfItem(this.getActive());
29965 if (i > this.barItems.length) {
29969 this.setActiveItem(this.barItems[i+1]);
29972 setActivePrev : function()
29974 var i = this.indexOfItem(this.getActive());
29980 this.setActiveItem(this.barItems[i-1]);
29983 format : function()
29985 if(!this.barItems.length){
29989 var width = 100 / this.barItems.length;
29991 Roo.each(this.barItems, function(i){
29992 i.el.setStyle('width', width + '%');
29993 i.topEl.el.setStyle('width', width + '%');
29994 i.bottomEl.el.setStyle('width', width + '%');
30003 * Nav Progress Item
30008 * @class Roo.bootstrap.NavProgressItem
30009 * @extends Roo.bootstrap.Component
30010 * Bootstrap NavProgressItem class
30011 * @cfg {String} rid the reference id
30012 * @cfg {Boolean} active (true|false) Is item active default false
30013 * @cfg {Boolean} disabled (true|false) Is item active default false
30014 * @cfg {String} html
30015 * @cfg {String} position (top|bottom) text position default bottom
30016 * @cfg {String} icon show icon instead of number
30019 * Create a new NavProgressItem
30020 * @param {Object} config The config object
30022 Roo.bootstrap.NavProgressItem = function(config){
30023 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30028 * The raw click event for the entire grid.
30029 * @param {Roo.bootstrap.NavProgressItem} this
30030 * @param {Roo.EventObject} e
30037 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30043 position : 'bottom',
30046 getAutoCreate : function()
30048 var iconCls = 'roo-navigation-bar-item-icon';
30050 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30054 cls: 'roo-navigation-bar-item',
30064 cfg.cls += ' active';
30067 cfg.cls += ' disabled';
30073 disable : function()
30075 this.setDisabled(true);
30078 enable : function()
30080 this.setDisabled(false);
30083 initEvents: function()
30085 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30087 this.iconEl.on('click', this.onClick, this);
30090 onClick : function(e)
30092 e.preventDefault();
30098 if(this.fireEvent('click', this, e) === false){
30102 this.parent().setActiveItem(this);
30105 isActive: function ()
30107 return this.active;
30110 setActive : function(state)
30112 if(this.active == state){
30116 this.active = state;
30119 this.el.addClass('active');
30123 this.el.removeClass('active');
30128 setDisabled : function(state)
30130 if(this.disabled == state){
30134 this.disabled = state;
30137 this.el.addClass('disabled');
30141 this.el.removeClass('disabled');
30144 tooltipEl : function()
30146 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30159 * @class Roo.bootstrap.FieldLabel
30160 * @extends Roo.bootstrap.Component
30161 * Bootstrap FieldLabel class
30162 * @cfg {String} html contents of the element
30163 * @cfg {String} tag tag of the element default label
30164 * @cfg {String} cls class of the element
30165 * @cfg {String} target label target
30166 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30167 * @cfg {String} invalidClass default "text-warning"
30168 * @cfg {String} validClass default "text-success"
30169 * @cfg {String} iconTooltip default "This field is required"
30170 * @cfg {String} indicatorpos (left|right) default left
30173 * Create a new FieldLabel
30174 * @param {Object} config The config object
30177 Roo.bootstrap.FieldLabel = function(config){
30178 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30183 * Fires after the field has been marked as invalid.
30184 * @param {Roo.form.FieldLabel} this
30185 * @param {String} msg The validation message
30190 * Fires after the field has been validated with no errors.
30191 * @param {Roo.form.FieldLabel} this
30197 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30204 invalidClass : 'has-warning',
30205 validClass : 'has-success',
30206 iconTooltip : 'This field is required',
30207 indicatorpos : 'left',
30209 getAutoCreate : function(){
30212 if (!this.allowBlank) {
30218 cls : 'roo-bootstrap-field-label ' + this.cls,
30223 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30224 tooltip : this.iconTooltip
30233 if(this.indicatorpos == 'right'){
30236 cls : 'roo-bootstrap-field-label ' + this.cls,
30245 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30246 tooltip : this.iconTooltip
30255 initEvents: function()
30257 Roo.bootstrap.Element.superclass.initEvents.call(this);
30259 this.indicator = this.indicatorEl();
30261 if(this.indicator){
30262 this.indicator.removeClass('visible');
30263 this.indicator.addClass('invisible');
30266 Roo.bootstrap.FieldLabel.register(this);
30269 indicatorEl : function()
30271 var indicator = this.el.select('i.roo-required-indicator',true).first();
30282 * Mark this field as valid
30284 markValid : function()
30286 if(this.indicator){
30287 this.indicator.removeClass('visible');
30288 this.indicator.addClass('invisible');
30291 this.el.removeClass(this.invalidClass);
30293 this.el.addClass(this.validClass);
30295 this.fireEvent('valid', this);
30299 * Mark this field as invalid
30300 * @param {String} msg The validation message
30302 markInvalid : function(msg)
30304 if(this.indicator){
30305 this.indicator.removeClass('invisible');
30306 this.indicator.addClass('visible');
30309 this.el.removeClass(this.validClass);
30311 this.el.addClass(this.invalidClass);
30313 this.fireEvent('invalid', this, msg);
30319 Roo.apply(Roo.bootstrap.FieldLabel, {
30324 * register a FieldLabel Group
30325 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30327 register : function(label)
30329 if(this.groups.hasOwnProperty(label.target)){
30333 this.groups[label.target] = label;
30337 * fetch a FieldLabel Group based on the target
30338 * @param {string} target
30339 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30341 get: function(target) {
30342 if (typeof(this.groups[target]) == 'undefined') {
30346 return this.groups[target] ;
30355 * page DateSplitField.
30361 * @class Roo.bootstrap.DateSplitField
30362 * @extends Roo.bootstrap.Component
30363 * Bootstrap DateSplitField class
30364 * @cfg {string} fieldLabel - the label associated
30365 * @cfg {Number} labelWidth set the width of label (0-12)
30366 * @cfg {String} labelAlign (top|left)
30367 * @cfg {Boolean} dayAllowBlank (true|false) default false
30368 * @cfg {Boolean} monthAllowBlank (true|false) default false
30369 * @cfg {Boolean} yearAllowBlank (true|false) default false
30370 * @cfg {string} dayPlaceholder
30371 * @cfg {string} monthPlaceholder
30372 * @cfg {string} yearPlaceholder
30373 * @cfg {string} dayFormat default 'd'
30374 * @cfg {string} monthFormat default 'm'
30375 * @cfg {string} yearFormat default 'Y'
30376 * @cfg {Number} labellg set the width of label (1-12)
30377 * @cfg {Number} labelmd set the width of label (1-12)
30378 * @cfg {Number} labelsm set the width of label (1-12)
30379 * @cfg {Number} labelxs set the width of label (1-12)
30383 * Create a new DateSplitField
30384 * @param {Object} config The config object
30387 Roo.bootstrap.DateSplitField = function(config){
30388 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30394 * getting the data of years
30395 * @param {Roo.bootstrap.DateSplitField} this
30396 * @param {Object} years
30401 * getting the data of days
30402 * @param {Roo.bootstrap.DateSplitField} this
30403 * @param {Object} days
30408 * Fires after the field has been marked as invalid.
30409 * @param {Roo.form.Field} this
30410 * @param {String} msg The validation message
30415 * Fires after the field has been validated with no errors.
30416 * @param {Roo.form.Field} this
30422 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30425 labelAlign : 'top',
30427 dayAllowBlank : false,
30428 monthAllowBlank : false,
30429 yearAllowBlank : false,
30430 dayPlaceholder : '',
30431 monthPlaceholder : '',
30432 yearPlaceholder : '',
30436 isFormField : true,
30442 getAutoCreate : function()
30446 cls : 'row roo-date-split-field-group',
30451 cls : 'form-hidden-field roo-date-split-field-group-value',
30457 var labelCls = 'col-md-12';
30458 var contentCls = 'col-md-4';
30460 if(this.fieldLabel){
30464 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30468 html : this.fieldLabel
30473 if(this.labelAlign == 'left'){
30475 if(this.labelWidth > 12){
30476 label.style = "width: " + this.labelWidth + 'px';
30479 if(this.labelWidth < 13 && this.labelmd == 0){
30480 this.labelmd = this.labelWidth;
30483 if(this.labellg > 0){
30484 labelCls = ' col-lg-' + this.labellg;
30485 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30488 if(this.labelmd > 0){
30489 labelCls = ' col-md-' + this.labelmd;
30490 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30493 if(this.labelsm > 0){
30494 labelCls = ' col-sm-' + this.labelsm;
30495 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30498 if(this.labelxs > 0){
30499 labelCls = ' col-xs-' + this.labelxs;
30500 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30504 label.cls += ' ' + labelCls;
30506 cfg.cn.push(label);
30509 Roo.each(['day', 'month', 'year'], function(t){
30512 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30519 inputEl: function ()
30521 return this.el.select('.roo-date-split-field-group-value', true).first();
30524 onRender : function(ct, position)
30528 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30530 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30532 this.dayField = new Roo.bootstrap.ComboBox({
30533 allowBlank : this.dayAllowBlank,
30534 alwaysQuery : true,
30535 displayField : 'value',
30538 forceSelection : true,
30540 placeholder : this.dayPlaceholder,
30541 selectOnFocus : true,
30542 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30543 triggerAction : 'all',
30545 valueField : 'value',
30546 store : new Roo.data.SimpleStore({
30547 data : (function() {
30549 _this.fireEvent('days', _this, days);
30552 fields : [ 'value' ]
30555 select : function (_self, record, index)
30557 _this.setValue(_this.getValue());
30562 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30564 this.monthField = new Roo.bootstrap.MonthField({
30565 after : '<i class=\"fa fa-calendar\"></i>',
30566 allowBlank : this.monthAllowBlank,
30567 placeholder : this.monthPlaceholder,
30570 render : function (_self)
30572 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30573 e.preventDefault();
30577 select : function (_self, oldvalue, newvalue)
30579 _this.setValue(_this.getValue());
30584 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30586 this.yearField = new Roo.bootstrap.ComboBox({
30587 allowBlank : this.yearAllowBlank,
30588 alwaysQuery : true,
30589 displayField : 'value',
30592 forceSelection : true,
30594 placeholder : this.yearPlaceholder,
30595 selectOnFocus : true,
30596 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30597 triggerAction : 'all',
30599 valueField : 'value',
30600 store : new Roo.data.SimpleStore({
30601 data : (function() {
30603 _this.fireEvent('years', _this, years);
30606 fields : [ 'value' ]
30609 select : function (_self, record, index)
30611 _this.setValue(_this.getValue());
30616 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30619 setValue : function(v, format)
30621 this.inputEl.dom.value = v;
30623 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30625 var d = Date.parseDate(v, f);
30632 this.setDay(d.format(this.dayFormat));
30633 this.setMonth(d.format(this.monthFormat));
30634 this.setYear(d.format(this.yearFormat));
30641 setDay : function(v)
30643 this.dayField.setValue(v);
30644 this.inputEl.dom.value = this.getValue();
30649 setMonth : function(v)
30651 this.monthField.setValue(v, true);
30652 this.inputEl.dom.value = this.getValue();
30657 setYear : function(v)
30659 this.yearField.setValue(v);
30660 this.inputEl.dom.value = this.getValue();
30665 getDay : function()
30667 return this.dayField.getValue();
30670 getMonth : function()
30672 return this.monthField.getValue();
30675 getYear : function()
30677 return this.yearField.getValue();
30680 getValue : function()
30682 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30684 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30694 this.inputEl.dom.value = '';
30699 validate : function()
30701 var d = this.dayField.validate();
30702 var m = this.monthField.validate();
30703 var y = this.yearField.validate();
30708 (!this.dayAllowBlank && !d) ||
30709 (!this.monthAllowBlank && !m) ||
30710 (!this.yearAllowBlank && !y)
30715 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30724 this.markInvalid();
30729 markValid : function()
30732 var label = this.el.select('label', true).first();
30733 var icon = this.el.select('i.fa-star', true).first();
30739 this.fireEvent('valid', this);
30743 * Mark this field as invalid
30744 * @param {String} msg The validation message
30746 markInvalid : function(msg)
30749 var label = this.el.select('label', true).first();
30750 var icon = this.el.select('i.fa-star', true).first();
30752 if(label && !icon){
30753 this.el.select('.roo-date-split-field-label', true).createChild({
30755 cls : 'text-danger fa fa-lg fa-star',
30756 tooltip : 'This field is required',
30757 style : 'margin-right:5px;'
30761 this.fireEvent('invalid', this, msg);
30764 clearInvalid : function()
30766 var label = this.el.select('label', true).first();
30767 var icon = this.el.select('i.fa-star', true).first();
30773 this.fireEvent('valid', this);
30776 getName: function()
30786 * http://masonry.desandro.com
30788 * The idea is to render all the bricks based on vertical width...
30790 * The original code extends 'outlayer' - we might need to use that....
30796 * @class Roo.bootstrap.LayoutMasonry
30797 * @extends Roo.bootstrap.Component
30798 * Bootstrap Layout Masonry class
30801 * Create a new Element
30802 * @param {Object} config The config object
30805 Roo.bootstrap.LayoutMasonry = function(config){
30807 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30811 Roo.bootstrap.LayoutMasonry.register(this);
30817 * Fire after layout the items
30818 * @param {Roo.bootstrap.LayoutMasonry} this
30819 * @param {Roo.EventObject} e
30826 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30829 * @cfg {Boolean} isLayoutInstant = no animation?
30831 isLayoutInstant : false, // needed?
30834 * @cfg {Number} boxWidth width of the columns
30839 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30844 * @cfg {Number} padWidth padding below box..
30849 * @cfg {Number} gutter gutter width..
30854 * @cfg {Number} maxCols maximum number of columns
30860 * @cfg {Boolean} isAutoInitial defalut true
30862 isAutoInitial : true,
30867 * @cfg {Boolean} isHorizontal defalut false
30869 isHorizontal : false,
30871 currentSize : null,
30877 bricks: null, //CompositeElement
30881 _isLayoutInited : false,
30883 // isAlternative : false, // only use for vertical layout...
30886 * @cfg {Number} alternativePadWidth padding below box..
30888 alternativePadWidth : 50,
30890 selectedBrick : [],
30892 getAutoCreate : function(){
30894 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30898 cls: 'blog-masonary-wrapper ' + this.cls,
30900 cls : 'mas-boxes masonary'
30907 getChildContainer: function( )
30909 if (this.boxesEl) {
30910 return this.boxesEl;
30913 this.boxesEl = this.el.select('.mas-boxes').first();
30915 return this.boxesEl;
30919 initEvents : function()
30923 if(this.isAutoInitial){
30924 Roo.log('hook children rendered');
30925 this.on('childrenrendered', function() {
30926 Roo.log('children rendered');
30932 initial : function()
30934 this.selectedBrick = [];
30936 this.currentSize = this.el.getBox(true);
30938 Roo.EventManager.onWindowResize(this.resize, this);
30940 if(!this.isAutoInitial){
30948 //this.layout.defer(500,this);
30952 resize : function()
30954 var cs = this.el.getBox(true);
30957 this.currentSize.width == cs.width &&
30958 this.currentSize.x == cs.x &&
30959 this.currentSize.height == cs.height &&
30960 this.currentSize.y == cs.y
30962 Roo.log("no change in with or X or Y");
30966 this.currentSize = cs;
30972 layout : function()
30974 this._resetLayout();
30976 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30978 this.layoutItems( isInstant );
30980 this._isLayoutInited = true;
30982 this.fireEvent('layout', this);
30986 _resetLayout : function()
30988 if(this.isHorizontal){
30989 this.horizontalMeasureColumns();
30993 this.verticalMeasureColumns();
30997 verticalMeasureColumns : function()
30999 this.getContainerWidth();
31001 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31002 // this.colWidth = Math.floor(this.containerWidth * 0.8);
31006 var boxWidth = this.boxWidth + this.padWidth;
31008 if(this.containerWidth < this.boxWidth){
31009 boxWidth = this.containerWidth
31012 var containerWidth = this.containerWidth;
31014 var cols = Math.floor(containerWidth / boxWidth);
31016 this.cols = Math.max( cols, 1 );
31018 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31020 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31022 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31024 this.colWidth = boxWidth + avail - this.padWidth;
31026 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31027 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31030 horizontalMeasureColumns : function()
31032 this.getContainerWidth();
31034 var boxWidth = this.boxWidth;
31036 if(this.containerWidth < boxWidth){
31037 boxWidth = this.containerWidth;
31040 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31042 this.el.setHeight(boxWidth);
31046 getContainerWidth : function()
31048 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31051 layoutItems : function( isInstant )
31053 Roo.log(this.bricks);
31055 var items = Roo.apply([], this.bricks);
31057 if(this.isHorizontal){
31058 this._horizontalLayoutItems( items , isInstant );
31062 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31063 // this._verticalAlternativeLayoutItems( items , isInstant );
31067 this._verticalLayoutItems( items , isInstant );
31071 _verticalLayoutItems : function ( items , isInstant)
31073 if ( !items || !items.length ) {
31078 ['xs', 'xs', 'xs', 'tall'],
31079 ['xs', 'xs', 'tall'],
31080 ['xs', 'xs', 'sm'],
31081 ['xs', 'xs', 'xs'],
31087 ['sm', 'xs', 'xs'],
31091 ['tall', 'xs', 'xs', 'xs'],
31092 ['tall', 'xs', 'xs'],
31104 Roo.each(items, function(item, k){
31106 switch (item.size) {
31107 // these layouts take up a full box,
31118 boxes.push([item]);
31141 var filterPattern = function(box, length)
31149 var pattern = box.slice(0, length);
31153 Roo.each(pattern, function(i){
31154 format.push(i.size);
31157 Roo.each(standard, function(s){
31159 if(String(s) != String(format)){
31168 if(!match && length == 1){
31173 filterPattern(box, length - 1);
31177 queue.push(pattern);
31179 box = box.slice(length, box.length);
31181 filterPattern(box, 4);
31187 Roo.each(boxes, function(box, k){
31193 if(box.length == 1){
31198 filterPattern(box, 4);
31202 this._processVerticalLayoutQueue( queue, isInstant );
31206 // _verticalAlternativeLayoutItems : function( items , isInstant )
31208 // if ( !items || !items.length ) {
31212 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31216 _horizontalLayoutItems : function ( items , isInstant)
31218 if ( !items || !items.length || items.length < 3) {
31224 var eItems = items.slice(0, 3);
31226 items = items.slice(3, items.length);
31229 ['xs', 'xs', 'xs', 'wide'],
31230 ['xs', 'xs', 'wide'],
31231 ['xs', 'xs', 'sm'],
31232 ['xs', 'xs', 'xs'],
31238 ['sm', 'xs', 'xs'],
31242 ['wide', 'xs', 'xs', 'xs'],
31243 ['wide', 'xs', 'xs'],
31256 Roo.each(items, function(item, k){
31258 switch (item.size) {
31269 boxes.push([item]);
31293 var filterPattern = function(box, length)
31301 var pattern = box.slice(0, length);
31305 Roo.each(pattern, function(i){
31306 format.push(i.size);
31309 Roo.each(standard, function(s){
31311 if(String(s) != String(format)){
31320 if(!match && length == 1){
31325 filterPattern(box, length - 1);
31329 queue.push(pattern);
31331 box = box.slice(length, box.length);
31333 filterPattern(box, 4);
31339 Roo.each(boxes, function(box, k){
31345 if(box.length == 1){
31350 filterPattern(box, 4);
31357 var pos = this.el.getBox(true);
31361 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31363 var hit_end = false;
31365 Roo.each(queue, function(box){
31369 Roo.each(box, function(b){
31371 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31381 Roo.each(box, function(b){
31383 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31386 mx = Math.max(mx, b.x);
31390 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31394 Roo.each(box, function(b){
31396 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31410 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31413 /** Sets position of item in DOM
31414 * @param {Element} item
31415 * @param {Number} x - horizontal position
31416 * @param {Number} y - vertical position
31417 * @param {Boolean} isInstant - disables transitions
31419 _processVerticalLayoutQueue : function( queue, isInstant )
31421 var pos = this.el.getBox(true);
31426 for (var i = 0; i < this.cols; i++){
31430 Roo.each(queue, function(box, k){
31432 var col = k % this.cols;
31434 Roo.each(box, function(b,kk){
31436 b.el.position('absolute');
31438 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31439 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31441 if(b.size == 'md-left' || b.size == 'md-right'){
31442 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31443 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31446 b.el.setWidth(width);
31447 b.el.setHeight(height);
31449 b.el.select('iframe',true).setSize(width,height);
31453 for (var i = 0; i < this.cols; i++){
31455 if(maxY[i] < maxY[col]){
31460 col = Math.min(col, i);
31464 x = pos.x + col * (this.colWidth + this.padWidth);
31468 var positions = [];
31470 switch (box.length){
31472 positions = this.getVerticalOneBoxColPositions(x, y, box);
31475 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31478 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31481 positions = this.getVerticalFourBoxColPositions(x, y, box);
31487 Roo.each(box, function(b,kk){
31489 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31491 var sz = b.el.getSize();
31493 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31501 for (var i = 0; i < this.cols; i++){
31502 mY = Math.max(mY, maxY[i]);
31505 this.el.setHeight(mY - pos.y);
31509 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31511 // var pos = this.el.getBox(true);
31514 // var maxX = pos.right;
31516 // var maxHeight = 0;
31518 // Roo.each(items, function(item, k){
31522 // item.el.position('absolute');
31524 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31526 // item.el.setWidth(width);
31528 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31530 // item.el.setHeight(height);
31533 // item.el.setXY([x, y], isInstant ? false : true);
31535 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31538 // y = y + height + this.alternativePadWidth;
31540 // maxHeight = maxHeight + height + this.alternativePadWidth;
31544 // this.el.setHeight(maxHeight);
31548 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31550 var pos = this.el.getBox(true);
31555 var maxX = pos.right;
31557 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31559 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31561 Roo.each(queue, function(box, k){
31563 Roo.each(box, function(b, kk){
31565 b.el.position('absolute');
31567 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31568 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31570 if(b.size == 'md-left' || b.size == 'md-right'){
31571 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31572 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31575 b.el.setWidth(width);
31576 b.el.setHeight(height);
31584 var positions = [];
31586 switch (box.length){
31588 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31591 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31594 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31597 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31603 Roo.each(box, function(b,kk){
31605 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31607 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31615 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31617 Roo.each(eItems, function(b,k){
31619 b.size = (k == 0) ? 'sm' : 'xs';
31620 b.x = (k == 0) ? 2 : 1;
31621 b.y = (k == 0) ? 2 : 1;
31623 b.el.position('absolute');
31625 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31627 b.el.setWidth(width);
31629 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31631 b.el.setHeight(height);
31635 var positions = [];
31638 x : maxX - this.unitWidth * 2 - this.gutter,
31643 x : maxX - this.unitWidth,
31644 y : minY + (this.unitWidth + this.gutter) * 2
31648 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31652 Roo.each(eItems, function(b,k){
31654 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31660 getVerticalOneBoxColPositions : function(x, y, box)
31664 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31666 if(box[0].size == 'md-left'){
31670 if(box[0].size == 'md-right'){
31675 x : x + (this.unitWidth + this.gutter) * rand,
31682 getVerticalTwoBoxColPositions : function(x, y, box)
31686 if(box[0].size == 'xs'){
31690 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31694 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31708 x : x + (this.unitWidth + this.gutter) * 2,
31709 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31716 getVerticalThreeBoxColPositions : function(x, y, box)
31720 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31728 x : x + (this.unitWidth + this.gutter) * 1,
31733 x : x + (this.unitWidth + this.gutter) * 2,
31741 if(box[0].size == 'xs' && box[1].size == 'xs'){
31750 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31754 x : x + (this.unitWidth + this.gutter) * 1,
31768 x : x + (this.unitWidth + this.gutter) * 2,
31773 x : x + (this.unitWidth + this.gutter) * 2,
31774 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31781 getVerticalFourBoxColPositions : function(x, y, box)
31785 if(box[0].size == 'xs'){
31794 y : y + (this.unitHeight + this.gutter) * 1
31799 y : y + (this.unitHeight + this.gutter) * 2
31803 x : x + (this.unitWidth + this.gutter) * 1,
31817 x : x + (this.unitWidth + this.gutter) * 2,
31822 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31823 y : y + (this.unitHeight + this.gutter) * 1
31827 x : x + (this.unitWidth + this.gutter) * 2,
31828 y : y + (this.unitWidth + this.gutter) * 2
31835 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31839 if(box[0].size == 'md-left'){
31841 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31848 if(box[0].size == 'md-right'){
31850 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31851 y : minY + (this.unitWidth + this.gutter) * 1
31857 var rand = Math.floor(Math.random() * (4 - box[0].y));
31860 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31861 y : minY + (this.unitWidth + this.gutter) * rand
31868 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31872 if(box[0].size == 'xs'){
31875 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31880 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31881 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31889 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31894 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31895 y : minY + (this.unitWidth + this.gutter) * 2
31902 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31906 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31914 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31915 y : minY + (this.unitWidth + this.gutter) * 1
31919 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31920 y : minY + (this.unitWidth + this.gutter) * 2
31927 if(box[0].size == 'xs' && box[1].size == 'xs'){
31930 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31935 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31940 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31941 y : minY + (this.unitWidth + this.gutter) * 1
31949 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31954 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31955 y : minY + (this.unitWidth + this.gutter) * 2
31959 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31960 y : minY + (this.unitWidth + this.gutter) * 2
31967 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31971 if(box[0].size == 'xs'){
31974 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31979 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31984 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),
31989 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31990 y : minY + (this.unitWidth + this.gutter) * 1
31998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32003 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004 y : minY + (this.unitWidth + this.gutter) * 2
32008 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32009 y : minY + (this.unitWidth + this.gutter) * 2
32013 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),
32014 y : minY + (this.unitWidth + this.gutter) * 2
32022 * remove a Masonry Brick
32023 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32025 removeBrick : function(brick_id)
32031 for (var i = 0; i<this.bricks.length; i++) {
32032 if (this.bricks[i].id == brick_id) {
32033 this.bricks.splice(i,1);
32034 this.el.dom.removeChild(Roo.get(brick_id).dom);
32041 * adds a Masonry Brick
32042 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32044 addBrick : function(cfg)
32046 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32047 //this.register(cn);
32048 cn.parentId = this.id;
32049 cn.render(this.el);
32054 * register a Masonry Brick
32055 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32058 register : function(brick)
32060 this.bricks.push(brick);
32061 brick.masonryId = this.id;
32065 * clear all the Masonry Brick
32067 clearAll : function()
32070 //this.getChildContainer().dom.innerHTML = "";
32071 this.el.dom.innerHTML = '';
32074 getSelected : function()
32076 if (!this.selectedBrick) {
32080 return this.selectedBrick;
32084 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32088 * register a Masonry Layout
32089 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32092 register : function(layout)
32094 this.groups[layout.id] = layout;
32097 * fetch a Masonry Layout based on the masonry layout ID
32098 * @param {string} the masonry layout to add
32099 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32102 get: function(layout_id) {
32103 if (typeof(this.groups[layout_id]) == 'undefined') {
32106 return this.groups[layout_id] ;
32118 * http://masonry.desandro.com
32120 * The idea is to render all the bricks based on vertical width...
32122 * The original code extends 'outlayer' - we might need to use that....
32128 * @class Roo.bootstrap.LayoutMasonryAuto
32129 * @extends Roo.bootstrap.Component
32130 * Bootstrap Layout Masonry class
32133 * Create a new Element
32134 * @param {Object} config The config object
32137 Roo.bootstrap.LayoutMasonryAuto = function(config){
32138 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32141 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32144 * @cfg {Boolean} isFitWidth - resize the width..
32146 isFitWidth : false, // options..
32148 * @cfg {Boolean} isOriginLeft = left align?
32150 isOriginLeft : true,
32152 * @cfg {Boolean} isOriginTop = top align?
32154 isOriginTop : false,
32156 * @cfg {Boolean} isLayoutInstant = no animation?
32158 isLayoutInstant : false, // needed?
32160 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32162 isResizingContainer : true,
32164 * @cfg {Number} columnWidth width of the columns
32170 * @cfg {Number} maxCols maximum number of columns
32175 * @cfg {Number} padHeight padding below box..
32181 * @cfg {Boolean} isAutoInitial defalut true
32184 isAutoInitial : true,
32190 initialColumnWidth : 0,
32191 currentSize : null,
32193 colYs : null, // array.
32200 bricks: null, //CompositeElement
32201 cols : 0, // array?
32202 // element : null, // wrapped now this.el
32203 _isLayoutInited : null,
32206 getAutoCreate : function(){
32210 cls: 'blog-masonary-wrapper ' + this.cls,
32212 cls : 'mas-boxes masonary'
32219 getChildContainer: function( )
32221 if (this.boxesEl) {
32222 return this.boxesEl;
32225 this.boxesEl = this.el.select('.mas-boxes').first();
32227 return this.boxesEl;
32231 initEvents : function()
32235 if(this.isAutoInitial){
32236 Roo.log('hook children rendered');
32237 this.on('childrenrendered', function() {
32238 Roo.log('children rendered');
32245 initial : function()
32247 this.reloadItems();
32249 this.currentSize = this.el.getBox(true);
32251 /// was window resize... - let's see if this works..
32252 Roo.EventManager.onWindowResize(this.resize, this);
32254 if(!this.isAutoInitial){
32259 this.layout.defer(500,this);
32262 reloadItems: function()
32264 this.bricks = this.el.select('.masonry-brick', true);
32266 this.bricks.each(function(b) {
32267 //Roo.log(b.getSize());
32268 if (!b.attr('originalwidth')) {
32269 b.attr('originalwidth', b.getSize().width);
32274 Roo.log(this.bricks.elements.length);
32277 resize : function()
32280 var cs = this.el.getBox(true);
32282 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32283 Roo.log("no change in with or X");
32286 this.currentSize = cs;
32290 layout : function()
32293 this._resetLayout();
32294 //this._manageStamps();
32296 // don't animate first layout
32297 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32298 this.layoutItems( isInstant );
32300 // flag for initalized
32301 this._isLayoutInited = true;
32304 layoutItems : function( isInstant )
32306 //var items = this._getItemsForLayout( this.items );
32307 // original code supports filtering layout items.. we just ignore it..
32309 this._layoutItems( this.bricks , isInstant );
32311 this._postLayout();
32313 _layoutItems : function ( items , isInstant)
32315 //this.fireEvent( 'layout', this, items );
32318 if ( !items || !items.elements.length ) {
32319 // no items, emit event with empty array
32324 items.each(function(item) {
32325 Roo.log("layout item");
32327 // get x/y object from method
32328 var position = this._getItemLayoutPosition( item );
32330 position.item = item;
32331 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32332 queue.push( position );
32335 this._processLayoutQueue( queue );
32337 /** Sets position of item in DOM
32338 * @param {Element} item
32339 * @param {Number} x - horizontal position
32340 * @param {Number} y - vertical position
32341 * @param {Boolean} isInstant - disables transitions
32343 _processLayoutQueue : function( queue )
32345 for ( var i=0, len = queue.length; i < len; i++ ) {
32346 var obj = queue[i];
32347 obj.item.position('absolute');
32348 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32354 * Any logic you want to do after each layout,
32355 * i.e. size the container
32357 _postLayout : function()
32359 this.resizeContainer();
32362 resizeContainer : function()
32364 if ( !this.isResizingContainer ) {
32367 var size = this._getContainerSize();
32369 this.el.setSize(size.width,size.height);
32370 this.boxesEl.setSize(size.width,size.height);
32376 _resetLayout : function()
32378 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32379 this.colWidth = this.el.getWidth();
32380 //this.gutter = this.el.getWidth();
32382 this.measureColumns();
32388 this.colYs.push( 0 );
32394 measureColumns : function()
32396 this.getContainerWidth();
32397 // if columnWidth is 0, default to outerWidth of first item
32398 if ( !this.columnWidth ) {
32399 var firstItem = this.bricks.first();
32400 Roo.log(firstItem);
32401 this.columnWidth = this.containerWidth;
32402 if (firstItem && firstItem.attr('originalwidth') ) {
32403 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32405 // columnWidth fall back to item of first element
32406 Roo.log("set column width?");
32407 this.initialColumnWidth = this.columnWidth ;
32409 // if first elem has no width, default to size of container
32414 if (this.initialColumnWidth) {
32415 this.columnWidth = this.initialColumnWidth;
32420 // column width is fixed at the top - however if container width get's smaller we should
32423 // this bit calcs how man columns..
32425 var columnWidth = this.columnWidth += this.gutter;
32427 // calculate columns
32428 var containerWidth = this.containerWidth + this.gutter;
32430 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32431 // fix rounding errors, typically with gutters
32432 var excess = columnWidth - containerWidth % columnWidth;
32435 // if overshoot is less than a pixel, round up, otherwise floor it
32436 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32437 cols = Math[ mathMethod ]( cols );
32438 this.cols = Math.max( cols, 1 );
32439 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32441 // padding positioning..
32442 var totalColWidth = this.cols * this.columnWidth;
32443 var padavail = this.containerWidth - totalColWidth;
32444 // so for 2 columns - we need 3 'pads'
32446 var padNeeded = (1+this.cols) * this.padWidth;
32448 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32450 this.columnWidth += padExtra
32451 //this.padWidth = Math.floor(padavail / ( this.cols));
32453 // adjust colum width so that padding is fixed??
32455 // we have 3 columns ... total = width * 3
32456 // we have X left over... that should be used by
32458 //if (this.expandC) {
32466 getContainerWidth : function()
32468 /* // container is parent if fit width
32469 var container = this.isFitWidth ? this.element.parentNode : this.element;
32470 // check that this.size and size are there
32471 // IE8 triggers resize on body size change, so they might not be
32473 var size = getSize( container ); //FIXME
32474 this.containerWidth = size && size.innerWidth; //FIXME
32477 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32481 _getItemLayoutPosition : function( item ) // what is item?
32483 // we resize the item to our columnWidth..
32485 item.setWidth(this.columnWidth);
32486 item.autoBoxAdjust = false;
32488 var sz = item.getSize();
32490 // how many columns does this brick span
32491 var remainder = this.containerWidth % this.columnWidth;
32493 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32494 // round if off by 1 pixel, otherwise use ceil
32495 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32496 colSpan = Math.min( colSpan, this.cols );
32498 // normally this should be '1' as we dont' currently allow multi width columns..
32500 var colGroup = this._getColGroup( colSpan );
32501 // get the minimum Y value from the columns
32502 var minimumY = Math.min.apply( Math, colGroup );
32503 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32505 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32507 // position the brick
32509 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32510 y: this.currentSize.y + minimumY + this.padHeight
32514 // apply setHeight to necessary columns
32515 var setHeight = minimumY + sz.height + this.padHeight;
32516 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32518 var setSpan = this.cols + 1 - colGroup.length;
32519 for ( var i = 0; i < setSpan; i++ ) {
32520 this.colYs[ shortColIndex + i ] = setHeight ;
32527 * @param {Number} colSpan - number of columns the element spans
32528 * @returns {Array} colGroup
32530 _getColGroup : function( colSpan )
32532 if ( colSpan < 2 ) {
32533 // if brick spans only one column, use all the column Ys
32538 // how many different places could this brick fit horizontally
32539 var groupCount = this.cols + 1 - colSpan;
32540 // for each group potential horizontal position
32541 for ( var i = 0; i < groupCount; i++ ) {
32542 // make an array of colY values for that one group
32543 var groupColYs = this.colYs.slice( i, i + colSpan );
32544 // and get the max value of the array
32545 colGroup[i] = Math.max.apply( Math, groupColYs );
32550 _manageStamp : function( stamp )
32552 var stampSize = stamp.getSize();
32553 var offset = stamp.getBox();
32554 // get the columns that this stamp affects
32555 var firstX = this.isOriginLeft ? offset.x : offset.right;
32556 var lastX = firstX + stampSize.width;
32557 var firstCol = Math.floor( firstX / this.columnWidth );
32558 firstCol = Math.max( 0, firstCol );
32560 var lastCol = Math.floor( lastX / this.columnWidth );
32561 // lastCol should not go over if multiple of columnWidth #425
32562 lastCol -= lastX % this.columnWidth ? 0 : 1;
32563 lastCol = Math.min( this.cols - 1, lastCol );
32565 // set colYs to bottom of the stamp
32566 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32569 for ( var i = firstCol; i <= lastCol; i++ ) {
32570 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32575 _getContainerSize : function()
32577 this.maxY = Math.max.apply( Math, this.colYs );
32582 if ( this.isFitWidth ) {
32583 size.width = this._getContainerFitWidth();
32589 _getContainerFitWidth : function()
32591 var unusedCols = 0;
32592 // count unused columns
32595 if ( this.colYs[i] !== 0 ) {
32600 // fit container to columns that have been used
32601 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32604 needsResizeLayout : function()
32606 var previousWidth = this.containerWidth;
32607 this.getContainerWidth();
32608 return previousWidth !== this.containerWidth;
32623 * @class Roo.bootstrap.MasonryBrick
32624 * @extends Roo.bootstrap.Component
32625 * Bootstrap MasonryBrick class
32628 * Create a new MasonryBrick
32629 * @param {Object} config The config object
32632 Roo.bootstrap.MasonryBrick = function(config){
32634 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32636 Roo.bootstrap.MasonryBrick.register(this);
32642 * When a MasonryBrick is clcik
32643 * @param {Roo.bootstrap.MasonryBrick} this
32644 * @param {Roo.EventObject} e
32650 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32653 * @cfg {String} title
32657 * @cfg {String} html
32661 * @cfg {String} bgimage
32665 * @cfg {String} videourl
32669 * @cfg {String} cls
32673 * @cfg {String} href
32677 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32682 * @cfg {String} placetitle (center|bottom)
32687 * @cfg {Boolean} isFitContainer defalut true
32689 isFitContainer : true,
32692 * @cfg {Boolean} preventDefault defalut false
32694 preventDefault : false,
32697 * @cfg {Boolean} inverse defalut false
32699 maskInverse : false,
32701 getAutoCreate : function()
32703 if(!this.isFitContainer){
32704 return this.getSplitAutoCreate();
32707 var cls = 'masonry-brick masonry-brick-full';
32709 if(this.href.length){
32710 cls += ' masonry-brick-link';
32713 if(this.bgimage.length){
32714 cls += ' masonry-brick-image';
32717 if(this.maskInverse){
32718 cls += ' mask-inverse';
32721 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32722 cls += ' enable-mask';
32726 cls += ' masonry-' + this.size + '-brick';
32729 if(this.placetitle.length){
32731 switch (this.placetitle) {
32733 cls += ' masonry-center-title';
32736 cls += ' masonry-bottom-title';
32743 if(!this.html.length && !this.bgimage.length){
32744 cls += ' masonry-center-title';
32747 if(!this.html.length && this.bgimage.length){
32748 cls += ' masonry-bottom-title';
32753 cls += ' ' + this.cls;
32757 tag: (this.href.length) ? 'a' : 'div',
32762 cls: 'masonry-brick-mask'
32766 cls: 'masonry-brick-paragraph',
32772 if(this.href.length){
32773 cfg.href = this.href;
32776 var cn = cfg.cn[1].cn;
32778 if(this.title.length){
32781 cls: 'masonry-brick-title',
32786 if(this.html.length){
32789 cls: 'masonry-brick-text',
32794 if (!this.title.length && !this.html.length) {
32795 cfg.cn[1].cls += ' hide';
32798 if(this.bgimage.length){
32801 cls: 'masonry-brick-image-view',
32806 if(this.videourl.length){
32807 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32808 // youtube support only?
32811 cls: 'masonry-brick-image-view',
32814 allowfullscreen : true
32822 getSplitAutoCreate : function()
32824 var cls = 'masonry-brick masonry-brick-split';
32826 if(this.href.length){
32827 cls += ' masonry-brick-link';
32830 if(this.bgimage.length){
32831 cls += ' masonry-brick-image';
32835 cls += ' masonry-' + this.size + '-brick';
32838 switch (this.placetitle) {
32840 cls += ' masonry-center-title';
32843 cls += ' masonry-bottom-title';
32846 if(!this.bgimage.length){
32847 cls += ' masonry-center-title';
32850 if(this.bgimage.length){
32851 cls += ' masonry-bottom-title';
32857 cls += ' ' + this.cls;
32861 tag: (this.href.length) ? 'a' : 'div',
32866 cls: 'masonry-brick-split-head',
32870 cls: 'masonry-brick-paragraph',
32877 cls: 'masonry-brick-split-body',
32883 if(this.href.length){
32884 cfg.href = this.href;
32887 if(this.title.length){
32888 cfg.cn[0].cn[0].cn.push({
32890 cls: 'masonry-brick-title',
32895 if(this.html.length){
32896 cfg.cn[1].cn.push({
32898 cls: 'masonry-brick-text',
32903 if(this.bgimage.length){
32904 cfg.cn[0].cn.push({
32906 cls: 'masonry-brick-image-view',
32911 if(this.videourl.length){
32912 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32913 // youtube support only?
32914 cfg.cn[0].cn.cn.push({
32916 cls: 'masonry-brick-image-view',
32919 allowfullscreen : true
32926 initEvents: function()
32928 switch (this.size) {
32961 this.el.on('touchstart', this.onTouchStart, this);
32962 this.el.on('touchmove', this.onTouchMove, this);
32963 this.el.on('touchend', this.onTouchEnd, this);
32964 this.el.on('contextmenu', this.onContextMenu, this);
32966 this.el.on('mouseenter' ,this.enter, this);
32967 this.el.on('mouseleave', this.leave, this);
32968 this.el.on('click', this.onClick, this);
32971 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32972 this.parent().bricks.push(this);
32977 onClick: function(e, el)
32979 var time = this.endTimer - this.startTimer;
32980 // Roo.log(e.preventDefault());
32983 e.preventDefault();
32988 if(!this.preventDefault){
32992 e.preventDefault();
32994 if (this.activeClass != '') {
32995 this.selectBrick();
32998 this.fireEvent('click', this, e);
33001 enter: function(e, el)
33003 e.preventDefault();
33005 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33009 if(this.bgimage.length && this.html.length){
33010 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33014 leave: function(e, el)
33016 e.preventDefault();
33018 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33022 if(this.bgimage.length && this.html.length){
33023 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33027 onTouchStart: function(e, el)
33029 // e.preventDefault();
33031 this.touchmoved = false;
33033 if(!this.isFitContainer){
33037 if(!this.bgimage.length || !this.html.length){
33041 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33043 this.timer = new Date().getTime();
33047 onTouchMove: function(e, el)
33049 this.touchmoved = true;
33052 onContextMenu : function(e,el)
33054 e.preventDefault();
33055 e.stopPropagation();
33059 onTouchEnd: function(e, el)
33061 // e.preventDefault();
33063 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33070 if(!this.bgimage.length || !this.html.length){
33072 if(this.href.length){
33073 window.location.href = this.href;
33079 if(!this.isFitContainer){
33083 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33085 window.location.href = this.href;
33088 //selection on single brick only
33089 selectBrick : function() {
33091 if (!this.parentId) {
33095 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33096 var index = m.selectedBrick.indexOf(this.id);
33099 m.selectedBrick.splice(index,1);
33100 this.el.removeClass(this.activeClass);
33104 for(var i = 0; i < m.selectedBrick.length; i++) {
33105 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33106 b.el.removeClass(b.activeClass);
33109 m.selectedBrick = [];
33111 m.selectedBrick.push(this.id);
33112 this.el.addClass(this.activeClass);
33116 isSelected : function(){
33117 return this.el.hasClass(this.activeClass);
33122 Roo.apply(Roo.bootstrap.MasonryBrick, {
33125 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33127 * register a Masonry Brick
33128 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33131 register : function(brick)
33133 //this.groups[brick.id] = brick;
33134 this.groups.add(brick.id, brick);
33137 * fetch a masonry brick based on the masonry brick ID
33138 * @param {string} the masonry brick to add
33139 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33142 get: function(brick_id)
33144 // if (typeof(this.groups[brick_id]) == 'undefined') {
33147 // return this.groups[brick_id] ;
33149 if(this.groups.key(brick_id)) {
33150 return this.groups.key(brick_id);
33168 * @class Roo.bootstrap.Brick
33169 * @extends Roo.bootstrap.Component
33170 * Bootstrap Brick class
33173 * Create a new Brick
33174 * @param {Object} config The config object
33177 Roo.bootstrap.Brick = function(config){
33178 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33184 * When a Brick is click
33185 * @param {Roo.bootstrap.Brick} this
33186 * @param {Roo.EventObject} e
33192 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33195 * @cfg {String} title
33199 * @cfg {String} html
33203 * @cfg {String} bgimage
33207 * @cfg {String} cls
33211 * @cfg {String} href
33215 * @cfg {String} video
33219 * @cfg {Boolean} square
33223 getAutoCreate : function()
33225 var cls = 'roo-brick';
33227 if(this.href.length){
33228 cls += ' roo-brick-link';
33231 if(this.bgimage.length){
33232 cls += ' roo-brick-image';
33235 if(!this.html.length && !this.bgimage.length){
33236 cls += ' roo-brick-center-title';
33239 if(!this.html.length && this.bgimage.length){
33240 cls += ' roo-brick-bottom-title';
33244 cls += ' ' + this.cls;
33248 tag: (this.href.length) ? 'a' : 'div',
33253 cls: 'roo-brick-paragraph',
33259 if(this.href.length){
33260 cfg.href = this.href;
33263 var cn = cfg.cn[0].cn;
33265 if(this.title.length){
33268 cls: 'roo-brick-title',
33273 if(this.html.length){
33276 cls: 'roo-brick-text',
33283 if(this.bgimage.length){
33286 cls: 'roo-brick-image-view',
33294 initEvents: function()
33296 if(this.title.length || this.html.length){
33297 this.el.on('mouseenter' ,this.enter, this);
33298 this.el.on('mouseleave', this.leave, this);
33301 Roo.EventManager.onWindowResize(this.resize, this);
33303 if(this.bgimage.length){
33304 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33305 this.imageEl.on('load', this.onImageLoad, this);
33312 onImageLoad : function()
33317 resize : function()
33319 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33321 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33323 if(this.bgimage.length){
33324 var image = this.el.select('.roo-brick-image-view', true).first();
33326 image.setWidth(paragraph.getWidth());
33329 image.setHeight(paragraph.getWidth());
33332 this.el.setHeight(image.getHeight());
33333 paragraph.setHeight(image.getHeight());
33339 enter: function(e, el)
33341 e.preventDefault();
33343 if(this.bgimage.length){
33344 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33345 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33349 leave: function(e, el)
33351 e.preventDefault();
33353 if(this.bgimage.length){
33354 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33355 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33370 * @class Roo.bootstrap.NumberField
33371 * @extends Roo.bootstrap.Input
33372 * Bootstrap NumberField class
33378 * Create a new NumberField
33379 * @param {Object} config The config object
33382 Roo.bootstrap.NumberField = function(config){
33383 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33386 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33389 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33391 allowDecimals : true,
33393 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33395 decimalSeparator : ".",
33397 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33399 decimalPrecision : 2,
33401 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33403 allowNegative : true,
33406 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33410 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33412 minValue : Number.NEGATIVE_INFINITY,
33414 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33416 maxValue : Number.MAX_VALUE,
33418 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33420 minText : "The minimum value for this field is {0}",
33422 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33424 maxText : "The maximum value for this field is {0}",
33426 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33427 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33429 nanText : "{0} is not a valid number",
33431 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33433 thousandsDelimiter : false,
33435 * @cfg {String} valueAlign alignment of value
33437 valueAlign : "left",
33439 getAutoCreate : function()
33441 var hiddenInput = {
33445 cls: 'hidden-number-input'
33449 hiddenInput.name = this.name;
33454 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33456 this.name = hiddenInput.name;
33458 if(cfg.cn.length > 0) {
33459 cfg.cn.push(hiddenInput);
33466 initEvents : function()
33468 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33470 var allowed = "0123456789";
33472 if(this.allowDecimals){
33473 allowed += this.decimalSeparator;
33476 if(this.allowNegative){
33480 if(this.thousandsDelimiter) {
33484 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33486 var keyPress = function(e){
33488 var k = e.getKey();
33490 var c = e.getCharCode();
33493 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33494 allowed.indexOf(String.fromCharCode(c)) === -1
33500 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33504 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33509 this.el.on("keypress", keyPress, this);
33512 validateValue : function(value)
33515 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33519 var num = this.parseValue(value);
33522 this.markInvalid(String.format(this.nanText, value));
33526 if(num < this.minValue){
33527 this.markInvalid(String.format(this.minText, this.minValue));
33531 if(num > this.maxValue){
33532 this.markInvalid(String.format(this.maxText, this.maxValue));
33539 getValue : function()
33541 var v = this.hiddenEl().getValue();
33543 return this.fixPrecision(this.parseValue(v));
33546 parseValue : function(value)
33548 if(this.thousandsDelimiter) {
33550 r = new RegExp(",", "g");
33551 value = value.replace(r, "");
33554 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33555 return isNaN(value) ? '' : value;
33558 fixPrecision : function(value)
33560 if(this.thousandsDelimiter) {
33562 r = new RegExp(",", "g");
33563 value = value.replace(r, "");
33566 var nan = isNaN(value);
33568 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33569 return nan ? '' : value;
33571 return parseFloat(value).toFixed(this.decimalPrecision);
33574 setValue : function(v)
33576 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33582 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33584 this.inputEl().dom.value = (v == '') ? '' :
33585 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33587 if(!this.allowZero && v === '0') {
33588 this.hiddenEl().dom.value = '';
33589 this.inputEl().dom.value = '';
33596 decimalPrecisionFcn : function(v)
33598 return Math.floor(v);
33601 beforeBlur : function()
33603 var v = this.parseValue(this.getRawValue());
33605 if(v || v === 0 || v === ''){
33610 hiddenEl : function()
33612 return this.el.select('input.hidden-number-input',true).first();
33624 * @class Roo.bootstrap.DocumentSlider
33625 * @extends Roo.bootstrap.Component
33626 * Bootstrap DocumentSlider class
33629 * Create a new DocumentViewer
33630 * @param {Object} config The config object
33633 Roo.bootstrap.DocumentSlider = function(config){
33634 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33641 * Fire after initEvent
33642 * @param {Roo.bootstrap.DocumentSlider} this
33647 * Fire after update
33648 * @param {Roo.bootstrap.DocumentSlider} this
33654 * @param {Roo.bootstrap.DocumentSlider} this
33660 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33666 getAutoCreate : function()
33670 cls : 'roo-document-slider',
33674 cls : 'roo-document-slider-header',
33678 cls : 'roo-document-slider-header-title'
33684 cls : 'roo-document-slider-body',
33688 cls : 'roo-document-slider-prev',
33692 cls : 'fa fa-chevron-left'
33698 cls : 'roo-document-slider-thumb',
33702 cls : 'roo-document-slider-image'
33708 cls : 'roo-document-slider-next',
33712 cls : 'fa fa-chevron-right'
33724 initEvents : function()
33726 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33727 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33729 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33730 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33732 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33733 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33735 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33736 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33738 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33739 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33741 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33742 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33744 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33745 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33747 this.thumbEl.on('click', this.onClick, this);
33749 this.prevIndicator.on('click', this.prev, this);
33751 this.nextIndicator.on('click', this.next, this);
33755 initial : function()
33757 if(this.files.length){
33758 this.indicator = 1;
33762 this.fireEvent('initial', this);
33765 update : function()
33767 this.imageEl.attr('src', this.files[this.indicator - 1]);
33769 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33771 this.prevIndicator.show();
33773 if(this.indicator == 1){
33774 this.prevIndicator.hide();
33777 this.nextIndicator.show();
33779 if(this.indicator == this.files.length){
33780 this.nextIndicator.hide();
33783 this.thumbEl.scrollTo('top');
33785 this.fireEvent('update', this);
33788 onClick : function(e)
33790 e.preventDefault();
33792 this.fireEvent('click', this);
33797 e.preventDefault();
33799 this.indicator = Math.max(1, this.indicator - 1);
33806 e.preventDefault();
33808 this.indicator = Math.min(this.files.length, this.indicator + 1);
33822 * @class Roo.bootstrap.RadioSet
33823 * @extends Roo.bootstrap.Input
33824 * Bootstrap RadioSet class
33825 * @cfg {String} indicatorpos (left|right) default left
33826 * @cfg {Boolean} inline (true|false) inline the element (default true)
33827 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33829 * Create a new RadioSet
33830 * @param {Object} config The config object
33833 Roo.bootstrap.RadioSet = function(config){
33835 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33839 Roo.bootstrap.RadioSet.register(this);
33844 * Fires when the element is checked or unchecked.
33845 * @param {Roo.bootstrap.RadioSet} this This radio
33846 * @param {Roo.bootstrap.Radio} item The checked item
33851 * Fires when the element is click.
33852 * @param {Roo.bootstrap.RadioSet} this This radio set
33853 * @param {Roo.bootstrap.Radio} item The checked item
33854 * @param {Roo.EventObject} e The event object
33861 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33869 indicatorpos : 'left',
33871 getAutoCreate : function()
33875 cls : 'roo-radio-set-label',
33879 html : this.fieldLabel
33884 if(this.indicatorpos == 'left'){
33887 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33888 tooltip : 'This field is required'
33893 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33894 tooltip : 'This field is required'
33900 cls : 'roo-radio-set-items'
33903 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33905 if (align === 'left' && this.fieldLabel.length) {
33908 cls : "roo-radio-set-right",
33914 if(this.labelWidth > 12){
33915 label.style = "width: " + this.labelWidth + 'px';
33918 if(this.labelWidth < 13 && this.labelmd == 0){
33919 this.labelmd = this.labelWidth;
33922 if(this.labellg > 0){
33923 label.cls += ' col-lg-' + this.labellg;
33924 items.cls += ' col-lg-' + (12 - this.labellg);
33927 if(this.labelmd > 0){
33928 label.cls += ' col-md-' + this.labelmd;
33929 items.cls += ' col-md-' + (12 - this.labelmd);
33932 if(this.labelsm > 0){
33933 label.cls += ' col-sm-' + this.labelsm;
33934 items.cls += ' col-sm-' + (12 - this.labelsm);
33937 if(this.labelxs > 0){
33938 label.cls += ' col-xs-' + this.labelxs;
33939 items.cls += ' col-xs-' + (12 - this.labelxs);
33945 cls : 'roo-radio-set',
33949 cls : 'roo-radio-set-input',
33952 value : this.value ? this.value : ''
33959 if(this.weight.length){
33960 cfg.cls += ' roo-radio-' + this.weight;
33964 cfg.cls += ' roo-radio-set-inline';
33968 ['xs','sm','md','lg'].map(function(size){
33969 if (settings[size]) {
33970 cfg.cls += ' col-' + size + '-' + settings[size];
33978 initEvents : function()
33980 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33981 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33983 if(!this.fieldLabel.length){
33984 this.labelEl.hide();
33987 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33988 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33990 this.indicator = this.indicatorEl();
33992 if(this.indicator){
33993 this.indicator.addClass('invisible');
33996 this.originalValue = this.getValue();
34000 inputEl: function ()
34002 return this.el.select('.roo-radio-set-input', true).first();
34005 getChildContainer : function()
34007 return this.itemsEl;
34010 register : function(item)
34012 this.radioes.push(item);
34016 validate : function()
34018 if(this.getVisibilityEl().hasClass('hidden')){
34024 Roo.each(this.radioes, function(i){
34033 if(this.allowBlank) {
34037 if(this.disabled || valid){
34042 this.markInvalid();
34047 markValid : function()
34049 if(this.labelEl.isVisible(true)){
34050 this.indicatorEl().removeClass('visible');
34051 this.indicatorEl().addClass('invisible');
34054 this.el.removeClass([this.invalidClass, this.validClass]);
34055 this.el.addClass(this.validClass);
34057 this.fireEvent('valid', this);
34060 markInvalid : function(msg)
34062 if(this.allowBlank || this.disabled){
34066 if(this.labelEl.isVisible(true)){
34067 this.indicatorEl().removeClass('invisible');
34068 this.indicatorEl().addClass('visible');
34071 this.el.removeClass([this.invalidClass, this.validClass]);
34072 this.el.addClass(this.invalidClass);
34074 this.fireEvent('invalid', this, msg);
34078 setValue : function(v, suppressEvent)
34080 if(this.value === v){
34087 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34090 Roo.each(this.radioes, function(i){
34092 i.el.removeClass('checked');
34095 Roo.each(this.radioes, function(i){
34097 if(i.value === v || i.value.toString() === v.toString()){
34099 i.el.addClass('checked');
34101 if(suppressEvent !== true){
34102 this.fireEvent('check', this, i);
34113 clearInvalid : function(){
34115 if(!this.el || this.preventMark){
34119 this.el.removeClass([this.invalidClass]);
34121 this.fireEvent('valid', this);
34126 Roo.apply(Roo.bootstrap.RadioSet, {
34130 register : function(set)
34132 this.groups[set.name] = set;
34135 get: function(name)
34137 if (typeof(this.groups[name]) == 'undefined') {
34141 return this.groups[name] ;
34147 * Ext JS Library 1.1.1
34148 * Copyright(c) 2006-2007, Ext JS, LLC.
34150 * Originally Released Under LGPL - original licence link has changed is not relivant.
34153 * <script type="text/javascript">
34158 * @class Roo.bootstrap.SplitBar
34159 * @extends Roo.util.Observable
34160 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34164 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34165 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34166 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34167 split.minSize = 100;
34168 split.maxSize = 600;
34169 split.animate = true;
34170 split.on('moved', splitterMoved);
34173 * Create a new SplitBar
34174 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34175 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34176 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34177 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34178 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34179 position of the SplitBar).
34181 Roo.bootstrap.SplitBar = function(cfg){
34186 // dragElement : elm
34187 // resizingElement: el,
34189 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34190 // placement : Roo.bootstrap.SplitBar.LEFT ,
34191 // existingProxy ???
34194 this.el = Roo.get(cfg.dragElement, true);
34195 this.el.dom.unselectable = "on";
34197 this.resizingEl = Roo.get(cfg.resizingElement, true);
34201 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34202 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34205 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34208 * The minimum size of the resizing element. (Defaults to 0)
34214 * The maximum size of the resizing element. (Defaults to 2000)
34217 this.maxSize = 2000;
34220 * Whether to animate the transition to the new size
34223 this.animate = false;
34226 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34229 this.useShim = false;
34234 if(!cfg.existingProxy){
34236 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34238 this.proxy = Roo.get(cfg.existingProxy).dom;
34241 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34244 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34247 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34250 this.dragSpecs = {};
34253 * @private The adapter to use to positon and resize elements
34255 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34256 this.adapter.init(this);
34258 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34260 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34261 this.el.addClass("roo-splitbar-h");
34264 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34265 this.el.addClass("roo-splitbar-v");
34271 * Fires when the splitter is moved (alias for {@link #event-moved})
34272 * @param {Roo.bootstrap.SplitBar} this
34273 * @param {Number} newSize the new width or height
34278 * Fires when the splitter is moved
34279 * @param {Roo.bootstrap.SplitBar} this
34280 * @param {Number} newSize the new width or height
34284 * @event beforeresize
34285 * Fires before the splitter is dragged
34286 * @param {Roo.bootstrap.SplitBar} this
34288 "beforeresize" : true,
34290 "beforeapply" : true
34293 Roo.util.Observable.call(this);
34296 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34297 onStartProxyDrag : function(x, y){
34298 this.fireEvent("beforeresize", this);
34300 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34302 o.enableDisplayMode("block");
34303 // all splitbars share the same overlay
34304 Roo.bootstrap.SplitBar.prototype.overlay = o;
34306 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34307 this.overlay.show();
34308 Roo.get(this.proxy).setDisplayed("block");
34309 var size = this.adapter.getElementSize(this);
34310 this.activeMinSize = this.getMinimumSize();;
34311 this.activeMaxSize = this.getMaximumSize();;
34312 var c1 = size - this.activeMinSize;
34313 var c2 = Math.max(this.activeMaxSize - size, 0);
34314 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34315 this.dd.resetConstraints();
34316 this.dd.setXConstraint(
34317 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34318 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34320 this.dd.setYConstraint(0, 0);
34322 this.dd.resetConstraints();
34323 this.dd.setXConstraint(0, 0);
34324 this.dd.setYConstraint(
34325 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34326 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34329 this.dragSpecs.startSize = size;
34330 this.dragSpecs.startPoint = [x, y];
34331 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34335 * @private Called after the drag operation by the DDProxy
34337 onEndProxyDrag : function(e){
34338 Roo.get(this.proxy).setDisplayed(false);
34339 var endPoint = Roo.lib.Event.getXY(e);
34341 this.overlay.hide();
34344 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34345 newSize = this.dragSpecs.startSize +
34346 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34347 endPoint[0] - this.dragSpecs.startPoint[0] :
34348 this.dragSpecs.startPoint[0] - endPoint[0]
34351 newSize = this.dragSpecs.startSize +
34352 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34353 endPoint[1] - this.dragSpecs.startPoint[1] :
34354 this.dragSpecs.startPoint[1] - endPoint[1]
34357 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34358 if(newSize != this.dragSpecs.startSize){
34359 if(this.fireEvent('beforeapply', this, newSize) !== false){
34360 this.adapter.setElementSize(this, newSize);
34361 this.fireEvent("moved", this, newSize);
34362 this.fireEvent("resize", this, newSize);
34368 * Get the adapter this SplitBar uses
34369 * @return The adapter object
34371 getAdapter : function(){
34372 return this.adapter;
34376 * Set the adapter this SplitBar uses
34377 * @param {Object} adapter A SplitBar adapter object
34379 setAdapter : function(adapter){
34380 this.adapter = adapter;
34381 this.adapter.init(this);
34385 * Gets the minimum size for the resizing element
34386 * @return {Number} The minimum size
34388 getMinimumSize : function(){
34389 return this.minSize;
34393 * Sets the minimum size for the resizing element
34394 * @param {Number} minSize The minimum size
34396 setMinimumSize : function(minSize){
34397 this.minSize = minSize;
34401 * Gets the maximum size for the resizing element
34402 * @return {Number} The maximum size
34404 getMaximumSize : function(){
34405 return this.maxSize;
34409 * Sets the maximum size for the resizing element
34410 * @param {Number} maxSize The maximum size
34412 setMaximumSize : function(maxSize){
34413 this.maxSize = maxSize;
34417 * Sets the initialize size for the resizing element
34418 * @param {Number} size The initial size
34420 setCurrentSize : function(size){
34421 var oldAnimate = this.animate;
34422 this.animate = false;
34423 this.adapter.setElementSize(this, size);
34424 this.animate = oldAnimate;
34428 * Destroy this splitbar.
34429 * @param {Boolean} removeEl True to remove the element
34431 destroy : function(removeEl){
34433 this.shim.remove();
34436 this.proxy.parentNode.removeChild(this.proxy);
34444 * @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.
34446 Roo.bootstrap.SplitBar.createProxy = function(dir){
34447 var proxy = new Roo.Element(document.createElement("div"));
34448 proxy.unselectable();
34449 var cls = 'roo-splitbar-proxy';
34450 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34451 document.body.appendChild(proxy.dom);
34456 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34457 * Default Adapter. It assumes the splitter and resizing element are not positioned
34458 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34460 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34463 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34464 // do nothing for now
34465 init : function(s){
34469 * Called before drag operations to get the current size of the resizing element.
34470 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34472 getElementSize : function(s){
34473 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34474 return s.resizingEl.getWidth();
34476 return s.resizingEl.getHeight();
34481 * Called after drag operations to set the size of the resizing element.
34482 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34483 * @param {Number} newSize The new size to set
34484 * @param {Function} onComplete A function to be invoked when resizing is complete
34486 setElementSize : function(s, newSize, onComplete){
34487 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34489 s.resizingEl.setWidth(newSize);
34491 onComplete(s, newSize);
34494 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34499 s.resizingEl.setHeight(newSize);
34501 onComplete(s, newSize);
34504 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34511 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34512 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34513 * Adapter that moves the splitter element to align with the resized sizing element.
34514 * Used with an absolute positioned SplitBar.
34515 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34516 * document.body, make sure you assign an id to the body element.
34518 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34519 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34520 this.container = Roo.get(container);
34523 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34524 init : function(s){
34525 this.basic.init(s);
34528 getElementSize : function(s){
34529 return this.basic.getElementSize(s);
34532 setElementSize : function(s, newSize, onComplete){
34533 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34536 moveSplitter : function(s){
34537 var yes = Roo.bootstrap.SplitBar;
34538 switch(s.placement){
34540 s.el.setX(s.resizingEl.getRight());
34543 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34546 s.el.setY(s.resizingEl.getBottom());
34549 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34556 * Orientation constant - Create a vertical SplitBar
34560 Roo.bootstrap.SplitBar.VERTICAL = 1;
34563 * Orientation constant - Create a horizontal SplitBar
34567 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34570 * Placement constant - The resizing element is to the left of the splitter element
34574 Roo.bootstrap.SplitBar.LEFT = 1;
34577 * Placement constant - The resizing element is to the right of the splitter element
34581 Roo.bootstrap.SplitBar.RIGHT = 2;
34584 * Placement constant - The resizing element is positioned above the splitter element
34588 Roo.bootstrap.SplitBar.TOP = 3;
34591 * Placement constant - The resizing element is positioned under splitter element
34595 Roo.bootstrap.SplitBar.BOTTOM = 4;
34596 Roo.namespace("Roo.bootstrap.layout");/*
34598 * Ext JS Library 1.1.1
34599 * Copyright(c) 2006-2007, Ext JS, LLC.
34601 * Originally Released Under LGPL - original licence link has changed is not relivant.
34604 * <script type="text/javascript">
34608 * @class Roo.bootstrap.layout.Manager
34609 * @extends Roo.bootstrap.Component
34610 * Base class for layout managers.
34612 Roo.bootstrap.layout.Manager = function(config)
34614 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34620 /** false to disable window resize monitoring @type Boolean */
34621 this.monitorWindowResize = true;
34626 * Fires when a layout is performed.
34627 * @param {Roo.LayoutManager} this
34631 * @event regionresized
34632 * Fires when the user resizes a region.
34633 * @param {Roo.LayoutRegion} region The resized region
34634 * @param {Number} newSize The new size (width for east/west, height for north/south)
34636 "regionresized" : true,
34638 * @event regioncollapsed
34639 * Fires when a region is collapsed.
34640 * @param {Roo.LayoutRegion} region The collapsed region
34642 "regioncollapsed" : true,
34644 * @event regionexpanded
34645 * Fires when a region is expanded.
34646 * @param {Roo.LayoutRegion} region The expanded region
34648 "regionexpanded" : true
34650 this.updating = false;
34653 this.el = Roo.get(config.el);
34659 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34664 monitorWindowResize : true,
34670 onRender : function(ct, position)
34673 this.el = Roo.get(ct);
34676 //this.fireEvent('render',this);
34680 initEvents: function()
34684 // ie scrollbar fix
34685 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34686 document.body.scroll = "no";
34687 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34688 this.el.position('relative');
34690 this.id = this.el.id;
34691 this.el.addClass("roo-layout-container");
34692 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34693 if(this.el.dom != document.body ) {
34694 this.el.on('resize', this.layout,this);
34695 this.el.on('show', this.layout,this);
34701 * Returns true if this layout is currently being updated
34702 * @return {Boolean}
34704 isUpdating : function(){
34705 return this.updating;
34709 * Suspend the LayoutManager from doing auto-layouts while
34710 * making multiple add or remove calls
34712 beginUpdate : function(){
34713 this.updating = true;
34717 * Restore auto-layouts and optionally disable the manager from performing a layout
34718 * @param {Boolean} noLayout true to disable a layout update
34720 endUpdate : function(noLayout){
34721 this.updating = false;
34727 layout: function(){
34731 onRegionResized : function(region, newSize){
34732 this.fireEvent("regionresized", region, newSize);
34736 onRegionCollapsed : function(region){
34737 this.fireEvent("regioncollapsed", region);
34740 onRegionExpanded : function(region){
34741 this.fireEvent("regionexpanded", region);
34745 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34746 * performs box-model adjustments.
34747 * @return {Object} The size as an object {width: (the width), height: (the height)}
34749 getViewSize : function()
34752 if(this.el.dom != document.body){
34753 size = this.el.getSize();
34755 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34757 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34758 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34763 * Returns the Element this layout is bound to.
34764 * @return {Roo.Element}
34766 getEl : function(){
34771 * Returns the specified region.
34772 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34773 * @return {Roo.LayoutRegion}
34775 getRegion : function(target){
34776 return this.regions[target.toLowerCase()];
34779 onWindowResize : function(){
34780 if(this.monitorWindowResize){
34787 * Ext JS Library 1.1.1
34788 * Copyright(c) 2006-2007, Ext JS, LLC.
34790 * Originally Released Under LGPL - original licence link has changed is not relivant.
34793 * <script type="text/javascript">
34796 * @class Roo.bootstrap.layout.Border
34797 * @extends Roo.bootstrap.layout.Manager
34798 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34799 * please see: examples/bootstrap/nested.html<br><br>
34801 <b>The container the layout is rendered into can be either the body element or any other element.
34802 If it is not the body element, the container needs to either be an absolute positioned element,
34803 or you will need to add "position:relative" to the css of the container. You will also need to specify
34804 the container size if it is not the body element.</b>
34807 * Create a new Border
34808 * @param {Object} config Configuration options
34810 Roo.bootstrap.layout.Border = function(config){
34811 config = config || {};
34812 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34816 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34817 if(config[region]){
34818 config[region].region = region;
34819 this.addRegion(config[region]);
34825 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34827 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34829 * Creates and adds a new region if it doesn't already exist.
34830 * @param {String} target The target region key (north, south, east, west or center).
34831 * @param {Object} config The regions config object
34832 * @return {BorderLayoutRegion} The new region
34834 addRegion : function(config)
34836 if(!this.regions[config.region]){
34837 var r = this.factory(config);
34838 this.bindRegion(r);
34840 return this.regions[config.region];
34844 bindRegion : function(r){
34845 this.regions[r.config.region] = r;
34847 r.on("visibilitychange", this.layout, this);
34848 r.on("paneladded", this.layout, this);
34849 r.on("panelremoved", this.layout, this);
34850 r.on("invalidated", this.layout, this);
34851 r.on("resized", this.onRegionResized, this);
34852 r.on("collapsed", this.onRegionCollapsed, this);
34853 r.on("expanded", this.onRegionExpanded, this);
34857 * Performs a layout update.
34859 layout : function()
34861 if(this.updating) {
34865 // render all the rebions if they have not been done alreayd?
34866 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34867 if(this.regions[region] && !this.regions[region].bodyEl){
34868 this.regions[region].onRender(this.el)
34872 var size = this.getViewSize();
34873 var w = size.width;
34874 var h = size.height;
34879 //var x = 0, y = 0;
34881 var rs = this.regions;
34882 var north = rs["north"];
34883 var south = rs["south"];
34884 var west = rs["west"];
34885 var east = rs["east"];
34886 var center = rs["center"];
34887 //if(this.hideOnLayout){ // not supported anymore
34888 //c.el.setStyle("display", "none");
34890 if(north && north.isVisible()){
34891 var b = north.getBox();
34892 var m = north.getMargins();
34893 b.width = w - (m.left+m.right);
34896 centerY = b.height + b.y + m.bottom;
34897 centerH -= centerY;
34898 north.updateBox(this.safeBox(b));
34900 if(south && south.isVisible()){
34901 var b = south.getBox();
34902 var m = south.getMargins();
34903 b.width = w - (m.left+m.right);
34905 var totalHeight = (b.height + m.top + m.bottom);
34906 b.y = h - totalHeight + m.top;
34907 centerH -= totalHeight;
34908 south.updateBox(this.safeBox(b));
34910 if(west && west.isVisible()){
34911 var b = west.getBox();
34912 var m = west.getMargins();
34913 b.height = centerH - (m.top+m.bottom);
34915 b.y = centerY + m.top;
34916 var totalWidth = (b.width + m.left + m.right);
34917 centerX += totalWidth;
34918 centerW -= totalWidth;
34919 west.updateBox(this.safeBox(b));
34921 if(east && east.isVisible()){
34922 var b = east.getBox();
34923 var m = east.getMargins();
34924 b.height = centerH - (m.top+m.bottom);
34925 var totalWidth = (b.width + m.left + m.right);
34926 b.x = w - totalWidth + m.left;
34927 b.y = centerY + m.top;
34928 centerW -= totalWidth;
34929 east.updateBox(this.safeBox(b));
34932 var m = center.getMargins();
34934 x: centerX + m.left,
34935 y: centerY + m.top,
34936 width: centerW - (m.left+m.right),
34937 height: centerH - (m.top+m.bottom)
34939 //if(this.hideOnLayout){
34940 //center.el.setStyle("display", "block");
34942 center.updateBox(this.safeBox(centerBox));
34945 this.fireEvent("layout", this);
34949 safeBox : function(box){
34950 box.width = Math.max(0, box.width);
34951 box.height = Math.max(0, box.height);
34956 * Adds a ContentPanel (or subclass) to this layout.
34957 * @param {String} target The target region key (north, south, east, west or center).
34958 * @param {Roo.ContentPanel} panel The panel to add
34959 * @return {Roo.ContentPanel} The added panel
34961 add : function(target, panel){
34963 target = target.toLowerCase();
34964 return this.regions[target].add(panel);
34968 * Remove a ContentPanel (or subclass) to this layout.
34969 * @param {String} target The target region key (north, south, east, west or center).
34970 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34971 * @return {Roo.ContentPanel} The removed panel
34973 remove : function(target, panel){
34974 target = target.toLowerCase();
34975 return this.regions[target].remove(panel);
34979 * Searches all regions for a panel with the specified id
34980 * @param {String} panelId
34981 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34983 findPanel : function(panelId){
34984 var rs = this.regions;
34985 for(var target in rs){
34986 if(typeof rs[target] != "function"){
34987 var p = rs[target].getPanel(panelId);
34997 * Searches all regions for a panel with the specified id and activates (shows) it.
34998 * @param {String/ContentPanel} panelId The panels id or the panel itself
34999 * @return {Roo.ContentPanel} The shown panel or null
35001 showPanel : function(panelId) {
35002 var rs = this.regions;
35003 for(var target in rs){
35004 var r = rs[target];
35005 if(typeof r != "function"){
35006 if(r.hasPanel(panelId)){
35007 return r.showPanel(panelId);
35015 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35016 * @param {Roo.state.Provider} provider (optional) An alternate state provider
35019 restoreState : function(provider){
35021 provider = Roo.state.Manager;
35023 var sm = new Roo.LayoutStateManager();
35024 sm.init(this, provider);
35030 * Adds a xtype elements to the layout.
35034 xtype : 'ContentPanel',
35041 xtype : 'NestedLayoutPanel',
35047 items : [ ... list of content panels or nested layout panels.. ]
35051 * @param {Object} cfg Xtype definition of item to add.
35053 addxtype : function(cfg)
35055 // basically accepts a pannel...
35056 // can accept a layout region..!?!?
35057 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35060 // theory? children can only be panels??
35062 //if (!cfg.xtype.match(/Panel$/)) {
35067 if (typeof(cfg.region) == 'undefined') {
35068 Roo.log("Failed to add Panel, region was not set");
35072 var region = cfg.region;
35078 xitems = cfg.items;
35085 case 'Content': // ContentPanel (el, cfg)
35086 case 'Scroll': // ContentPanel (el, cfg)
35088 cfg.autoCreate = true;
35089 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35091 // var el = this.el.createChild();
35092 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35095 this.add(region, ret);
35099 case 'TreePanel': // our new panel!
35100 cfg.el = this.el.createChild();
35101 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35102 this.add(region, ret);
35107 // create a new Layout (which is a Border Layout...
35109 var clayout = cfg.layout;
35110 clayout.el = this.el.createChild();
35111 clayout.items = clayout.items || [];
35115 // replace this exitems with the clayout ones..
35116 xitems = clayout.items;
35118 // force background off if it's in center...
35119 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35120 cfg.background = false;
35122 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35125 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35126 //console.log('adding nested layout panel ' + cfg.toSource());
35127 this.add(region, ret);
35128 nb = {}; /// find first...
35133 // needs grid and region
35135 //var el = this.getRegion(region).el.createChild();
35137 *var el = this.el.createChild();
35138 // create the grid first...
35139 cfg.grid.container = el;
35140 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35143 if (region == 'center' && this.active ) {
35144 cfg.background = false;
35147 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35149 this.add(region, ret);
35151 if (cfg.background) {
35152 // render grid on panel activation (if panel background)
35153 ret.on('activate', function(gp) {
35154 if (!gp.grid.rendered) {
35155 // gp.grid.render(el);
35159 // cfg.grid.render(el);
35165 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35166 // it was the old xcomponent building that caused this before.
35167 // espeically if border is the top element in the tree.
35177 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35179 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35180 this.add(region, ret);
35184 throw "Can not add '" + cfg.xtype + "' to Border";
35190 this.beginUpdate();
35194 Roo.each(xitems, function(i) {
35195 region = nb && i.region ? i.region : false;
35197 var add = ret.addxtype(i);
35200 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35201 if (!i.background) {
35202 abn[region] = nb[region] ;
35209 // make the last non-background panel active..
35210 //if (nb) { Roo.log(abn); }
35213 for(var r in abn) {
35214 region = this.getRegion(r);
35216 // tried using nb[r], but it does not work..
35218 region.showPanel(abn[r]);
35229 factory : function(cfg)
35232 var validRegions = Roo.bootstrap.layout.Border.regions;
35234 var target = cfg.region;
35237 var r = Roo.bootstrap.layout;
35241 return new r.North(cfg);
35243 return new r.South(cfg);
35245 return new r.East(cfg);
35247 return new r.West(cfg);
35249 return new r.Center(cfg);
35251 throw 'Layout region "'+target+'" not supported.';
35258 * Ext JS Library 1.1.1
35259 * Copyright(c) 2006-2007, Ext JS, LLC.
35261 * Originally Released Under LGPL - original licence link has changed is not relivant.
35264 * <script type="text/javascript">
35268 * @class Roo.bootstrap.layout.Basic
35269 * @extends Roo.util.Observable
35270 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35271 * and does not have a titlebar, tabs or any other features. All it does is size and position
35272 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35273 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35274 * @cfg {string} region the region that it inhabits..
35275 * @cfg {bool} skipConfig skip config?
35279 Roo.bootstrap.layout.Basic = function(config){
35281 this.mgr = config.mgr;
35283 this.position = config.region;
35285 var skipConfig = config.skipConfig;
35289 * @scope Roo.BasicLayoutRegion
35293 * @event beforeremove
35294 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35295 * @param {Roo.LayoutRegion} this
35296 * @param {Roo.ContentPanel} panel The panel
35297 * @param {Object} e The cancel event object
35299 "beforeremove" : true,
35301 * @event invalidated
35302 * Fires when the layout for this region is changed.
35303 * @param {Roo.LayoutRegion} this
35305 "invalidated" : true,
35307 * @event visibilitychange
35308 * Fires when this region is shown or hidden
35309 * @param {Roo.LayoutRegion} this
35310 * @param {Boolean} visibility true or false
35312 "visibilitychange" : true,
35314 * @event paneladded
35315 * Fires when a panel is added.
35316 * @param {Roo.LayoutRegion} this
35317 * @param {Roo.ContentPanel} panel The panel
35319 "paneladded" : true,
35321 * @event panelremoved
35322 * Fires when a panel is removed.
35323 * @param {Roo.LayoutRegion} this
35324 * @param {Roo.ContentPanel} panel The panel
35326 "panelremoved" : true,
35328 * @event beforecollapse
35329 * Fires when this region before collapse.
35330 * @param {Roo.LayoutRegion} this
35332 "beforecollapse" : true,
35335 * Fires when this region is collapsed.
35336 * @param {Roo.LayoutRegion} this
35338 "collapsed" : true,
35341 * Fires when this region is expanded.
35342 * @param {Roo.LayoutRegion} this
35347 * Fires when this region is slid into view.
35348 * @param {Roo.LayoutRegion} this
35350 "slideshow" : true,
35353 * Fires when this region slides out of view.
35354 * @param {Roo.LayoutRegion} this
35356 "slidehide" : true,
35358 * @event panelactivated
35359 * Fires when a panel is activated.
35360 * @param {Roo.LayoutRegion} this
35361 * @param {Roo.ContentPanel} panel The activated panel
35363 "panelactivated" : true,
35366 * Fires when the user resizes this region.
35367 * @param {Roo.LayoutRegion} this
35368 * @param {Number} newSize The new size (width for east/west, height for north/south)
35372 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35373 this.panels = new Roo.util.MixedCollection();
35374 this.panels.getKey = this.getPanelId.createDelegate(this);
35376 this.activePanel = null;
35377 // ensure listeners are added...
35379 if (config.listeners || config.events) {
35380 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35381 listeners : config.listeners || {},
35382 events : config.events || {}
35386 if(skipConfig !== true){
35387 this.applyConfig(config);
35391 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35393 getPanelId : function(p){
35397 applyConfig : function(config){
35398 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35399 this.config = config;
35404 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35405 * the width, for horizontal (north, south) the height.
35406 * @param {Number} newSize The new width or height
35408 resizeTo : function(newSize){
35409 var el = this.el ? this.el :
35410 (this.activePanel ? this.activePanel.getEl() : null);
35412 switch(this.position){
35415 el.setWidth(newSize);
35416 this.fireEvent("resized", this, newSize);
35420 el.setHeight(newSize);
35421 this.fireEvent("resized", this, newSize);
35427 getBox : function(){
35428 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35431 getMargins : function(){
35432 return this.margins;
35435 updateBox : function(box){
35437 var el = this.activePanel.getEl();
35438 el.dom.style.left = box.x + "px";
35439 el.dom.style.top = box.y + "px";
35440 this.activePanel.setSize(box.width, box.height);
35444 * Returns the container element for this region.
35445 * @return {Roo.Element}
35447 getEl : function(){
35448 return this.activePanel;
35452 * Returns true if this region is currently visible.
35453 * @return {Boolean}
35455 isVisible : function(){
35456 return this.activePanel ? true : false;
35459 setActivePanel : function(panel){
35460 panel = this.getPanel(panel);
35461 if(this.activePanel && this.activePanel != panel){
35462 this.activePanel.setActiveState(false);
35463 this.activePanel.getEl().setLeftTop(-10000,-10000);
35465 this.activePanel = panel;
35466 panel.setActiveState(true);
35468 panel.setSize(this.box.width, this.box.height);
35470 this.fireEvent("panelactivated", this, panel);
35471 this.fireEvent("invalidated");
35475 * Show the specified panel.
35476 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35477 * @return {Roo.ContentPanel} The shown panel or null
35479 showPanel : function(panel){
35480 panel = this.getPanel(panel);
35482 this.setActivePanel(panel);
35488 * Get the active panel for this region.
35489 * @return {Roo.ContentPanel} The active panel or null
35491 getActivePanel : function(){
35492 return this.activePanel;
35496 * Add the passed ContentPanel(s)
35497 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35498 * @return {Roo.ContentPanel} The panel added (if only one was added)
35500 add : function(panel){
35501 if(arguments.length > 1){
35502 for(var i = 0, len = arguments.length; i < len; i++) {
35503 this.add(arguments[i]);
35507 if(this.hasPanel(panel)){
35508 this.showPanel(panel);
35511 var el = panel.getEl();
35512 if(el.dom.parentNode != this.mgr.el.dom){
35513 this.mgr.el.dom.appendChild(el.dom);
35515 if(panel.setRegion){
35516 panel.setRegion(this);
35518 this.panels.add(panel);
35519 el.setStyle("position", "absolute");
35520 if(!panel.background){
35521 this.setActivePanel(panel);
35522 if(this.config.initialSize && this.panels.getCount()==1){
35523 this.resizeTo(this.config.initialSize);
35526 this.fireEvent("paneladded", this, panel);
35531 * Returns true if the panel is in this region.
35532 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35533 * @return {Boolean}
35535 hasPanel : function(panel){
35536 if(typeof panel == "object"){ // must be panel obj
35537 panel = panel.getId();
35539 return this.getPanel(panel) ? true : false;
35543 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35544 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35545 * @param {Boolean} preservePanel Overrides the config preservePanel option
35546 * @return {Roo.ContentPanel} The panel that was removed
35548 remove : function(panel, preservePanel){
35549 panel = this.getPanel(panel);
35554 this.fireEvent("beforeremove", this, panel, e);
35555 if(e.cancel === true){
35558 var panelId = panel.getId();
35559 this.panels.removeKey(panelId);
35564 * Returns the panel specified or null if it's not in this region.
35565 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35566 * @return {Roo.ContentPanel}
35568 getPanel : function(id){
35569 if(typeof id == "object"){ // must be panel obj
35572 return this.panels.get(id);
35576 * Returns this regions position (north/south/east/west/center).
35579 getPosition: function(){
35580 return this.position;
35584 * Ext JS Library 1.1.1
35585 * Copyright(c) 2006-2007, Ext JS, LLC.
35587 * Originally Released Under LGPL - original licence link has changed is not relivant.
35590 * <script type="text/javascript">
35594 * @class Roo.bootstrap.layout.Region
35595 * @extends Roo.bootstrap.layout.Basic
35596 * This class represents a region in a layout manager.
35598 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35599 * @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})
35600 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35601 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35602 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35603 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35604 * @cfg {String} title The title for the region (overrides panel titles)
35605 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35606 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35607 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35608 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35609 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35610 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35611 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35612 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35613 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35614 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35616 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35617 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35618 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35619 * @cfg {Number} width For East/West panels
35620 * @cfg {Number} height For North/South panels
35621 * @cfg {Boolean} split To show the splitter
35622 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35624 * @cfg {string} cls Extra CSS classes to add to region
35626 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35627 * @cfg {string} region the region that it inhabits..
35630 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35631 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35633 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35634 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35635 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35637 Roo.bootstrap.layout.Region = function(config)
35639 this.applyConfig(config);
35641 var mgr = config.mgr;
35642 var pos = config.region;
35643 config.skipConfig = true;
35644 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35647 this.onRender(mgr.el);
35650 this.visible = true;
35651 this.collapsed = false;
35652 this.unrendered_panels = [];
35655 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35657 position: '', // set by wrapper (eg. north/south etc..)
35658 unrendered_panels : null, // unrendered panels.
35659 createBody : function(){
35660 /** This region's body element
35661 * @type Roo.Element */
35662 this.bodyEl = this.el.createChild({
35664 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35668 onRender: function(ctr, pos)
35670 var dh = Roo.DomHelper;
35671 /** This region's container element
35672 * @type Roo.Element */
35673 this.el = dh.append(ctr.dom, {
35675 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35677 /** This region's title element
35678 * @type Roo.Element */
35680 this.titleEl = dh.append(this.el.dom,
35683 unselectable: "on",
35684 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35686 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35687 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35690 this.titleEl.enableDisplayMode();
35691 /** This region's title text element
35692 * @type HTMLElement */
35693 this.titleTextEl = this.titleEl.dom.firstChild;
35694 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35696 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35697 this.closeBtn.enableDisplayMode();
35698 this.closeBtn.on("click", this.closeClicked, this);
35699 this.closeBtn.hide();
35701 this.createBody(this.config);
35702 if(this.config.hideWhenEmpty){
35704 this.on("paneladded", this.validateVisibility, this);
35705 this.on("panelremoved", this.validateVisibility, this);
35707 if(this.autoScroll){
35708 this.bodyEl.setStyle("overflow", "auto");
35710 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35712 //if(c.titlebar !== false){
35713 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35714 this.titleEl.hide();
35716 this.titleEl.show();
35717 if(this.config.title){
35718 this.titleTextEl.innerHTML = this.config.title;
35722 if(this.config.collapsed){
35723 this.collapse(true);
35725 if(this.config.hidden){
35729 if (this.unrendered_panels && this.unrendered_panels.length) {
35730 for (var i =0;i< this.unrendered_panels.length; i++) {
35731 this.add(this.unrendered_panels[i]);
35733 this.unrendered_panels = null;
35739 applyConfig : function(c)
35742 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35743 var dh = Roo.DomHelper;
35744 if(c.titlebar !== false){
35745 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35746 this.collapseBtn.on("click", this.collapse, this);
35747 this.collapseBtn.enableDisplayMode();
35749 if(c.showPin === true || this.showPin){
35750 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35751 this.stickBtn.enableDisplayMode();
35752 this.stickBtn.on("click", this.expand, this);
35753 this.stickBtn.hide();
35758 /** This region's collapsed element
35759 * @type Roo.Element */
35762 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35763 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35766 if(c.floatable !== false){
35767 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35768 this.collapsedEl.on("click", this.collapseClick, this);
35771 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35772 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35773 id: "message", unselectable: "on", style:{"float":"left"}});
35774 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35776 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35777 this.expandBtn.on("click", this.expand, this);
35781 if(this.collapseBtn){
35782 this.collapseBtn.setVisible(c.collapsible == true);
35785 this.cmargins = c.cmargins || this.cmargins ||
35786 (this.position == "west" || this.position == "east" ?
35787 {top: 0, left: 2, right:2, bottom: 0} :
35788 {top: 2, left: 0, right:0, bottom: 2});
35790 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35793 this.bottomTabs = c.tabPosition != "top";
35795 this.autoScroll = c.autoScroll || false;
35800 this.duration = c.duration || .30;
35801 this.slideDuration = c.slideDuration || .45;
35806 * Returns true if this region is currently visible.
35807 * @return {Boolean}
35809 isVisible : function(){
35810 return this.visible;
35814 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35815 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35817 //setCollapsedTitle : function(title){
35818 // title = title || " ";
35819 // if(this.collapsedTitleTextEl){
35820 // this.collapsedTitleTextEl.innerHTML = title;
35824 getBox : function(){
35826 // if(!this.collapsed){
35827 b = this.el.getBox(false, true);
35829 // b = this.collapsedEl.getBox(false, true);
35834 getMargins : function(){
35835 return this.margins;
35836 //return this.collapsed ? this.cmargins : this.margins;
35839 highlight : function(){
35840 this.el.addClass("x-layout-panel-dragover");
35843 unhighlight : function(){
35844 this.el.removeClass("x-layout-panel-dragover");
35847 updateBox : function(box)
35849 if (!this.bodyEl) {
35850 return; // not rendered yet..
35854 if(!this.collapsed){
35855 this.el.dom.style.left = box.x + "px";
35856 this.el.dom.style.top = box.y + "px";
35857 this.updateBody(box.width, box.height);
35859 this.collapsedEl.dom.style.left = box.x + "px";
35860 this.collapsedEl.dom.style.top = box.y + "px";
35861 this.collapsedEl.setSize(box.width, box.height);
35864 this.tabs.autoSizeTabs();
35868 updateBody : function(w, h)
35871 this.el.setWidth(w);
35872 w -= this.el.getBorderWidth("rl");
35873 if(this.config.adjustments){
35874 w += this.config.adjustments[0];
35877 if(h !== null && h > 0){
35878 this.el.setHeight(h);
35879 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35880 h -= this.el.getBorderWidth("tb");
35881 if(this.config.adjustments){
35882 h += this.config.adjustments[1];
35884 this.bodyEl.setHeight(h);
35886 h = this.tabs.syncHeight(h);
35889 if(this.panelSize){
35890 w = w !== null ? w : this.panelSize.width;
35891 h = h !== null ? h : this.panelSize.height;
35893 if(this.activePanel){
35894 var el = this.activePanel.getEl();
35895 w = w !== null ? w : el.getWidth();
35896 h = h !== null ? h : el.getHeight();
35897 this.panelSize = {width: w, height: h};
35898 this.activePanel.setSize(w, h);
35900 if(Roo.isIE && this.tabs){
35901 this.tabs.el.repaint();
35906 * Returns the container element for this region.
35907 * @return {Roo.Element}
35909 getEl : function(){
35914 * Hides this region.
35917 //if(!this.collapsed){
35918 this.el.dom.style.left = "-2000px";
35921 // this.collapsedEl.dom.style.left = "-2000px";
35922 // this.collapsedEl.hide();
35924 this.visible = false;
35925 this.fireEvent("visibilitychange", this, false);
35929 * Shows this region if it was previously hidden.
35932 //if(!this.collapsed){
35935 // this.collapsedEl.show();
35937 this.visible = true;
35938 this.fireEvent("visibilitychange", this, true);
35941 closeClicked : function(){
35942 if(this.activePanel){
35943 this.remove(this.activePanel);
35947 collapseClick : function(e){
35949 e.stopPropagation();
35952 e.stopPropagation();
35958 * Collapses this region.
35959 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35962 collapse : function(skipAnim, skipCheck = false){
35963 if(this.collapsed) {
35967 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35969 this.collapsed = true;
35971 this.split.el.hide();
35973 if(this.config.animate && skipAnim !== true){
35974 this.fireEvent("invalidated", this);
35975 this.animateCollapse();
35977 this.el.setLocation(-20000,-20000);
35979 this.collapsedEl.show();
35980 this.fireEvent("collapsed", this);
35981 this.fireEvent("invalidated", this);
35987 animateCollapse : function(){
35992 * Expands this region if it was previously collapsed.
35993 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35994 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35997 expand : function(e, skipAnim){
35999 e.stopPropagation();
36001 if(!this.collapsed || this.el.hasActiveFx()) {
36005 this.afterSlideIn();
36008 this.collapsed = false;
36009 if(this.config.animate && skipAnim !== true){
36010 this.animateExpand();
36014 this.split.el.show();
36016 this.collapsedEl.setLocation(-2000,-2000);
36017 this.collapsedEl.hide();
36018 this.fireEvent("invalidated", this);
36019 this.fireEvent("expanded", this);
36023 animateExpand : function(){
36027 initTabs : function()
36029 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36031 var ts = new Roo.bootstrap.panel.Tabs({
36032 el: this.bodyEl.dom,
36033 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36034 disableTooltips: this.config.disableTabTips,
36035 toolbar : this.config.toolbar
36038 if(this.config.hideTabs){
36039 ts.stripWrap.setDisplayed(false);
36042 ts.resizeTabs = this.config.resizeTabs === true;
36043 ts.minTabWidth = this.config.minTabWidth || 40;
36044 ts.maxTabWidth = this.config.maxTabWidth || 250;
36045 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36046 ts.monitorResize = false;
36047 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36048 ts.bodyEl.addClass('roo-layout-tabs-body');
36049 this.panels.each(this.initPanelAsTab, this);
36052 initPanelAsTab : function(panel){
36053 var ti = this.tabs.addTab(
36057 this.config.closeOnTab && panel.isClosable(),
36060 if(panel.tabTip !== undefined){
36061 ti.setTooltip(panel.tabTip);
36063 ti.on("activate", function(){
36064 this.setActivePanel(panel);
36067 if(this.config.closeOnTab){
36068 ti.on("beforeclose", function(t, e){
36070 this.remove(panel);
36074 panel.tabItem = ti;
36079 updatePanelTitle : function(panel, title)
36081 if(this.activePanel == panel){
36082 this.updateTitle(title);
36085 var ti = this.tabs.getTab(panel.getEl().id);
36087 if(panel.tabTip !== undefined){
36088 ti.setTooltip(panel.tabTip);
36093 updateTitle : function(title){
36094 if(this.titleTextEl && !this.config.title){
36095 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36099 setActivePanel : function(panel)
36101 panel = this.getPanel(panel);
36102 if(this.activePanel && this.activePanel != panel){
36103 if(this.activePanel.setActiveState(false) === false){
36107 this.activePanel = panel;
36108 panel.setActiveState(true);
36109 if(this.panelSize){
36110 panel.setSize(this.panelSize.width, this.panelSize.height);
36113 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36115 this.updateTitle(panel.getTitle());
36117 this.fireEvent("invalidated", this);
36119 this.fireEvent("panelactivated", this, panel);
36123 * Shows the specified panel.
36124 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36125 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36127 showPanel : function(panel)
36129 panel = this.getPanel(panel);
36132 var tab = this.tabs.getTab(panel.getEl().id);
36133 if(tab.isHidden()){
36134 this.tabs.unhideTab(tab.id);
36138 this.setActivePanel(panel);
36145 * Get the active panel for this region.
36146 * @return {Roo.ContentPanel} The active panel or null
36148 getActivePanel : function(){
36149 return this.activePanel;
36152 validateVisibility : function(){
36153 if(this.panels.getCount() < 1){
36154 this.updateTitle(" ");
36155 this.closeBtn.hide();
36158 if(!this.isVisible()){
36165 * Adds the passed ContentPanel(s) to this region.
36166 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36167 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36169 add : function(panel)
36171 if(arguments.length > 1){
36172 for(var i = 0, len = arguments.length; i < len; i++) {
36173 this.add(arguments[i]);
36178 // if we have not been rendered yet, then we can not really do much of this..
36179 if (!this.bodyEl) {
36180 this.unrendered_panels.push(panel);
36187 if(this.hasPanel(panel)){
36188 this.showPanel(panel);
36191 panel.setRegion(this);
36192 this.panels.add(panel);
36193 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36194 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36195 // and hide them... ???
36196 this.bodyEl.dom.appendChild(panel.getEl().dom);
36197 if(panel.background !== true){
36198 this.setActivePanel(panel);
36200 this.fireEvent("paneladded", this, panel);
36207 this.initPanelAsTab(panel);
36211 if(panel.background !== true){
36212 this.tabs.activate(panel.getEl().id);
36214 this.fireEvent("paneladded", this, panel);
36219 * Hides the tab for the specified panel.
36220 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36222 hidePanel : function(panel){
36223 if(this.tabs && (panel = this.getPanel(panel))){
36224 this.tabs.hideTab(panel.getEl().id);
36229 * Unhides the tab for a previously hidden panel.
36230 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36232 unhidePanel : function(panel){
36233 if(this.tabs && (panel = this.getPanel(panel))){
36234 this.tabs.unhideTab(panel.getEl().id);
36238 clearPanels : function(){
36239 while(this.panels.getCount() > 0){
36240 this.remove(this.panels.first());
36245 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36246 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36247 * @param {Boolean} preservePanel Overrides the config preservePanel option
36248 * @return {Roo.ContentPanel} The panel that was removed
36250 remove : function(panel, preservePanel)
36252 panel = this.getPanel(panel);
36257 this.fireEvent("beforeremove", this, panel, e);
36258 if(e.cancel === true){
36261 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36262 var panelId = panel.getId();
36263 this.panels.removeKey(panelId);
36265 document.body.appendChild(panel.getEl().dom);
36268 this.tabs.removeTab(panel.getEl().id);
36269 }else if (!preservePanel){
36270 this.bodyEl.dom.removeChild(panel.getEl().dom);
36272 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36273 var p = this.panels.first();
36274 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36275 tempEl.appendChild(p.getEl().dom);
36276 this.bodyEl.update("");
36277 this.bodyEl.dom.appendChild(p.getEl().dom);
36279 this.updateTitle(p.getTitle());
36281 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36282 this.setActivePanel(p);
36284 panel.setRegion(null);
36285 if(this.activePanel == panel){
36286 this.activePanel = null;
36288 if(this.config.autoDestroy !== false && preservePanel !== true){
36289 try{panel.destroy();}catch(e){}
36291 this.fireEvent("panelremoved", this, panel);
36296 * Returns the TabPanel component used by this region
36297 * @return {Roo.TabPanel}
36299 getTabs : function(){
36303 createTool : function(parentEl, className){
36304 var btn = Roo.DomHelper.append(parentEl, {
36306 cls: "x-layout-tools-button",
36309 cls: "roo-layout-tools-button-inner " + className,
36313 btn.addClassOnOver("roo-layout-tools-button-over");
36318 * Ext JS Library 1.1.1
36319 * Copyright(c) 2006-2007, Ext JS, LLC.
36321 * Originally Released Under LGPL - original licence link has changed is not relivant.
36324 * <script type="text/javascript">
36330 * @class Roo.SplitLayoutRegion
36331 * @extends Roo.LayoutRegion
36332 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36334 Roo.bootstrap.layout.Split = function(config){
36335 this.cursor = config.cursor;
36336 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36339 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36341 splitTip : "Drag to resize.",
36342 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36343 useSplitTips : false,
36345 applyConfig : function(config){
36346 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36349 onRender : function(ctr,pos) {
36351 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36352 if(!this.config.split){
36357 var splitEl = Roo.DomHelper.append(ctr.dom, {
36359 id: this.el.id + "-split",
36360 cls: "roo-layout-split roo-layout-split-"+this.position,
36363 /** The SplitBar for this region
36364 * @type Roo.SplitBar */
36365 // does not exist yet...
36366 Roo.log([this.position, this.orientation]);
36368 this.split = new Roo.bootstrap.SplitBar({
36369 dragElement : splitEl,
36370 resizingElement: this.el,
36371 orientation : this.orientation
36374 this.split.on("moved", this.onSplitMove, this);
36375 this.split.useShim = this.config.useShim === true;
36376 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36377 if(this.useSplitTips){
36378 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36380 //if(config.collapsible){
36381 // this.split.el.on("dblclick", this.collapse, this);
36384 if(typeof this.config.minSize != "undefined"){
36385 this.split.minSize = this.config.minSize;
36387 if(typeof this.config.maxSize != "undefined"){
36388 this.split.maxSize = this.config.maxSize;
36390 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36391 this.hideSplitter();
36396 getHMaxSize : function(){
36397 var cmax = this.config.maxSize || 10000;
36398 var center = this.mgr.getRegion("center");
36399 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36402 getVMaxSize : function(){
36403 var cmax = this.config.maxSize || 10000;
36404 var center = this.mgr.getRegion("center");
36405 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36408 onSplitMove : function(split, newSize){
36409 this.fireEvent("resized", this, newSize);
36413 * Returns the {@link Roo.SplitBar} for this region.
36414 * @return {Roo.SplitBar}
36416 getSplitBar : function(){
36421 this.hideSplitter();
36422 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36425 hideSplitter : function(){
36427 this.split.el.setLocation(-2000,-2000);
36428 this.split.el.hide();
36434 this.split.el.show();
36436 Roo.bootstrap.layout.Split.superclass.show.call(this);
36439 beforeSlide: function(){
36440 if(Roo.isGecko){// firefox overflow auto bug workaround
36441 this.bodyEl.clip();
36443 this.tabs.bodyEl.clip();
36445 if(this.activePanel){
36446 this.activePanel.getEl().clip();
36448 if(this.activePanel.beforeSlide){
36449 this.activePanel.beforeSlide();
36455 afterSlide : function(){
36456 if(Roo.isGecko){// firefox overflow auto bug workaround
36457 this.bodyEl.unclip();
36459 this.tabs.bodyEl.unclip();
36461 if(this.activePanel){
36462 this.activePanel.getEl().unclip();
36463 if(this.activePanel.afterSlide){
36464 this.activePanel.afterSlide();
36470 initAutoHide : function(){
36471 if(this.autoHide !== false){
36472 if(!this.autoHideHd){
36473 var st = new Roo.util.DelayedTask(this.slideIn, this);
36474 this.autoHideHd = {
36475 "mouseout": function(e){
36476 if(!e.within(this.el, true)){
36480 "mouseover" : function(e){
36486 this.el.on(this.autoHideHd);
36490 clearAutoHide : function(){
36491 if(this.autoHide !== false){
36492 this.el.un("mouseout", this.autoHideHd.mouseout);
36493 this.el.un("mouseover", this.autoHideHd.mouseover);
36497 clearMonitor : function(){
36498 Roo.get(document).un("click", this.slideInIf, this);
36501 // these names are backwards but not changed for compat
36502 slideOut : function(){
36503 if(this.isSlid || this.el.hasActiveFx()){
36506 this.isSlid = true;
36507 if(this.collapseBtn){
36508 this.collapseBtn.hide();
36510 this.closeBtnState = this.closeBtn.getStyle('display');
36511 this.closeBtn.hide();
36513 this.stickBtn.show();
36516 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36517 this.beforeSlide();
36518 this.el.setStyle("z-index", 10001);
36519 this.el.slideIn(this.getSlideAnchor(), {
36520 callback: function(){
36522 this.initAutoHide();
36523 Roo.get(document).on("click", this.slideInIf, this);
36524 this.fireEvent("slideshow", this);
36531 afterSlideIn : function(){
36532 this.clearAutoHide();
36533 this.isSlid = false;
36534 this.clearMonitor();
36535 this.el.setStyle("z-index", "");
36536 if(this.collapseBtn){
36537 this.collapseBtn.show();
36539 this.closeBtn.setStyle('display', this.closeBtnState);
36541 this.stickBtn.hide();
36543 this.fireEvent("slidehide", this);
36546 slideIn : function(cb){
36547 if(!this.isSlid || this.el.hasActiveFx()){
36551 this.isSlid = false;
36552 this.beforeSlide();
36553 this.el.slideOut(this.getSlideAnchor(), {
36554 callback: function(){
36555 this.el.setLeftTop(-10000, -10000);
36557 this.afterSlideIn();
36565 slideInIf : function(e){
36566 if(!e.within(this.el)){
36571 animateCollapse : function(){
36572 this.beforeSlide();
36573 this.el.setStyle("z-index", 20000);
36574 var anchor = this.getSlideAnchor();
36575 this.el.slideOut(anchor, {
36576 callback : function(){
36577 this.el.setStyle("z-index", "");
36578 this.collapsedEl.slideIn(anchor, {duration:.3});
36580 this.el.setLocation(-10000,-10000);
36582 this.fireEvent("collapsed", this);
36589 animateExpand : function(){
36590 this.beforeSlide();
36591 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36592 this.el.setStyle("z-index", 20000);
36593 this.collapsedEl.hide({
36596 this.el.slideIn(this.getSlideAnchor(), {
36597 callback : function(){
36598 this.el.setStyle("z-index", "");
36601 this.split.el.show();
36603 this.fireEvent("invalidated", this);
36604 this.fireEvent("expanded", this);
36632 getAnchor : function(){
36633 return this.anchors[this.position];
36636 getCollapseAnchor : function(){
36637 return this.canchors[this.position];
36640 getSlideAnchor : function(){
36641 return this.sanchors[this.position];
36644 getAlignAdj : function(){
36645 var cm = this.cmargins;
36646 switch(this.position){
36662 getExpandAdj : function(){
36663 var c = this.collapsedEl, cm = this.cmargins;
36664 switch(this.position){
36666 return [-(cm.right+c.getWidth()+cm.left), 0];
36669 return [cm.right+c.getWidth()+cm.left, 0];
36672 return [0, -(cm.top+cm.bottom+c.getHeight())];
36675 return [0, cm.top+cm.bottom+c.getHeight()];
36681 * Ext JS Library 1.1.1
36682 * Copyright(c) 2006-2007, Ext JS, LLC.
36684 * Originally Released Under LGPL - original licence link has changed is not relivant.
36687 * <script type="text/javascript">
36690 * These classes are private internal classes
36692 Roo.bootstrap.layout.Center = function(config){
36693 config.region = "center";
36694 Roo.bootstrap.layout.Region.call(this, config);
36695 this.visible = true;
36696 this.minWidth = config.minWidth || 20;
36697 this.minHeight = config.minHeight || 20;
36700 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36702 // center panel can't be hidden
36706 // center panel can't be hidden
36709 getMinWidth: function(){
36710 return this.minWidth;
36713 getMinHeight: function(){
36714 return this.minHeight;
36727 Roo.bootstrap.layout.North = function(config)
36729 config.region = 'north';
36730 config.cursor = 'n-resize';
36732 Roo.bootstrap.layout.Split.call(this, config);
36736 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36737 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36738 this.split.el.addClass("roo-layout-split-v");
36740 var size = config.initialSize || config.height;
36741 if(typeof size != "undefined"){
36742 this.el.setHeight(size);
36745 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36747 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36751 getBox : function(){
36752 if(this.collapsed){
36753 return this.collapsedEl.getBox();
36755 var box = this.el.getBox();
36757 box.height += this.split.el.getHeight();
36762 updateBox : function(box){
36763 if(this.split && !this.collapsed){
36764 box.height -= this.split.el.getHeight();
36765 this.split.el.setLeft(box.x);
36766 this.split.el.setTop(box.y+box.height);
36767 this.split.el.setWidth(box.width);
36769 if(this.collapsed){
36770 this.updateBody(box.width, null);
36772 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36780 Roo.bootstrap.layout.South = function(config){
36781 config.region = 'south';
36782 config.cursor = 's-resize';
36783 Roo.bootstrap.layout.Split.call(this, config);
36785 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36786 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36787 this.split.el.addClass("roo-layout-split-v");
36789 var size = config.initialSize || config.height;
36790 if(typeof size != "undefined"){
36791 this.el.setHeight(size);
36795 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36796 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36797 getBox : function(){
36798 if(this.collapsed){
36799 return this.collapsedEl.getBox();
36801 var box = this.el.getBox();
36803 var sh = this.split.el.getHeight();
36810 updateBox : function(box){
36811 if(this.split && !this.collapsed){
36812 var sh = this.split.el.getHeight();
36815 this.split.el.setLeft(box.x);
36816 this.split.el.setTop(box.y-sh);
36817 this.split.el.setWidth(box.width);
36819 if(this.collapsed){
36820 this.updateBody(box.width, null);
36822 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36826 Roo.bootstrap.layout.East = function(config){
36827 config.region = "east";
36828 config.cursor = "e-resize";
36829 Roo.bootstrap.layout.Split.call(this, config);
36831 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36832 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36833 this.split.el.addClass("roo-layout-split-h");
36835 var size = config.initialSize || config.width;
36836 if(typeof size != "undefined"){
36837 this.el.setWidth(size);
36840 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36841 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36842 getBox : function(){
36843 if(this.collapsed){
36844 return this.collapsedEl.getBox();
36846 var box = this.el.getBox();
36848 var sw = this.split.el.getWidth();
36855 updateBox : function(box){
36856 if(this.split && !this.collapsed){
36857 var sw = this.split.el.getWidth();
36859 this.split.el.setLeft(box.x);
36860 this.split.el.setTop(box.y);
36861 this.split.el.setHeight(box.height);
36864 if(this.collapsed){
36865 this.updateBody(null, box.height);
36867 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36871 Roo.bootstrap.layout.West = function(config){
36872 config.region = "west";
36873 config.cursor = "w-resize";
36875 Roo.bootstrap.layout.Split.call(this, config);
36877 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36878 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36879 this.split.el.addClass("roo-layout-split-h");
36883 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36884 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36886 onRender: function(ctr, pos)
36888 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36889 var size = this.config.initialSize || this.config.width;
36890 if(typeof size != "undefined"){
36891 this.el.setWidth(size);
36895 getBox : function(){
36896 if(this.collapsed){
36897 return this.collapsedEl.getBox();
36899 var box = this.el.getBox();
36901 box.width += this.split.el.getWidth();
36906 updateBox : function(box){
36907 if(this.split && !this.collapsed){
36908 var sw = this.split.el.getWidth();
36910 this.split.el.setLeft(box.x+box.width);
36911 this.split.el.setTop(box.y);
36912 this.split.el.setHeight(box.height);
36914 if(this.collapsed){
36915 this.updateBody(null, box.height);
36917 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36920 Roo.namespace("Roo.bootstrap.panel");/*
36922 * Ext JS Library 1.1.1
36923 * Copyright(c) 2006-2007, Ext JS, LLC.
36925 * Originally Released Under LGPL - original licence link has changed is not relivant.
36928 * <script type="text/javascript">
36931 * @class Roo.ContentPanel
36932 * @extends Roo.util.Observable
36933 * A basic ContentPanel element.
36934 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36935 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36936 * @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
36937 * @cfg {Boolean} closable True if the panel can be closed/removed
36938 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36939 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36940 * @cfg {Toolbar} toolbar A toolbar for this panel
36941 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36942 * @cfg {String} title The title for this panel
36943 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36944 * @cfg {String} url Calls {@link #setUrl} with this value
36945 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36946 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36947 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36948 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36949 * @cfg {Boolean} badges render the badges
36952 * Create a new ContentPanel.
36953 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36954 * @param {String/Object} config A string to set only the title or a config object
36955 * @param {String} content (optional) Set the HTML content for this panel
36956 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36958 Roo.bootstrap.panel.Content = function( config){
36960 this.tpl = config.tpl || false;
36962 var el = config.el;
36963 var content = config.content;
36965 if(config.autoCreate){ // xtype is available if this is called from factory
36968 this.el = Roo.get(el);
36969 if(!this.el && config && config.autoCreate){
36970 if(typeof config.autoCreate == "object"){
36971 if(!config.autoCreate.id){
36972 config.autoCreate.id = config.id||el;
36974 this.el = Roo.DomHelper.append(document.body,
36975 config.autoCreate, true);
36977 var elcfg = { tag: "div",
36978 cls: "roo-layout-inactive-content",
36982 elcfg.html = config.html;
36986 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36989 this.closable = false;
36990 this.loaded = false;
36991 this.active = false;
36994 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36996 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36998 this.wrapEl = this.el; //this.el.wrap();
37000 if (config.toolbar.items) {
37001 ti = config.toolbar.items ;
37002 delete config.toolbar.items ;
37006 this.toolbar.render(this.wrapEl, 'before');
37007 for(var i =0;i < ti.length;i++) {
37008 // Roo.log(['add child', items[i]]);
37009 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37011 this.toolbar.items = nitems;
37012 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37013 delete config.toolbar;
37017 // xtype created footer. - not sure if will work as we normally have to render first..
37018 if (this.footer && !this.footer.el && this.footer.xtype) {
37019 if (!this.wrapEl) {
37020 this.wrapEl = this.el.wrap();
37023 this.footer.container = this.wrapEl.createChild();
37025 this.footer = Roo.factory(this.footer, Roo);
37030 if(typeof config == "string"){
37031 this.title = config;
37033 Roo.apply(this, config);
37037 this.resizeEl = Roo.get(this.resizeEl, true);
37039 this.resizeEl = this.el;
37041 // handle view.xtype
37049 * Fires when this panel is activated.
37050 * @param {Roo.ContentPanel} this
37054 * @event deactivate
37055 * Fires when this panel is activated.
37056 * @param {Roo.ContentPanel} this
37058 "deactivate" : true,
37062 * Fires when this panel is resized if fitToFrame is true.
37063 * @param {Roo.ContentPanel} this
37064 * @param {Number} width The width after any component adjustments
37065 * @param {Number} height The height after any component adjustments
37071 * Fires when this tab is created
37072 * @param {Roo.ContentPanel} this
37083 if(this.autoScroll){
37084 this.resizeEl.setStyle("overflow", "auto");
37086 // fix randome scrolling
37087 //this.el.on('scroll', function() {
37088 // Roo.log('fix random scolling');
37089 // this.scrollTo('top',0);
37092 content = content || this.content;
37094 this.setContent(content);
37096 if(config && config.url){
37097 this.setUrl(this.url, this.params, this.loadOnce);
37102 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37104 if (this.view && typeof(this.view.xtype) != 'undefined') {
37105 this.view.el = this.el.appendChild(document.createElement("div"));
37106 this.view = Roo.factory(this.view);
37107 this.view.render && this.view.render(false, '');
37111 this.fireEvent('render', this);
37114 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37118 setRegion : function(region){
37119 this.region = region;
37120 this.setActiveClass(region && !this.background);
37124 setActiveClass: function(state)
37127 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37128 this.el.setStyle('position','relative');
37130 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37131 this.el.setStyle('position', 'absolute');
37136 * Returns the toolbar for this Panel if one was configured.
37137 * @return {Roo.Toolbar}
37139 getToolbar : function(){
37140 return this.toolbar;
37143 setActiveState : function(active)
37145 this.active = active;
37146 this.setActiveClass(active);
37148 if(this.fireEvent("deactivate", this) === false){
37153 this.fireEvent("activate", this);
37157 * Updates this panel's element
37158 * @param {String} content The new content
37159 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37161 setContent : function(content, loadScripts){
37162 this.el.update(content, loadScripts);
37165 ignoreResize : function(w, h){
37166 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37169 this.lastSize = {width: w, height: h};
37174 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37175 * @return {Roo.UpdateManager} The UpdateManager
37177 getUpdateManager : function(){
37178 return this.el.getUpdateManager();
37181 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37182 * @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:
37185 url: "your-url.php",
37186 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37187 callback: yourFunction,
37188 scope: yourObject, //(optional scope)
37191 text: "Loading...",
37196 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37197 * 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.
37198 * @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}
37199 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37200 * @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.
37201 * @return {Roo.ContentPanel} this
37204 var um = this.el.getUpdateManager();
37205 um.update.apply(um, arguments);
37211 * 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.
37212 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37213 * @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)
37214 * @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)
37215 * @return {Roo.UpdateManager} The UpdateManager
37217 setUrl : function(url, params, loadOnce){
37218 if(this.refreshDelegate){
37219 this.removeListener("activate", this.refreshDelegate);
37221 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37222 this.on("activate", this.refreshDelegate);
37223 return this.el.getUpdateManager();
37226 _handleRefresh : function(url, params, loadOnce){
37227 if(!loadOnce || !this.loaded){
37228 var updater = this.el.getUpdateManager();
37229 updater.update(url, params, this._setLoaded.createDelegate(this));
37233 _setLoaded : function(){
37234 this.loaded = true;
37238 * Returns this panel's id
37241 getId : function(){
37246 * Returns this panel's element - used by regiosn to add.
37247 * @return {Roo.Element}
37249 getEl : function(){
37250 return this.wrapEl || this.el;
37255 adjustForComponents : function(width, height)
37257 //Roo.log('adjustForComponents ');
37258 if(this.resizeEl != this.el){
37259 width -= this.el.getFrameWidth('lr');
37260 height -= this.el.getFrameWidth('tb');
37263 var te = this.toolbar.getEl();
37264 te.setWidth(width);
37265 height -= te.getHeight();
37268 var te = this.footer.getEl();
37269 te.setWidth(width);
37270 height -= te.getHeight();
37274 if(this.adjustments){
37275 width += this.adjustments[0];
37276 height += this.adjustments[1];
37278 return {"width": width, "height": height};
37281 setSize : function(width, height){
37282 if(this.fitToFrame && !this.ignoreResize(width, height)){
37283 if(this.fitContainer && this.resizeEl != this.el){
37284 this.el.setSize(width, height);
37286 var size = this.adjustForComponents(width, height);
37287 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37288 this.fireEvent('resize', this, size.width, size.height);
37293 * Returns this panel's title
37296 getTitle : function(){
37298 if (typeof(this.title) != 'object') {
37303 for (var k in this.title) {
37304 if (!this.title.hasOwnProperty(k)) {
37308 if (k.indexOf('-') >= 0) {
37309 var s = k.split('-');
37310 for (var i = 0; i<s.length; i++) {
37311 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37314 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37321 * Set this panel's title
37322 * @param {String} title
37324 setTitle : function(title){
37325 this.title = title;
37327 this.region.updatePanelTitle(this, title);
37332 * Returns true is this panel was configured to be closable
37333 * @return {Boolean}
37335 isClosable : function(){
37336 return this.closable;
37339 beforeSlide : function(){
37341 this.resizeEl.clip();
37344 afterSlide : function(){
37346 this.resizeEl.unclip();
37350 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37351 * Will fail silently if the {@link #setUrl} method has not been called.
37352 * This does not activate the panel, just updates its content.
37354 refresh : function(){
37355 if(this.refreshDelegate){
37356 this.loaded = false;
37357 this.refreshDelegate();
37362 * Destroys this panel
37364 destroy : function(){
37365 this.el.removeAllListeners();
37366 var tempEl = document.createElement("span");
37367 tempEl.appendChild(this.el.dom);
37368 tempEl.innerHTML = "";
37374 * form - if the content panel contains a form - this is a reference to it.
37375 * @type {Roo.form.Form}
37379 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37380 * This contains a reference to it.
37386 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37396 * @param {Object} cfg Xtype definition of item to add.
37400 getChildContainer: function () {
37401 return this.getEl();
37406 var ret = new Roo.factory(cfg);
37411 if (cfg.xtype.match(/^Form$/)) {
37414 //if (this.footer) {
37415 // el = this.footer.container.insertSibling(false, 'before');
37417 el = this.el.createChild();
37420 this.form = new Roo.form.Form(cfg);
37423 if ( this.form.allItems.length) {
37424 this.form.render(el.dom);
37428 // should only have one of theses..
37429 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37430 // views.. should not be just added - used named prop 'view''
37432 cfg.el = this.el.appendChild(document.createElement("div"));
37435 var ret = new Roo.factory(cfg);
37437 ret.render && ret.render(false, ''); // render blank..
37447 * @class Roo.bootstrap.panel.Grid
37448 * @extends Roo.bootstrap.panel.Content
37450 * Create a new GridPanel.
37451 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37452 * @param {Object} config A the config object
37458 Roo.bootstrap.panel.Grid = function(config)
37462 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37463 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37465 config.el = this.wrapper;
37466 //this.el = this.wrapper;
37468 if (config.container) {
37469 // ctor'ed from a Border/panel.grid
37472 this.wrapper.setStyle("overflow", "hidden");
37473 this.wrapper.addClass('roo-grid-container');
37478 if(config.toolbar){
37479 var tool_el = this.wrapper.createChild();
37480 this.toolbar = Roo.factory(config.toolbar);
37482 if (config.toolbar.items) {
37483 ti = config.toolbar.items ;
37484 delete config.toolbar.items ;
37488 this.toolbar.render(tool_el);
37489 for(var i =0;i < ti.length;i++) {
37490 // Roo.log(['add child', items[i]]);
37491 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37493 this.toolbar.items = nitems;
37495 delete config.toolbar;
37498 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37499 config.grid.scrollBody = true;;
37500 config.grid.monitorWindowResize = false; // turn off autosizing
37501 config.grid.autoHeight = false;
37502 config.grid.autoWidth = false;
37504 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37506 if (config.background) {
37507 // render grid on panel activation (if panel background)
37508 this.on('activate', function(gp) {
37509 if (!gp.grid.rendered) {
37510 gp.grid.render(this.wrapper);
37511 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37516 this.grid.render(this.wrapper);
37517 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37520 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37521 // ??? needed ??? config.el = this.wrapper;
37526 // xtype created footer. - not sure if will work as we normally have to render first..
37527 if (this.footer && !this.footer.el && this.footer.xtype) {
37529 var ctr = this.grid.getView().getFooterPanel(true);
37530 this.footer.dataSource = this.grid.dataSource;
37531 this.footer = Roo.factory(this.footer, Roo);
37532 this.footer.render(ctr);
37542 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37543 getId : function(){
37544 return this.grid.id;
37548 * Returns the grid for this panel
37549 * @return {Roo.bootstrap.Table}
37551 getGrid : function(){
37555 setSize : function(width, height){
37556 if(!this.ignoreResize(width, height)){
37557 var grid = this.grid;
37558 var size = this.adjustForComponents(width, height);
37559 var gridel = grid.getGridEl();
37560 gridel.setSize(size.width, size.height);
37562 var thd = grid.getGridEl().select('thead',true).first();
37563 var tbd = grid.getGridEl().select('tbody', true).first();
37565 tbd.setSize(width, height - thd.getHeight());
37574 beforeSlide : function(){
37575 this.grid.getView().scroller.clip();
37578 afterSlide : function(){
37579 this.grid.getView().scroller.unclip();
37582 destroy : function(){
37583 this.grid.destroy();
37585 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37590 * @class Roo.bootstrap.panel.Nest
37591 * @extends Roo.bootstrap.panel.Content
37593 * Create a new Panel, that can contain a layout.Border.
37596 * @param {Roo.BorderLayout} layout The layout for this panel
37597 * @param {String/Object} config A string to set only the title or a config object
37599 Roo.bootstrap.panel.Nest = function(config)
37601 // construct with only one argument..
37602 /* FIXME - implement nicer consturctors
37603 if (layout.layout) {
37605 layout = config.layout;
37606 delete config.layout;
37608 if (layout.xtype && !layout.getEl) {
37609 // then layout needs constructing..
37610 layout = Roo.factory(layout, Roo);
37614 config.el = config.layout.getEl();
37616 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37618 config.layout.monitorWindowResize = false; // turn off autosizing
37619 this.layout = config.layout;
37620 this.layout.getEl().addClass("roo-layout-nested-layout");
37627 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37629 setSize : function(width, height){
37630 if(!this.ignoreResize(width, height)){
37631 var size = this.adjustForComponents(width, height);
37632 var el = this.layout.getEl();
37633 if (size.height < 1) {
37634 el.setWidth(size.width);
37636 el.setSize(size.width, size.height);
37638 var touch = el.dom.offsetWidth;
37639 this.layout.layout();
37640 // ie requires a double layout on the first pass
37641 if(Roo.isIE && !this.initialized){
37642 this.initialized = true;
37643 this.layout.layout();
37648 // activate all subpanels if not currently active..
37650 setActiveState : function(active){
37651 this.active = active;
37652 this.setActiveClass(active);
37655 this.fireEvent("deactivate", this);
37659 this.fireEvent("activate", this);
37660 // not sure if this should happen before or after..
37661 if (!this.layout) {
37662 return; // should not happen..
37665 for (var r in this.layout.regions) {
37666 reg = this.layout.getRegion(r);
37667 if (reg.getActivePanel()) {
37668 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37669 reg.setActivePanel(reg.getActivePanel());
37672 if (!reg.panels.length) {
37675 reg.showPanel(reg.getPanel(0));
37684 * Returns the nested BorderLayout for this panel
37685 * @return {Roo.BorderLayout}
37687 getLayout : function(){
37688 return this.layout;
37692 * Adds a xtype elements to the layout of the nested panel
37696 xtype : 'ContentPanel',
37703 xtype : 'NestedLayoutPanel',
37709 items : [ ... list of content panels or nested layout panels.. ]
37713 * @param {Object} cfg Xtype definition of item to add.
37715 addxtype : function(cfg) {
37716 return this.layout.addxtype(cfg);
37721 * Ext JS Library 1.1.1
37722 * Copyright(c) 2006-2007, Ext JS, LLC.
37724 * Originally Released Under LGPL - original licence link has changed is not relivant.
37727 * <script type="text/javascript">
37730 * @class Roo.TabPanel
37731 * @extends Roo.util.Observable
37732 * A lightweight tab container.
37736 // basic tabs 1, built from existing content
37737 var tabs = new Roo.TabPanel("tabs1");
37738 tabs.addTab("script", "View Script");
37739 tabs.addTab("markup", "View Markup");
37740 tabs.activate("script");
37742 // more advanced tabs, built from javascript
37743 var jtabs = new Roo.TabPanel("jtabs");
37744 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37746 // set up the UpdateManager
37747 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37748 var updater = tab2.getUpdateManager();
37749 updater.setDefaultUrl("ajax1.htm");
37750 tab2.on('activate', updater.refresh, updater, true);
37752 // Use setUrl for Ajax loading
37753 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37754 tab3.setUrl("ajax2.htm", null, true);
37757 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37760 jtabs.activate("jtabs-1");
37763 * Create a new TabPanel.
37764 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37765 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37767 Roo.bootstrap.panel.Tabs = function(config){
37769 * The container element for this TabPanel.
37770 * @type Roo.Element
37772 this.el = Roo.get(config.el);
37775 if(typeof config == "boolean"){
37776 this.tabPosition = config ? "bottom" : "top";
37778 Roo.apply(this, config);
37782 if(this.tabPosition == "bottom"){
37783 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37784 this.el.addClass("roo-tabs-bottom");
37786 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37787 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37788 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37790 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37792 if(this.tabPosition != "bottom"){
37793 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37794 * @type Roo.Element
37796 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37797 this.el.addClass("roo-tabs-top");
37801 this.bodyEl.setStyle("position", "relative");
37803 this.active = null;
37804 this.activateDelegate = this.activate.createDelegate(this);
37809 * Fires when the active tab changes
37810 * @param {Roo.TabPanel} this
37811 * @param {Roo.TabPanelItem} activePanel The new active tab
37815 * @event beforetabchange
37816 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37817 * @param {Roo.TabPanel} this
37818 * @param {Object} e Set cancel to true on this object to cancel the tab change
37819 * @param {Roo.TabPanelItem} tab The tab being changed to
37821 "beforetabchange" : true
37824 Roo.EventManager.onWindowResize(this.onResize, this);
37825 this.cpad = this.el.getPadding("lr");
37826 this.hiddenCount = 0;
37829 // toolbar on the tabbar support...
37830 if (this.toolbar) {
37831 alert("no toolbar support yet");
37832 this.toolbar = false;
37834 var tcfg = this.toolbar;
37835 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37836 this.toolbar = new Roo.Toolbar(tcfg);
37837 if (Roo.isSafari) {
37838 var tbl = tcfg.container.child('table', true);
37839 tbl.setAttribute('width', '100%');
37847 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37850 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37852 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37854 tabPosition : "top",
37856 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37858 currentTabWidth : 0,
37860 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37864 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37868 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37870 preferredTabWidth : 175,
37872 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37874 resizeTabs : false,
37876 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37878 monitorResize : true,
37880 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37885 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37886 * @param {String} id The id of the div to use <b>or create</b>
37887 * @param {String} text The text for the tab
37888 * @param {String} content (optional) Content to put in the TabPanelItem body
37889 * @param {Boolean} closable (optional) True to create a close icon on the tab
37890 * @return {Roo.TabPanelItem} The created TabPanelItem
37892 addTab : function(id, text, content, closable, tpl)
37894 var item = new Roo.bootstrap.panel.TabItem({
37898 closable : closable,
37901 this.addTabItem(item);
37903 item.setContent(content);
37909 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37910 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37911 * @return {Roo.TabPanelItem}
37913 getTab : function(id){
37914 return this.items[id];
37918 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37919 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37921 hideTab : function(id){
37922 var t = this.items[id];
37925 this.hiddenCount++;
37926 this.autoSizeTabs();
37931 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37932 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37934 unhideTab : function(id){
37935 var t = this.items[id];
37937 t.setHidden(false);
37938 this.hiddenCount--;
37939 this.autoSizeTabs();
37944 * Adds an existing {@link Roo.TabPanelItem}.
37945 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37947 addTabItem : function(item){
37948 this.items[item.id] = item;
37949 this.items.push(item);
37950 // if(this.resizeTabs){
37951 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37952 // this.autoSizeTabs();
37954 // item.autoSize();
37959 * Removes a {@link Roo.TabPanelItem}.
37960 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37962 removeTab : function(id){
37963 var items = this.items;
37964 var tab = items[id];
37965 if(!tab) { return; }
37966 var index = items.indexOf(tab);
37967 if(this.active == tab && items.length > 1){
37968 var newTab = this.getNextAvailable(index);
37973 this.stripEl.dom.removeChild(tab.pnode.dom);
37974 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37975 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37977 items.splice(index, 1);
37978 delete this.items[tab.id];
37979 tab.fireEvent("close", tab);
37980 tab.purgeListeners();
37981 this.autoSizeTabs();
37984 getNextAvailable : function(start){
37985 var items = this.items;
37987 // look for a next tab that will slide over to
37988 // replace the one being removed
37989 while(index < items.length){
37990 var item = items[++index];
37991 if(item && !item.isHidden()){
37995 // if one isn't found select the previous tab (on the left)
37998 var item = items[--index];
37999 if(item && !item.isHidden()){
38007 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38008 * @param {String/Number} id The id or index of the TabPanelItem to disable.
38010 disableTab : function(id){
38011 var tab = this.items[id];
38012 if(tab && this.active != tab){
38018 * Enables a {@link Roo.TabPanelItem} that is disabled.
38019 * @param {String/Number} id The id or index of the TabPanelItem to enable.
38021 enableTab : function(id){
38022 var tab = this.items[id];
38027 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38028 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38029 * @return {Roo.TabPanelItem} The TabPanelItem.
38031 activate : function(id){
38032 var tab = this.items[id];
38036 if(tab == this.active || tab.disabled){
38040 this.fireEvent("beforetabchange", this, e, tab);
38041 if(e.cancel !== true && !tab.disabled){
38043 this.active.hide();
38045 this.active = this.items[id];
38046 this.active.show();
38047 this.fireEvent("tabchange", this, this.active);
38053 * Gets the active {@link Roo.TabPanelItem}.
38054 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38056 getActiveTab : function(){
38057 return this.active;
38061 * Updates the tab body element to fit the height of the container element
38062 * for overflow scrolling
38063 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38065 syncHeight : function(targetHeight){
38066 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38067 var bm = this.bodyEl.getMargins();
38068 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38069 this.bodyEl.setHeight(newHeight);
38073 onResize : function(){
38074 if(this.monitorResize){
38075 this.autoSizeTabs();
38080 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38082 beginUpdate : function(){
38083 this.updating = true;
38087 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38089 endUpdate : function(){
38090 this.updating = false;
38091 this.autoSizeTabs();
38095 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38097 autoSizeTabs : function(){
38098 var count = this.items.length;
38099 var vcount = count - this.hiddenCount;
38100 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38103 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38104 var availWidth = Math.floor(w / vcount);
38105 var b = this.stripBody;
38106 if(b.getWidth() > w){
38107 var tabs = this.items;
38108 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38109 if(availWidth < this.minTabWidth){
38110 /*if(!this.sleft){ // incomplete scrolling code
38111 this.createScrollButtons();
38114 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38117 if(this.currentTabWidth < this.preferredTabWidth){
38118 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38124 * Returns the number of tabs in this TabPanel.
38127 getCount : function(){
38128 return this.items.length;
38132 * Resizes all the tabs to the passed width
38133 * @param {Number} The new width
38135 setTabWidth : function(width){
38136 this.currentTabWidth = width;
38137 for(var i = 0, len = this.items.length; i < len; i++) {
38138 if(!this.items[i].isHidden()) {
38139 this.items[i].setWidth(width);
38145 * Destroys this TabPanel
38146 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38148 destroy : function(removeEl){
38149 Roo.EventManager.removeResizeListener(this.onResize, this);
38150 for(var i = 0, len = this.items.length; i < len; i++){
38151 this.items[i].purgeListeners();
38153 if(removeEl === true){
38154 this.el.update("");
38159 createStrip : function(container)
38161 var strip = document.createElement("nav");
38162 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38163 container.appendChild(strip);
38167 createStripList : function(strip)
38169 // div wrapper for retard IE
38170 // returns the "tr" element.
38171 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38172 //'<div class="x-tabs-strip-wrap">'+
38173 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38174 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38175 return strip.firstChild; //.firstChild.firstChild.firstChild;
38177 createBody : function(container)
38179 var body = document.createElement("div");
38180 Roo.id(body, "tab-body");
38181 //Roo.fly(body).addClass("x-tabs-body");
38182 Roo.fly(body).addClass("tab-content");
38183 container.appendChild(body);
38186 createItemBody :function(bodyEl, id){
38187 var body = Roo.getDom(id);
38189 body = document.createElement("div");
38192 //Roo.fly(body).addClass("x-tabs-item-body");
38193 Roo.fly(body).addClass("tab-pane");
38194 bodyEl.insertBefore(body, bodyEl.firstChild);
38198 createStripElements : function(stripEl, text, closable, tpl)
38200 var td = document.createElement("li"); // was td..
38203 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38206 stripEl.appendChild(td);
38208 td.className = "x-tabs-closable";
38209 if(!this.closeTpl){
38210 this.closeTpl = new Roo.Template(
38211 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38212 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38213 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38216 var el = this.closeTpl.overwrite(td, {"text": text});
38217 var close = el.getElementsByTagName("div")[0];
38218 var inner = el.getElementsByTagName("em")[0];
38219 return {"el": el, "close": close, "inner": inner};
38222 // not sure what this is..
38223 // if(!this.tabTpl){
38224 //this.tabTpl = new Roo.Template(
38225 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38226 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38228 // this.tabTpl = new Roo.Template(
38229 // '<a href="#">' +
38230 // '<span unselectable="on"' +
38231 // (this.disableTooltips ? '' : ' title="{text}"') +
38232 // ' >{text}</span></a>'
38238 var template = tpl || this.tabTpl || false;
38242 template = new Roo.Template(
38244 '<span unselectable="on"' +
38245 (this.disableTooltips ? '' : ' title="{text}"') +
38246 ' >{text}</span></a>'
38250 switch (typeof(template)) {
38254 template = new Roo.Template(template);
38260 var el = template.overwrite(td, {"text": text});
38262 var inner = el.getElementsByTagName("span")[0];
38264 return {"el": el, "inner": inner};
38272 * @class Roo.TabPanelItem
38273 * @extends Roo.util.Observable
38274 * Represents an individual item (tab plus body) in a TabPanel.
38275 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38276 * @param {String} id The id of this TabPanelItem
38277 * @param {String} text The text for the tab of this TabPanelItem
38278 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38280 Roo.bootstrap.panel.TabItem = function(config){
38282 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38283 * @type Roo.TabPanel
38285 this.tabPanel = config.panel;
38287 * The id for this TabPanelItem
38290 this.id = config.id;
38292 this.disabled = false;
38294 this.text = config.text;
38296 this.loaded = false;
38297 this.closable = config.closable;
38300 * The body element for this TabPanelItem.
38301 * @type Roo.Element
38303 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38304 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38305 this.bodyEl.setStyle("display", "block");
38306 this.bodyEl.setStyle("zoom", "1");
38307 //this.hideAction();
38309 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38311 this.el = Roo.get(els.el);
38312 this.inner = Roo.get(els.inner, true);
38313 this.textEl = Roo.get(this.el.dom.firstChild, true);
38314 this.pnode = Roo.get(els.el.parentNode, true);
38315 // this.el.on("mousedown", this.onTabMouseDown, this);
38316 this.el.on("click", this.onTabClick, this);
38318 if(config.closable){
38319 var c = Roo.get(els.close, true);
38320 c.dom.title = this.closeText;
38321 c.addClassOnOver("close-over");
38322 c.on("click", this.closeClick, this);
38328 * Fires when this tab becomes the active tab.
38329 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38330 * @param {Roo.TabPanelItem} this
38334 * @event beforeclose
38335 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38336 * @param {Roo.TabPanelItem} this
38337 * @param {Object} e Set cancel to true on this object to cancel the close.
38339 "beforeclose": true,
38342 * Fires when this tab is closed.
38343 * @param {Roo.TabPanelItem} this
38347 * @event deactivate
38348 * Fires when this tab is no longer the active tab.
38349 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38350 * @param {Roo.TabPanelItem} this
38352 "deactivate" : true
38354 this.hidden = false;
38356 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38359 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38361 purgeListeners : function(){
38362 Roo.util.Observable.prototype.purgeListeners.call(this);
38363 this.el.removeAllListeners();
38366 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38369 this.pnode.addClass("active");
38372 this.tabPanel.stripWrap.repaint();
38374 this.fireEvent("activate", this.tabPanel, this);
38378 * Returns true if this tab is the active tab.
38379 * @return {Boolean}
38381 isActive : function(){
38382 return this.tabPanel.getActiveTab() == this;
38386 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38389 this.pnode.removeClass("active");
38391 this.fireEvent("deactivate", this.tabPanel, this);
38394 hideAction : function(){
38395 this.bodyEl.hide();
38396 this.bodyEl.setStyle("position", "absolute");
38397 this.bodyEl.setLeft("-20000px");
38398 this.bodyEl.setTop("-20000px");
38401 showAction : function(){
38402 this.bodyEl.setStyle("position", "relative");
38403 this.bodyEl.setTop("");
38404 this.bodyEl.setLeft("");
38405 this.bodyEl.show();
38409 * Set the tooltip for the tab.
38410 * @param {String} tooltip The tab's tooltip
38412 setTooltip : function(text){
38413 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38414 this.textEl.dom.qtip = text;
38415 this.textEl.dom.removeAttribute('title');
38417 this.textEl.dom.title = text;
38421 onTabClick : function(e){
38422 e.preventDefault();
38423 this.tabPanel.activate(this.id);
38426 onTabMouseDown : function(e){
38427 e.preventDefault();
38428 this.tabPanel.activate(this.id);
38431 getWidth : function(){
38432 return this.inner.getWidth();
38435 setWidth : function(width){
38436 var iwidth = width - this.pnode.getPadding("lr");
38437 this.inner.setWidth(iwidth);
38438 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38439 this.pnode.setWidth(width);
38443 * Show or hide the tab
38444 * @param {Boolean} hidden True to hide or false to show.
38446 setHidden : function(hidden){
38447 this.hidden = hidden;
38448 this.pnode.setStyle("display", hidden ? "none" : "");
38452 * Returns true if this tab is "hidden"
38453 * @return {Boolean}
38455 isHidden : function(){
38456 return this.hidden;
38460 * Returns the text for this tab
38463 getText : function(){
38467 autoSize : function(){
38468 //this.el.beginMeasure();
38469 this.textEl.setWidth(1);
38471 * #2804 [new] Tabs in Roojs
38472 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38474 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38475 //this.el.endMeasure();
38479 * Sets the text for the tab (Note: this also sets the tooltip text)
38480 * @param {String} text The tab's text and tooltip
38482 setText : function(text){
38484 this.textEl.update(text);
38485 this.setTooltip(text);
38486 //if(!this.tabPanel.resizeTabs){
38487 // this.autoSize();
38491 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38493 activate : function(){
38494 this.tabPanel.activate(this.id);
38498 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38500 disable : function(){
38501 if(this.tabPanel.active != this){
38502 this.disabled = true;
38503 this.pnode.addClass("disabled");
38508 * Enables this TabPanelItem if it was previously disabled.
38510 enable : function(){
38511 this.disabled = false;
38512 this.pnode.removeClass("disabled");
38516 * Sets the content for this TabPanelItem.
38517 * @param {String} content The content
38518 * @param {Boolean} loadScripts true to look for and load scripts
38520 setContent : function(content, loadScripts){
38521 this.bodyEl.update(content, loadScripts);
38525 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38526 * @return {Roo.UpdateManager} The UpdateManager
38528 getUpdateManager : function(){
38529 return this.bodyEl.getUpdateManager();
38533 * Set a URL to be used to load the content for this TabPanelItem.
38534 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38535 * @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)
38536 * @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)
38537 * @return {Roo.UpdateManager} The UpdateManager
38539 setUrl : function(url, params, loadOnce){
38540 if(this.refreshDelegate){
38541 this.un('activate', this.refreshDelegate);
38543 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38544 this.on("activate", this.refreshDelegate);
38545 return this.bodyEl.getUpdateManager();
38549 _handleRefresh : function(url, params, loadOnce){
38550 if(!loadOnce || !this.loaded){
38551 var updater = this.bodyEl.getUpdateManager();
38552 updater.update(url, params, this._setLoaded.createDelegate(this));
38557 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38558 * Will fail silently if the setUrl method has not been called.
38559 * This does not activate the panel, just updates its content.
38561 refresh : function(){
38562 if(this.refreshDelegate){
38563 this.loaded = false;
38564 this.refreshDelegate();
38569 _setLoaded : function(){
38570 this.loaded = true;
38574 closeClick : function(e){
38577 this.fireEvent("beforeclose", this, o);
38578 if(o.cancel !== true){
38579 this.tabPanel.removeTab(this.id);
38583 * The text displayed in the tooltip for the close icon.
38586 closeText : "Close this tab"
38589 * This script refer to:
38590 * Title: International Telephone Input
38591 * Author: Jack O'Connor
38592 * Code version: v12.1.12
38593 * Availability: https://github.com/jackocnr/intl-tel-input.git
38596 Roo.bootstrap.PhoneInputData = function() {
38599 "Afghanistan (افغانستان)",
38604 "Albania (Shqipëri)",
38609 "Algeria (الجزائر)",
38634 "Antigua and Barbuda",
38644 "Armenia (Հայաստան)",
38660 "Austria (Österreich)",
38665 "Azerbaijan (Azərbaycan)",
38675 "Bahrain (البحرين)",
38680 "Bangladesh (বাংলাদেশ)",
38690 "Belarus (Беларусь)",
38695 "Belgium (België)",
38725 "Bosnia and Herzegovina (Босна и Херцеговина)",
38740 "British Indian Ocean Territory",
38745 "British Virgin Islands",
38755 "Bulgaria (България)",
38765 "Burundi (Uburundi)",
38770 "Cambodia (កម្ពុជា)",
38775 "Cameroon (Cameroun)",
38784 ["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"]
38787 "Cape Verde (Kabu Verdi)",
38792 "Caribbean Netherlands",
38803 "Central African Republic (République centrafricaine)",
38823 "Christmas Island",
38829 "Cocos (Keeling) Islands",
38840 "Comoros (جزر القمر)",
38845 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38850 "Congo (Republic) (Congo-Brazzaville)",
38870 "Croatia (Hrvatska)",
38891 "Czech Republic (Česká republika)",
38896 "Denmark (Danmark)",
38911 "Dominican Republic (República Dominicana)",
38915 ["809", "829", "849"]
38933 "Equatorial Guinea (Guinea Ecuatorial)",
38953 "Falkland Islands (Islas Malvinas)",
38958 "Faroe Islands (Føroyar)",
38979 "French Guiana (Guyane française)",
38984 "French Polynesia (Polynésie française)",
38999 "Georgia (საქართველო)",
39004 "Germany (Deutschland)",
39024 "Greenland (Kalaallit Nunaat)",
39061 "Guinea-Bissau (Guiné Bissau)",
39086 "Hungary (Magyarország)",
39091 "Iceland (Ísland)",
39111 "Iraq (العراق)",
39127 "Israel (ישראל)",
39154 "Jordan (الأردن)",
39159 "Kazakhstan (Казахстан)",
39180 "Kuwait (الكويت)",
39185 "Kyrgyzstan (Кыргызстан)",
39195 "Latvia (Latvija)",
39200 "Lebanon (لبنان)",
39215 "Libya (ليبيا)",
39225 "Lithuania (Lietuva)",
39240 "Macedonia (FYROM) (Македонија)",
39245 "Madagascar (Madagasikara)",
39275 "Marshall Islands",
39285 "Mauritania (موريتانيا)",
39290 "Mauritius (Moris)",
39311 "Moldova (Republica Moldova)",
39321 "Mongolia (Монгол)",
39326 "Montenegro (Crna Gora)",
39336 "Morocco (المغرب)",
39342 "Mozambique (Moçambique)",
39347 "Myanmar (Burma) (မြန်မာ)",
39352 "Namibia (Namibië)",
39367 "Netherlands (Nederland)",
39372 "New Caledonia (Nouvelle-Calédonie)",
39407 "North Korea (조선 민주주의 인민 공화국)",
39412 "Northern Mariana Islands",
39428 "Pakistan (پاکستان)",
39438 "Palestine (فلسطين)",
39448 "Papua New Guinea",
39490 "Réunion (La Réunion)",
39496 "Romania (România)",
39512 "Saint Barthélemy",
39523 "Saint Kitts and Nevis",
39533 "Saint Martin (Saint-Martin (partie française))",
39539 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39544 "Saint Vincent and the Grenadines",
39559 "São Tomé and Príncipe (São Tomé e Príncipe)",
39564 "Saudi Arabia (المملكة العربية السعودية)",
39569 "Senegal (Sénégal)",
39599 "Slovakia (Slovensko)",
39604 "Slovenia (Slovenija)",
39614 "Somalia (Soomaaliya)",
39624 "South Korea (대한민국)",
39629 "South Sudan (جنوب السودان)",
39639 "Sri Lanka (ශ්රී ලංකාව)",
39644 "Sudan (السودان)",
39654 "Svalbard and Jan Mayen",
39665 "Sweden (Sverige)",
39670 "Switzerland (Schweiz)",
39675 "Syria (سوريا)",
39720 "Trinidad and Tobago",
39725 "Tunisia (تونس)",
39730 "Turkey (Türkiye)",
39740 "Turks and Caicos Islands",
39750 "U.S. Virgin Islands",
39760 "Ukraine (Україна)",
39765 "United Arab Emirates (الإمارات العربية المتحدة)",
39787 "Uzbekistan (Oʻzbekiston)",
39797 "Vatican City (Città del Vaticano)",
39808 "Vietnam (Việt Nam)",
39813 "Wallis and Futuna (Wallis-et-Futuna)",
39818 "Western Sahara (الصحراء الغربية)",
39824 "Yemen (اليمن)",
39848 * This script refer to:
39849 * Title: International Telephone Input
39850 * Author: Jack O'Connor
39851 * Code version: v12.1.12
39852 * Availability: https://github.com/jackocnr/intl-tel-input.git
39856 * @class Roo.bootstrap.PhoneInput
39857 * @extends Roo.bootstrap.TriggerField
39858 * An input with International dial-code selection
39860 * @cfg {String} defaultDialCode default '+852'
39861 * @cfg {Array} preferedCountries default []
39864 * Create a new PhoneInput.
39865 * @param {Object} config Configuration options
39868 Roo.bootstrap.PhoneInput = function(config) {
39869 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39872 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39874 listWidth: undefined,
39876 selectedClass: 'active',
39878 invalidClass : "has-warning",
39880 validClass: 'has-success',
39882 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 // type: 'number', -- do not use number - we get the flaky up/down arrows.
39931 maxlength: this.max_length,
39932 cls : 'form-control tel-input',
39933 autocomplete: 'new-password'
39936 var hiddenInput = {
39939 cls: 'hidden-tel-input'
39943 hiddenInput.name = this.name;
39946 if (this.disabled) {
39947 input.disabled = true;
39950 var flag_container = {
39967 cls: this.hasFeedback ? 'has-feedback' : '',
39973 cls: 'dial-code-holder',
39980 cls: 'roo-select2-container input-group',
39987 if (this.fieldLabel.length) {
39990 tooltip: 'This field is required'
39996 cls: 'control-label',
40002 html: this.fieldLabel
40005 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40011 if(this.indicatorpos == 'right') {
40012 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40019 if(align == 'left') {
40027 if(this.labelWidth > 12){
40028 label.style = "width: " + this.labelWidth + 'px';
40030 if(this.labelWidth < 13 && this.labelmd == 0){
40031 this.labelmd = this.labelWidth;
40033 if(this.labellg > 0){
40034 label.cls += ' col-lg-' + this.labellg;
40035 input.cls += ' col-lg-' + (12 - this.labellg);
40037 if(this.labelmd > 0){
40038 label.cls += ' col-md-' + this.labelmd;
40039 container.cls += ' col-md-' + (12 - this.labelmd);
40041 if(this.labelsm > 0){
40042 label.cls += ' col-sm-' + this.labelsm;
40043 container.cls += ' col-sm-' + (12 - this.labelsm);
40045 if(this.labelxs > 0){
40046 label.cls += ' col-xs-' + this.labelxs;
40047 container.cls += ' col-xs-' + (12 - this.labelxs);
40057 var settings = this;
40059 ['xs','sm','md','lg'].map(function(size){
40060 if (settings[size]) {
40061 cfg.cls += ' col-' + size + '-' + settings[size];
40065 this.store = new Roo.data.Store({
40066 proxy : new Roo.data.MemoryProxy({}),
40067 reader : new Roo.data.JsonReader({
40078 'name' : 'dialCode',
40082 'name' : 'priority',
40086 'name' : 'areaCodes',
40093 if(!this.preferedCountries) {
40094 this.preferedCountries = [
40101 var p = this.preferedCountries.reverse();
40104 for (var i = 0; i < p.length; i++) {
40105 for (var j = 0; j < this.allCountries.length; j++) {
40106 if(this.allCountries[j].iso2 == p[i]) {
40107 var t = this.allCountries[j];
40108 this.allCountries.splice(j,1);
40109 this.allCountries.unshift(t);
40115 this.store.proxy.data = {
40117 data: this.allCountries
40123 initEvents : function()
40126 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40128 this.indicator = this.indicatorEl();
40129 this.flag = this.flagEl();
40130 this.dialCodeHolder = this.dialCodeHolderEl();
40132 this.trigger = this.el.select('div.flag-box',true).first();
40133 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40138 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40139 _this.list.setWidth(lw);
40142 this.list.on('mouseover', this.onViewOver, this);
40143 this.list.on('mousemove', this.onViewMove, this);
40144 this.inputEl().on("keyup", this.onKeyUp, this);
40145 this.inputEl().on("keypress", this.onKeyPress, this);
40147 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40149 this.view = new Roo.View(this.list, this.tpl, {
40150 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40153 this.view.on('click', this.onViewClick, this);
40154 this.setValue(this.defaultDialCode);
40157 onTriggerClick : function(e)
40159 Roo.log('trigger click');
40164 if(this.isExpanded()){
40166 this.hasFocus = false;
40168 this.store.load({});
40169 this.hasFocus = true;
40174 isExpanded : function()
40176 return this.list.isVisible();
40179 collapse : function()
40181 if(!this.isExpanded()){
40185 Roo.get(document).un('mousedown', this.collapseIf, this);
40186 Roo.get(document).un('mousewheel', this.collapseIf, this);
40187 this.fireEvent('collapse', this);
40191 expand : function()
40195 if(this.isExpanded() || !this.hasFocus){
40199 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40200 this.list.setWidth(lw);
40203 this.restrictHeight();
40205 Roo.get(document).on('mousedown', this.collapseIf, this);
40206 Roo.get(document).on('mousewheel', this.collapseIf, this);
40208 this.fireEvent('expand', this);
40211 restrictHeight : function()
40213 this.list.alignTo(this.inputEl(), this.listAlign);
40214 this.list.alignTo(this.inputEl(), this.listAlign);
40217 onViewOver : function(e, t)
40219 if(this.inKeyMode){
40222 var item = this.view.findItemFromChild(t);
40225 var index = this.view.indexOf(item);
40226 this.select(index, false);
40231 onViewClick : function(view, doFocus, el, e)
40233 var index = this.view.getSelectedIndexes()[0];
40235 var r = this.store.getAt(index);
40238 this.onSelect(r, index);
40240 if(doFocus !== false && !this.blockFocus){
40241 this.inputEl().focus();
40245 onViewMove : function(e, t)
40247 this.inKeyMode = false;
40250 select : function(index, scrollIntoView)
40252 this.selectedIndex = index;
40253 this.view.select(index);
40254 if(scrollIntoView !== false){
40255 var el = this.view.getNode(index);
40257 this.list.scrollChildIntoView(el, false);
40262 createList : function()
40264 this.list = Roo.get(document.body).createChild({
40266 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40267 style: 'display:none'
40270 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40273 collapseIf : function(e)
40275 var in_combo = e.within(this.el);
40276 var in_list = e.within(this.list);
40277 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40279 if (in_combo || in_list || is_list) {
40285 onSelect : function(record, index)
40287 if(this.fireEvent('beforeselect', this, record, index) !== false){
40289 this.setFlagClass(record.data.iso2);
40290 this.setDialCode(record.data.dialCode);
40291 this.hasFocus = false;
40293 this.fireEvent('select', this, record, index);
40297 flagEl : function()
40299 var flag = this.el.select('div.flag',true).first();
40306 dialCodeHolderEl : function()
40308 var d = this.el.select('input.dial-code-holder',true).first();
40315 setDialCode : function(v)
40317 this.dialCodeHolder.dom.value = '+'+v;
40320 setFlagClass : function(n)
40322 this.flag.dom.className = 'flag '+n;
40325 getValue : function()
40327 var v = this.inputEl().getValue();
40328 if(this.dialCodeHolder) {
40329 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40334 setValue : function(v)
40336 var d = this.getDialCode(v);
40338 //invalid dial code
40339 if(v.length == 0 || !d || d.length == 0) {
40341 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40342 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40348 this.setFlagClass(this.dialCodeMapping[d].iso2);
40349 this.setDialCode(d);
40350 this.inputEl().dom.value = v.replace('+'+d,'');
40351 this.hiddenEl().dom.value = this.getValue();
40356 getDialCode : function(v)
40360 if (v.length == 0) {
40361 return this.dialCodeHolder.dom.value;
40365 if (v.charAt(0) != "+") {
40368 var numericChars = "";
40369 for (var i = 1; i < v.length; i++) {
40370 var c = v.charAt(i);
40373 if (this.dialCodeMapping[numericChars]) {
40374 dialCode = v.substr(1, i);
40376 if (numericChars.length == 4) {
40386 this.setValue(this.defaultDialCode);
40390 hiddenEl : function()
40392 return this.el.select('input.hidden-tel-input',true).first();
40395 // after setting val
40396 onKeyUp : function(e){
40397 this.setValue(this.getValue());
40400 onKeyPress : function(e){
40401 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40408 * @class Roo.bootstrap.MoneyField
40409 * @extends Roo.bootstrap.ComboBox
40410 * Bootstrap MoneyField class
40413 * Create a new MoneyField.
40414 * @param {Object} config Configuration options
40417 Roo.bootstrap.MoneyField = function(config) {
40419 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40423 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40426 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40428 allowDecimals : true,
40430 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40432 decimalSeparator : ".",
40434 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40436 decimalPrecision : 0,
40438 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40440 allowNegative : true,
40442 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40446 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40448 minValue : Number.NEGATIVE_INFINITY,
40450 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40452 maxValue : Number.MAX_VALUE,
40454 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40456 minText : "The minimum value for this field is {0}",
40458 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40460 maxText : "The maximum value for this field is {0}",
40462 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40463 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40465 nanText : "{0} is not a valid number",
40467 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40471 * @cfg {String} defaults currency of the MoneyField
40472 * value should be in lkey
40474 defaultCurrency : false,
40476 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40478 thousandsDelimiter : false,
40480 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40491 getAutoCreate : function()
40493 var align = this.labelAlign || this.parentLabelAlign();
40505 cls : 'form-control roo-money-amount-input',
40506 autocomplete: 'new-password'
40509 var hiddenInput = {
40513 cls: 'hidden-number-input'
40516 if(this.max_length) {
40517 input.maxlength = this.max_length;
40521 hiddenInput.name = this.name;
40524 if (this.disabled) {
40525 input.disabled = true;
40528 var clg = 12 - this.inputlg;
40529 var cmd = 12 - this.inputmd;
40530 var csm = 12 - this.inputsm;
40531 var cxs = 12 - this.inputxs;
40535 cls : 'row roo-money-field',
40539 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40543 cls: 'roo-select2-container input-group',
40547 cls : 'form-control roo-money-currency-input',
40548 autocomplete: 'new-password',
40550 name : this.currencyName
40554 cls : 'input-group-addon',
40568 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40572 cls: this.hasFeedback ? 'has-feedback' : '',
40583 if (this.fieldLabel.length) {
40586 tooltip: 'This field is required'
40592 cls: 'control-label',
40598 html: this.fieldLabel
40601 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40607 if(this.indicatorpos == 'right') {
40608 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40615 if(align == 'left') {
40623 if(this.labelWidth > 12){
40624 label.style = "width: " + this.labelWidth + 'px';
40626 if(this.labelWidth < 13 && this.labelmd == 0){
40627 this.labelmd = this.labelWidth;
40629 if(this.labellg > 0){
40630 label.cls += ' col-lg-' + this.labellg;
40631 input.cls += ' col-lg-' + (12 - this.labellg);
40633 if(this.labelmd > 0){
40634 label.cls += ' col-md-' + this.labelmd;
40635 container.cls += ' col-md-' + (12 - this.labelmd);
40637 if(this.labelsm > 0){
40638 label.cls += ' col-sm-' + this.labelsm;
40639 container.cls += ' col-sm-' + (12 - this.labelsm);
40641 if(this.labelxs > 0){
40642 label.cls += ' col-xs-' + this.labelxs;
40643 container.cls += ' col-xs-' + (12 - this.labelxs);
40654 var settings = this;
40656 ['xs','sm','md','lg'].map(function(size){
40657 if (settings[size]) {
40658 cfg.cls += ' col-' + size + '-' + settings[size];
40665 initEvents : function()
40667 this.indicator = this.indicatorEl();
40669 this.initCurrencyEvent();
40671 this.initNumberEvent();
40674 initCurrencyEvent : function()
40677 throw "can not find store for combo";
40680 this.store = Roo.factory(this.store, Roo.data);
40681 this.store.parent = this;
40685 this.triggerEl = this.el.select('.input-group-addon', true).first();
40687 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40692 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40693 _this.list.setWidth(lw);
40696 this.list.on('mouseover', this.onViewOver, this);
40697 this.list.on('mousemove', this.onViewMove, this);
40698 this.list.on('scroll', this.onViewScroll, this);
40701 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40704 this.view = new Roo.View(this.list, this.tpl, {
40705 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40708 this.view.on('click', this.onViewClick, this);
40710 this.store.on('beforeload', this.onBeforeLoad, this);
40711 this.store.on('load', this.onLoad, this);
40712 this.store.on('loadexception', this.onLoadException, this);
40714 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40715 "up" : function(e){
40716 this.inKeyMode = true;
40720 "down" : function(e){
40721 if(!this.isExpanded()){
40722 this.onTriggerClick();
40724 this.inKeyMode = true;
40729 "enter" : function(e){
40732 if(this.fireEvent("specialkey", this, e)){
40733 this.onViewClick(false);
40739 "esc" : function(e){
40743 "tab" : function(e){
40746 if(this.fireEvent("specialkey", this, e)){
40747 this.onViewClick(false);
40755 doRelay : function(foo, bar, hname){
40756 if(hname == 'down' || this.scope.isExpanded()){
40757 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40765 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40769 initNumberEvent : function(e)
40771 this.inputEl().on("keydown" , this.fireKey, this);
40772 this.inputEl().on("focus", this.onFocus, this);
40773 this.inputEl().on("blur", this.onBlur, this);
40775 this.inputEl().relayEvent('keyup', this);
40777 if(this.indicator){
40778 this.indicator.addClass('invisible');
40781 this.originalValue = this.getValue();
40783 if(this.validationEvent == 'keyup'){
40784 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40785 this.inputEl().on('keyup', this.filterValidation, this);
40787 else if(this.validationEvent !== false){
40788 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40791 if(this.selectOnFocus){
40792 this.on("focus", this.preFocus, this);
40795 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40796 this.inputEl().on("keypress", this.filterKeys, this);
40798 this.inputEl().relayEvent('keypress', this);
40801 var allowed = "0123456789";
40803 if(this.allowDecimals){
40804 allowed += this.decimalSeparator;
40807 if(this.allowNegative){
40811 if(this.thousandsDelimiter) {
40815 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40817 var keyPress = function(e){
40819 var k = e.getKey();
40821 var c = e.getCharCode();
40824 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40825 allowed.indexOf(String.fromCharCode(c)) === -1
40831 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40835 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40840 this.inputEl().on("keypress", keyPress, this);
40844 onTriggerClick : function(e)
40851 this.loadNext = false;
40853 if(this.isExpanded()){
40858 this.hasFocus = true;
40860 if(this.triggerAction == 'all') {
40861 this.doQuery(this.allQuery, true);
40865 this.doQuery(this.getRawValue());
40868 getCurrency : function()
40870 var v = this.currencyEl().getValue();
40875 restrictHeight : function()
40877 this.list.alignTo(this.currencyEl(), this.listAlign);
40878 this.list.alignTo(this.currencyEl(), this.listAlign);
40881 onViewClick : function(view, doFocus, el, e)
40883 var index = this.view.getSelectedIndexes()[0];
40885 var r = this.store.getAt(index);
40888 this.onSelect(r, index);
40892 onSelect : function(record, index){
40894 if(this.fireEvent('beforeselect', this, record, index) !== false){
40896 this.setFromCurrencyData(index > -1 ? record.data : false);
40900 this.fireEvent('select', this, record, index);
40904 setFromCurrencyData : function(o)
40908 this.lastCurrency = o;
40910 if (this.currencyField) {
40911 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40913 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40916 this.lastSelectionText = currency;
40918 //setting default currency
40919 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40920 this.setCurrency(this.defaultCurrency);
40924 this.setCurrency(currency);
40927 setFromData : function(o)
40931 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40933 this.setFromCurrencyData(c);
40938 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40940 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40943 this.setValue(value);
40947 setCurrency : function(v)
40949 this.currencyValue = v;
40952 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40957 setValue : function(v)
40959 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40965 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40967 this.inputEl().dom.value = (v == '') ? '' :
40968 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40970 if(!this.allowZero && v === '0') {
40971 this.hiddenEl().dom.value = '';
40972 this.inputEl().dom.value = '';
40979 getRawValue : function()
40981 var v = this.inputEl().getValue();
40986 getValue : function()
40988 return this.fixPrecision(this.parseValue(this.getRawValue()));
40991 parseValue : function(value)
40993 if(this.thousandsDelimiter) {
40995 r = new RegExp(",", "g");
40996 value = value.replace(r, "");
40999 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41000 return isNaN(value) ? '' : value;
41004 fixPrecision : function(value)
41006 if(this.thousandsDelimiter) {
41008 r = new RegExp(",", "g");
41009 value = value.replace(r, "");
41012 var nan = isNaN(value);
41014 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41015 return nan ? '' : value;
41017 return parseFloat(value).toFixed(this.decimalPrecision);
41020 decimalPrecisionFcn : function(v)
41022 return Math.floor(v);
41025 validateValue : function(value)
41027 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41031 var num = this.parseValue(value);
41034 this.markInvalid(String.format(this.nanText, value));
41038 if(num < this.minValue){
41039 this.markInvalid(String.format(this.minText, this.minValue));
41043 if(num > this.maxValue){
41044 this.markInvalid(String.format(this.maxText, this.maxValue));
41051 validate : function()
41053 if(this.disabled || this.allowBlank){
41058 var currency = this.getCurrency();
41060 if(this.validateValue(this.getRawValue()) && currency.length){
41065 this.markInvalid();
41069 getName: function()
41074 beforeBlur : function()
41080 var v = this.parseValue(this.getRawValue());
41087 onBlur : function()
41091 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41092 //this.el.removeClass(this.focusClass);
41095 this.hasFocus = false;
41097 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41101 var v = this.getValue();
41103 if(String(v) !== String(this.startValue)){
41104 this.fireEvent('change', this, v, this.startValue);
41107 this.fireEvent("blur", this);
41110 inputEl : function()
41112 return this.el.select('.roo-money-amount-input', true).first();
41115 currencyEl : function()
41117 return this.el.select('.roo-money-currency-input', true).first();
41120 hiddenEl : function()
41122 return this.el.select('input.hidden-number-input',true).first();