4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
921 setWeight : function(str)
923 this.el.removeClass(this.weightClass);
924 this.el.addClass('btn-' + str);
938 * @class Roo.bootstrap.Column
939 * @extends Roo.bootstrap.Component
940 * Bootstrap Column class
941 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
942 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
943 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
944 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
945 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
946 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
947 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
948 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
951 * @cfg {Boolean} hidden (true|false) hide the element
952 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
953 * @cfg {String} fa (ban|check|...) font awesome icon
954 * @cfg {Number} fasize (1|2|....) font awsome size
956 * @cfg {String} icon (info-sign|check|...) glyphicon name
958 * @cfg {String} html content of column.
961 * Create a new Column
962 * @param {Object} config The config object
965 Roo.bootstrap.Column = function(config){
966 Roo.bootstrap.Column.superclass.constructor.call(this, config);
969 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
987 getAutoCreate : function(){
988 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
996 ['xs','sm','md','lg'].map(function(size){
997 //Roo.log( size + ':' + settings[size]);
999 if (settings[size+'off'] !== false) {
1000 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1003 if (settings[size] === false) {
1007 if (!settings[size]) { // 0 = hidden
1008 cfg.cls += ' hidden-' + size;
1011 cfg.cls += ' col-' + size + '-' + settings[size];
1016 cfg.cls += ' hidden';
1019 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1020 cfg.cls +=' alert alert-' + this.alert;
1024 if (this.html.length) {
1025 cfg.html = this.html;
1029 if (this.fasize > 1) {
1030 fasize = ' fa-' + this.fasize + 'x';
1032 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1037 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1056 * @class Roo.bootstrap.Container
1057 * @extends Roo.bootstrap.Component
1058 * Bootstrap Container class
1059 * @cfg {Boolean} jumbotron is it a jumbotron element
1060 * @cfg {String} html content of element
1061 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1062 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1063 * @cfg {String} header content of header (for panel)
1064 * @cfg {String} footer content of footer (for panel)
1065 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1066 * @cfg {String} tag (header|aside|section) type of HTML tag.
1067 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1068 * @cfg {String} fa font awesome icon
1069 * @cfg {String} icon (info-sign|check|...) glyphicon name
1070 * @cfg {Boolean} hidden (true|false) hide the element
1071 * @cfg {Boolean} expandable (true|false) default false
1072 * @cfg {Boolean} expanded (true|false) default true
1073 * @cfg {String} rheader contet on the right of header
1074 * @cfg {Boolean} clickable (true|false) default false
1078 * Create a new Container
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Container = function(config){
1083 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1089 * After the panel has been expand
1091 * @param {Roo.bootstrap.Container} this
1096 * After the panel has been collapsed
1098 * @param {Roo.bootstrap.Container} this
1103 * When a element is chick
1104 * @param {Roo.bootstrap.Container} this
1105 * @param {Roo.EventObject} e
1111 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1129 getChildContainer : function() {
1135 if (this.panel.length) {
1136 return this.el.select('.panel-body',true).first();
1143 getAutoCreate : function(){
1146 tag : this.tag || 'div',
1150 if (this.jumbotron) {
1151 cfg.cls = 'jumbotron';
1156 // - this is applied by the parent..
1158 // cfg.cls = this.cls + '';
1161 if (this.sticky.length) {
1163 var bd = Roo.get(document.body);
1164 if (!bd.hasClass('bootstrap-sticky')) {
1165 bd.addClass('bootstrap-sticky');
1166 Roo.select('html',true).setStyle('height', '100%');
1169 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1173 if (this.well.length) {
1174 switch (this.well) {
1177 cfg.cls +=' well well-' +this.well;
1186 cfg.cls += ' hidden';
1190 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1191 cfg.cls +=' alert alert-' + this.alert;
1196 if (this.panel.length) {
1197 cfg.cls += ' panel panel-' + this.panel;
1199 if (this.header.length) {
1203 if(this.expandable){
1205 cfg.cls = cfg.cls + ' expandable';
1209 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1217 cls : 'panel-title',
1218 html : (this.expandable ? ' ' : '') + this.header
1222 cls: 'panel-header-right',
1228 cls : 'panel-heading',
1229 style : this.expandable ? 'cursor: pointer' : '',
1237 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1242 if (this.footer.length) {
1244 cls : 'panel-footer',
1253 body.html = this.html || cfg.html;
1254 // prefix with the icons..
1256 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1259 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1264 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1265 cfg.cls = 'container';
1271 initEvents: function()
1273 if(this.expandable){
1274 var headerEl = this.headerEl();
1277 headerEl.on('click', this.onToggleClick, this);
1282 this.el.on('click', this.onClick, this);
1287 onToggleClick : function()
1289 var headerEl = this.headerEl();
1305 if(this.fireEvent('expand', this)) {
1307 this.expanded = true;
1309 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1311 this.el.select('.panel-body',true).first().removeClass('hide');
1313 var toggleEl = this.toggleEl();
1319 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1324 collapse : function()
1326 if(this.fireEvent('collapse', this)) {
1328 this.expanded = false;
1330 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1331 this.el.select('.panel-body',true).first().addClass('hide');
1333 var toggleEl = this.toggleEl();
1339 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1343 toggleEl : function()
1345 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1349 return this.el.select('.panel-heading .fa',true).first();
1352 headerEl : function()
1354 if(!this.el || !this.panel.length || !this.header.length){
1358 return this.el.select('.panel-heading',true).first()
1363 if(!this.el || !this.panel.length){
1367 return this.el.select('.panel-body',true).first()
1370 titleEl : function()
1372 if(!this.el || !this.panel.length || !this.header.length){
1376 return this.el.select('.panel-title',true).first();
1379 setTitle : function(v)
1381 var titleEl = this.titleEl();
1387 titleEl.dom.innerHTML = v;
1390 getTitle : function()
1393 var titleEl = this.titleEl();
1399 return titleEl.dom.innerHTML;
1402 setRightTitle : function(v)
1404 var t = this.el.select('.panel-header-right',true).first();
1410 t.dom.innerHTML = v;
1413 onClick : function(e)
1417 this.fireEvent('click', this, e);
1430 * @class Roo.bootstrap.Img
1431 * @extends Roo.bootstrap.Component
1432 * Bootstrap Img class
1433 * @cfg {Boolean} imgResponsive false | true
1434 * @cfg {String} border rounded | circle | thumbnail
1435 * @cfg {String} src image source
1436 * @cfg {String} alt image alternative text
1437 * @cfg {String} href a tag href
1438 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1439 * @cfg {String} xsUrl xs image source
1440 * @cfg {String} smUrl sm image source
1441 * @cfg {String} mdUrl md image source
1442 * @cfg {String} lgUrl lg image source
1445 * Create a new Input
1446 * @param {Object} config The config object
1449 Roo.bootstrap.Img = function(config){
1450 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1456 * The img click event for the img.
1457 * @param {Roo.EventObject} e
1463 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1465 imgResponsive: true,
1475 getAutoCreate : function()
1477 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1478 return this.createSingleImg();
1483 cls: 'roo-image-responsive-group',
1488 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1490 if(!_this[size + 'Url']){
1496 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1497 html: _this.html || cfg.html,
1498 src: _this[size + 'Url']
1501 img.cls += ' roo-image-responsive-' + size;
1503 var s = ['xs', 'sm', 'md', 'lg'];
1505 s.splice(s.indexOf(size), 1);
1507 Roo.each(s, function(ss){
1508 img.cls += ' hidden-' + ss;
1511 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1512 cfg.cls += ' img-' + _this.border;
1516 cfg.alt = _this.alt;
1529 a.target = _this.target;
1533 cfg.cn.push((_this.href) ? a : img);
1540 createSingleImg : function()
1544 cls: (this.imgResponsive) ? 'img-responsive' : '',
1546 src : 'about:blank' // just incase src get's set to undefined?!?
1549 cfg.html = this.html || cfg.html;
1551 cfg.src = this.src || cfg.src;
1553 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1554 cfg.cls += ' img-' + this.border;
1571 a.target = this.target;
1576 return (this.href) ? a : cfg;
1579 initEvents: function()
1582 this.el.on('click', this.onClick, this);
1587 onClick : function(e)
1589 Roo.log('img onclick');
1590 this.fireEvent('click', this, e);
1593 * Sets the url of the image - used to update it
1594 * @param {String} url the url of the image
1597 setSrc : function(url)
1601 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1602 this.el.dom.src = url;
1606 this.el.select('img', true).first().dom.src = url;
1622 * @class Roo.bootstrap.Link
1623 * @extends Roo.bootstrap.Component
1624 * Bootstrap Link Class
1625 * @cfg {String} alt image alternative text
1626 * @cfg {String} href a tag href
1627 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1628 * @cfg {String} html the content of the link.
1629 * @cfg {String} anchor name for the anchor link
1630 * @cfg {String} fa - favicon
1632 * @cfg {Boolean} preventDefault (true | false) default false
1636 * Create a new Input
1637 * @param {Object} config The config object
1640 Roo.bootstrap.Link = function(config){
1641 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1647 * The img click event for the img.
1648 * @param {Roo.EventObject} e
1654 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1658 preventDefault: false,
1664 getAutoCreate : function()
1666 var html = this.html || '';
1668 if (this.fa !== false) {
1669 html = '<i class="fa fa-' + this.fa + '"></i>';
1674 // anchor's do not require html/href...
1675 if (this.anchor === false) {
1677 cfg.href = this.href || '#';
1679 cfg.name = this.anchor;
1680 if (this.html !== false || this.fa !== false) {
1683 if (this.href !== false) {
1684 cfg.href = this.href;
1688 if(this.alt !== false){
1693 if(this.target !== false) {
1694 cfg.target = this.target;
1700 initEvents: function() {
1702 if(!this.href || this.preventDefault){
1703 this.el.on('click', this.onClick, this);
1707 onClick : function(e)
1709 if(this.preventDefault){
1712 //Roo.log('img onclick');
1713 this.fireEvent('click', this, e);
1726 * @class Roo.bootstrap.Header
1727 * @extends Roo.bootstrap.Component
1728 * Bootstrap Header class
1729 * @cfg {String} html content of header
1730 * @cfg {Number} level (1|2|3|4|5|6) default 1
1733 * Create a new Header
1734 * @param {Object} config The config object
1738 Roo.bootstrap.Header = function(config){
1739 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1742 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1750 getAutoCreate : function(){
1755 tag: 'h' + (1 *this.level),
1756 html: this.html || ''
1768 * Ext JS Library 1.1.1
1769 * Copyright(c) 2006-2007, Ext JS, LLC.
1771 * Originally Released Under LGPL - original licence link has changed is not relivant.
1774 * <script type="text/javascript">
1778 * @class Roo.bootstrap.MenuMgr
1779 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1782 Roo.bootstrap.MenuMgr = function(){
1783 var menus, active, groups = {}, attached = false, lastShow = new Date();
1785 // private - called when first menu is created
1788 active = new Roo.util.MixedCollection();
1789 Roo.get(document).addKeyListener(27, function(){
1790 if(active.length > 0){
1798 if(active && active.length > 0){
1799 var c = active.clone();
1809 if(active.length < 1){
1810 Roo.get(document).un("mouseup", onMouseDown);
1818 var last = active.last();
1819 lastShow = new Date();
1822 Roo.get(document).on("mouseup", onMouseDown);
1827 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1828 m.parentMenu.activeChild = m;
1829 }else if(last && last.isVisible()){
1830 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1835 function onBeforeHide(m){
1837 m.activeChild.hide();
1839 if(m.autoHideTimer){
1840 clearTimeout(m.autoHideTimer);
1841 delete m.autoHideTimer;
1846 function onBeforeShow(m){
1847 var pm = m.parentMenu;
1848 if(!pm && !m.allowOtherMenus){
1850 }else if(pm && pm.activeChild && active != m){
1851 pm.activeChild.hide();
1855 // private this should really trigger on mouseup..
1856 function onMouseDown(e){
1857 Roo.log("on Mouse Up");
1859 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1860 Roo.log("MenuManager hideAll");
1869 function onBeforeCheck(mi, state){
1871 var g = groups[mi.group];
1872 for(var i = 0, l = g.length; i < l; i++){
1874 g[i].setChecked(false);
1883 * Hides all menus that are currently visible
1885 hideAll : function(){
1890 register : function(menu){
1894 menus[menu.id] = menu;
1895 menu.on("beforehide", onBeforeHide);
1896 menu.on("hide", onHide);
1897 menu.on("beforeshow", onBeforeShow);
1898 menu.on("show", onShow);
1900 if(g && menu.events["checkchange"]){
1904 groups[g].push(menu);
1905 menu.on("checkchange", onCheck);
1910 * Returns a {@link Roo.menu.Menu} object
1911 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1912 * be used to generate and return a new Menu instance.
1914 get : function(menu){
1915 if(typeof menu == "string"){ // menu id
1917 }else if(menu.events){ // menu instance
1920 /*else if(typeof menu.length == 'number'){ // array of menu items?
1921 return new Roo.bootstrap.Menu({items:menu});
1922 }else{ // otherwise, must be a config
1923 return new Roo.bootstrap.Menu(menu);
1930 unregister : function(menu){
1931 delete menus[menu.id];
1932 menu.un("beforehide", onBeforeHide);
1933 menu.un("hide", onHide);
1934 menu.un("beforeshow", onBeforeShow);
1935 menu.un("show", onShow);
1937 if(g && menu.events["checkchange"]){
1938 groups[g].remove(menu);
1939 menu.un("checkchange", onCheck);
1944 registerCheckable : function(menuItem){
1945 var g = menuItem.group;
1950 groups[g].push(menuItem);
1951 menuItem.on("beforecheckchange", onBeforeCheck);
1956 unregisterCheckable : function(menuItem){
1957 var g = menuItem.group;
1959 groups[g].remove(menuItem);
1960 menuItem.un("beforecheckchange", onBeforeCheck);
1972 * @class Roo.bootstrap.Menu
1973 * @extends Roo.bootstrap.Component
1974 * Bootstrap Menu class - container for MenuItems
1975 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1976 * @cfg {bool} hidden if the menu should be hidden when rendered.
1977 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1978 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1982 * @param {Object} config The config object
1986 Roo.bootstrap.Menu = function(config){
1987 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1988 if (this.registerMenu && this.type != 'treeview') {
1989 Roo.bootstrap.MenuMgr.register(this);
1994 * Fires before this menu is displayed
1995 * @param {Roo.menu.Menu} this
2000 * Fires before this menu is hidden
2001 * @param {Roo.menu.Menu} this
2006 * Fires after this menu is displayed
2007 * @param {Roo.menu.Menu} this
2012 * Fires after this menu is hidden
2013 * @param {Roo.menu.Menu} this
2018 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2019 * @param {Roo.menu.Menu} this
2020 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2021 * @param {Roo.EventObject} e
2026 * Fires when the mouse is hovering over this menu
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.EventObject} e
2029 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2034 * Fires when the mouse exits this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when a menu item contained in this menu is clicked
2043 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2044 * @param {Roo.EventObject} e
2048 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2051 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2055 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2058 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2060 registerMenu : true,
2062 menuItems :false, // stores the menu items..
2072 getChildContainer : function() {
2076 getAutoCreate : function(){
2078 //if (['right'].indexOf(this.align)!==-1) {
2079 // cfg.cn[1].cls += ' pull-right'
2085 cls : 'dropdown-menu' ,
2086 style : 'z-index:1000'
2090 if (this.type === 'submenu') {
2091 cfg.cls = 'submenu active';
2093 if (this.type === 'treeview') {
2094 cfg.cls = 'treeview-menu';
2099 initEvents : function() {
2101 // Roo.log("ADD event");
2102 // Roo.log(this.triggerEl.dom);
2104 this.triggerEl.on('click', this.onTriggerClick, this);
2106 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2108 this.triggerEl.addClass('dropdown-toggle');
2111 this.el.on('touchstart' , this.onTouch, this);
2113 this.el.on('click' , this.onClick, this);
2115 this.el.on("mouseover", this.onMouseOver, this);
2116 this.el.on("mouseout", this.onMouseOut, this);
2120 findTargetItem : function(e)
2122 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2126 //Roo.log(t); Roo.log(t.id);
2128 //Roo.log(this.menuitems);
2129 return this.menuitems.get(t.id);
2131 //return this.items.get(t.menuItemId);
2137 onTouch : function(e)
2139 Roo.log("menu.onTouch");
2140 //e.stopEvent(); this make the user popdown broken
2144 onClick : function(e)
2146 Roo.log("menu.onClick");
2148 var t = this.findTargetItem(e);
2149 if(!t || t.isContainer){
2154 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2155 if(t == this.activeItem && t.shouldDeactivate(e)){
2156 this.activeItem.deactivate();
2157 delete this.activeItem;
2161 this.setActiveItem(t, true);
2169 Roo.log('pass click event');
2173 this.fireEvent("click", this, t, e);
2177 if(!t.href.length || t.href == '#'){
2178 (function() { _this.hide(); }).defer(100);
2183 onMouseOver : function(e){
2184 var t = this.findTargetItem(e);
2187 // if(t.canActivate && !t.disabled){
2188 // this.setActiveItem(t, true);
2192 this.fireEvent("mouseover", this, e, t);
2194 isVisible : function(){
2195 return !this.hidden;
2197 onMouseOut : function(e){
2198 var t = this.findTargetItem(e);
2201 // if(t == this.activeItem && t.shouldDeactivate(e)){
2202 // this.activeItem.deactivate();
2203 // delete this.activeItem;
2206 this.fireEvent("mouseout", this, e, t);
2211 * Displays this menu relative to another element
2212 * @param {String/HTMLElement/Roo.Element} element The element to align to
2213 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2214 * the element (defaults to this.defaultAlign)
2215 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2217 show : function(el, pos, parentMenu){
2218 this.parentMenu = parentMenu;
2222 this.fireEvent("beforeshow", this);
2223 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2226 * Displays this menu at a specific xy position
2227 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2228 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2230 showAt : function(xy, parentMenu, /* private: */_e){
2231 this.parentMenu = parentMenu;
2236 this.fireEvent("beforeshow", this);
2237 //xy = this.el.adjustForConstraints(xy);
2241 this.hideMenuItems();
2242 this.hidden = false;
2243 this.triggerEl.addClass('open');
2245 // reassign x when hitting right
2246 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2247 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2250 // reassign y when hitting bottom
2251 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2252 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2255 // but the list may align on trigger left or trigger top... should it be a properity?
2257 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2262 this.fireEvent("show", this);
2268 this.doFocus.defer(50, this);
2272 doFocus : function(){
2274 this.focusEl.focus();
2279 * Hides this menu and optionally all parent menus
2280 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2282 hide : function(deep)
2285 this.hideMenuItems();
2286 if(this.el && this.isVisible()){
2287 this.fireEvent("beforehide", this);
2288 if(this.activeItem){
2289 this.activeItem.deactivate();
2290 this.activeItem = null;
2292 this.triggerEl.removeClass('open');;
2294 this.fireEvent("hide", this);
2296 if(deep === true && this.parentMenu){
2297 this.parentMenu.hide(true);
2301 onTriggerClick : function(e)
2303 Roo.log('trigger click');
2305 var target = e.getTarget();
2307 Roo.log(target.nodeName.toLowerCase());
2309 if(target.nodeName.toLowerCase() === 'i'){
2315 onTriggerPress : function(e)
2317 Roo.log('trigger press');
2318 //Roo.log(e.getTarget());
2319 // Roo.log(this.triggerEl.dom);
2321 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2322 var pel = Roo.get(e.getTarget());
2323 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2324 Roo.log('is treeview or dropdown?');
2328 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2332 if (this.isVisible()) {
2337 this.show(this.triggerEl, false, false);
2340 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2347 hideMenuItems : function()
2349 Roo.log("hide Menu Items");
2353 //$(backdrop).remove()
2354 this.el.select('.open',true).each(function(aa) {
2356 aa.removeClass('open');
2357 //var parent = getParent($(this))
2358 //var relatedTarget = { relatedTarget: this }
2360 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2361 //if (e.isDefaultPrevented()) return
2362 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2365 addxtypeChild : function (tree, cntr) {
2366 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2368 this.menuitems.add(comp);
2380 this.getEl().dom.innerHTML = '';
2381 this.menuitems.clear();
2395 * @class Roo.bootstrap.MenuItem
2396 * @extends Roo.bootstrap.Component
2397 * Bootstrap MenuItem class
2398 * @cfg {String} html the menu label
2399 * @cfg {String} href the link
2400 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2401 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2402 * @cfg {Boolean} active used on sidebars to highlight active itesm
2403 * @cfg {String} fa favicon to show on left of menu item.
2404 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2408 * Create a new MenuItem
2409 * @param {Object} config The config object
2413 Roo.bootstrap.MenuItem = function(config){
2414 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2419 * The raw click event for the entire grid.
2420 * @param {Roo.bootstrap.MenuItem} this
2421 * @param {Roo.EventObject} e
2427 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2431 preventDefault: false,
2432 isContainer : false,
2436 getAutoCreate : function(){
2438 if(this.isContainer){
2441 cls: 'dropdown-menu-item'
2455 if (this.fa !== false) {
2458 cls : 'fa fa-' + this.fa
2467 cls: 'dropdown-menu-item',
2470 if (this.parent().type == 'treeview') {
2471 cfg.cls = 'treeview-menu';
2474 cfg.cls += ' active';
2479 anc.href = this.href || cfg.cn[0].href ;
2480 ctag.html = this.html || cfg.cn[0].html ;
2484 initEvents: function()
2486 if (this.parent().type == 'treeview') {
2487 this.el.select('a').on('click', this.onClick, this);
2491 this.menu.parentType = this.xtype;
2492 this.menu.triggerEl = this.el;
2493 this.menu = this.addxtype(Roo.apply({}, this.menu));
2497 onClick : function(e)
2499 Roo.log('item on click ');
2501 if(this.preventDefault){
2504 //this.parent().hideMenuItems();
2506 this.fireEvent('click', this, e);
2525 * @class Roo.bootstrap.MenuSeparator
2526 * @extends Roo.bootstrap.Component
2527 * Bootstrap MenuSeparator class
2530 * Create a new MenuItem
2531 * @param {Object} config The config object
2535 Roo.bootstrap.MenuSeparator = function(config){
2536 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2539 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2541 getAutoCreate : function(){
2560 * @class Roo.bootstrap.Modal
2561 * @extends Roo.bootstrap.Component
2562 * Bootstrap Modal class
2563 * @cfg {String} title Title of dialog
2564 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2565 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2566 * @cfg {Boolean} specificTitle default false
2567 * @cfg {Array} buttons Array of buttons or standard button set..
2568 * @cfg {String} buttonPosition (left|right|center) default right
2569 * @cfg {Boolean} animate default true
2570 * @cfg {Boolean} allow_close default true
2571 * @cfg {Boolean} fitwindow default false
2572 * @cfg {String} size (sm|lg) default empty
2573 * @cfg {Number} max_width set the max width of modal
2577 * Create a new Modal Dialog
2578 * @param {Object} config The config object
2581 Roo.bootstrap.Modal = function(config){
2582 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2587 * The raw btnclick event for the button
2588 * @param {Roo.EventObject} e
2593 * Fire when dialog resize
2594 * @param {Roo.bootstrap.Modal} this
2595 * @param {Roo.EventObject} e
2599 this.buttons = this.buttons || [];
2602 this.tmpl = Roo.factory(this.tmpl);
2607 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2609 title : 'test dialog',
2619 specificTitle: false,
2621 buttonPosition: 'right',
2644 onRender : function(ct, position)
2646 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2649 var cfg = Roo.apply({}, this.getAutoCreate());
2652 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2654 //if (!cfg.name.length) {
2658 cfg.cls += ' ' + this.cls;
2661 cfg.style = this.style;
2663 this.el = Roo.get(document.body).createChild(cfg, position);
2665 //var type = this.el.dom.type;
2668 if(this.tabIndex !== undefined){
2669 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2672 this.dialogEl = this.el.select('.modal-dialog',true).first();
2673 this.bodyEl = this.el.select('.modal-body',true).first();
2674 this.closeEl = this.el.select('.modal-header .close', true).first();
2675 this.headerEl = this.el.select('.modal-header',true).first();
2676 this.titleEl = this.el.select('.modal-title',true).first();
2677 this.footerEl = this.el.select('.modal-footer',true).first();
2679 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2681 //this.el.addClass("x-dlg-modal");
2683 if (this.buttons.length) {
2684 Roo.each(this.buttons, function(bb) {
2685 var b = Roo.apply({}, bb);
2686 b.xns = b.xns || Roo.bootstrap;
2687 b.xtype = b.xtype || 'Button';
2688 if (typeof(b.listeners) == 'undefined') {
2689 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2692 var btn = Roo.factory(b);
2694 btn.render(this.el.select('.modal-footer div').first());
2698 // render the children.
2701 if(typeof(this.items) != 'undefined'){
2702 var items = this.items;
2705 for(var i =0;i < items.length;i++) {
2706 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2710 this.items = nitems;
2712 // where are these used - they used to be body/close/footer
2716 //this.el.addClass([this.fieldClass, this.cls]);
2720 getAutoCreate : function()
2724 html : this.html || ''
2729 cls : 'modal-title',
2733 if(this.specificTitle){
2739 if (this.allow_close) {
2751 if(this.size.length){
2752 size = 'modal-' + this.size;
2759 cls: "modal-dialog " + size,
2762 cls : "modal-content",
2765 cls : 'modal-header',
2770 cls : 'modal-footer',
2774 cls: 'btn-' + this.buttonPosition
2791 modal.cls += ' fade';
2797 getChildContainer : function() {
2802 getButtonContainer : function() {
2803 return this.el.select('.modal-footer div',true).first();
2806 initEvents : function()
2808 if (this.allow_close) {
2809 this.closeEl.on('click', this.hide, this);
2811 Roo.EventManager.onWindowResize(this.resize, this, true);
2818 this.maskEl.setSize(
2819 Roo.lib.Dom.getViewWidth(true),
2820 Roo.lib.Dom.getViewHeight(true)
2823 if (this.fitwindow) {
2825 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2826 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2831 if(this.max_width !== 0) {
2833 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2836 this.setSize(w, this.height);
2840 if(this.max_height) {
2841 this.setSize(w,Math.min(
2843 Roo.lib.Dom.getViewportHeight(true) - 60
2849 if(!this.fit_content) {
2850 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2854 this.setSize(w, Math.min(
2856 this.headerEl.getHeight() +
2857 this.footerEl.getHeight() +
2858 this.getChildHeight(this.bodyEl.dom.childNodes),
2859 Roo.lib.Dom.getViewportHeight(true) - 60)
2865 setSize : function(w,h)
2876 if (!this.rendered) {
2880 //this.el.setStyle('display', 'block');
2881 this.el.removeClass('hideing');
2882 this.el.addClass('show');
2884 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2887 this.el.addClass('in');
2890 this.el.addClass('in');
2893 // not sure how we can show data in here..
2895 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2898 Roo.get(document.body).addClass("x-body-masked");
2900 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2901 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2902 this.maskEl.addClass('show');
2906 this.fireEvent('show', this);
2908 // set zindex here - otherwise it appears to be ignored...
2909 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2912 this.items.forEach( function(e) {
2913 e.layout ? e.layout() : false;
2921 if(this.fireEvent("beforehide", this) !== false){
2922 this.maskEl.removeClass('show');
2923 Roo.get(document.body).removeClass("x-body-masked");
2924 this.el.removeClass('in');
2925 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2927 if(this.animate){ // why
2928 this.el.addClass('hideing');
2930 if (!this.el.hasClass('hideing')) {
2931 return; // it's been shown again...
2933 this.el.removeClass('show');
2934 this.el.removeClass('hideing');
2938 this.el.removeClass('show');
2940 this.fireEvent('hide', this);
2943 isVisible : function()
2946 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2950 addButton : function(str, cb)
2954 var b = Roo.apply({}, { html : str } );
2955 b.xns = b.xns || Roo.bootstrap;
2956 b.xtype = b.xtype || 'Button';
2957 if (typeof(b.listeners) == 'undefined') {
2958 b.listeners = { click : cb.createDelegate(this) };
2961 var btn = Roo.factory(b);
2963 btn.render(this.el.select('.modal-footer div').first());
2969 setDefaultButton : function(btn)
2971 //this.el.select('.modal-footer').()
2975 resizeTo: function(w,h)
2979 this.dialogEl.setWidth(w);
2980 if (this.diff === false) {
2981 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2984 this.bodyEl.setHeight(h - this.diff);
2986 this.fireEvent('resize', this);
2989 setContentSize : function(w, h)
2993 onButtonClick: function(btn,e)
2996 this.fireEvent('btnclick', btn.name, e);
2999 * Set the title of the Dialog
3000 * @param {String} str new Title
3002 setTitle: function(str) {
3003 this.titleEl.dom.innerHTML = str;
3006 * Set the body of the Dialog
3007 * @param {String} str new Title
3009 setBody: function(str) {
3010 this.bodyEl.dom.innerHTML = str;
3013 * Set the body of the Dialog using the template
3014 * @param {Obj} data - apply this data to the template and replace the body contents.
3016 applyBody: function(obj)
3019 Roo.log("Error - using apply Body without a template");
3022 this.tmpl.overwrite(this.bodyEl, obj);
3025 getChildHeight : function(child_nodes)
3029 child_nodes.length == 0
3034 var child_height = 0;
3036 for(var i = 0; i < child_nodes.length; i++) {
3039 * for modal with tabs...
3040 if(child_nodes[i].classList.contains('roo-layout-panel')) {
3042 var layout_childs = child_nodes[i].childNodes;
3044 for(var j = 0; j < layout_childs.length; j++) {
3046 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3048 var layout_body_childs = layout_childs[j].childNodes;
3050 for(var k = 0; k < layout_body_childs.length; k++) {
3052 if(layout_body_childs[k].classList.contains('navbar')) {
3053 child_height += layout_body_childs[k].offsetHeight;
3057 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3059 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3061 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3063 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3064 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3079 child_height += child_nodes[i].offsetHeight;
3080 // Roo.log(child_nodes[i].offsetHeight);
3083 return child_height;
3089 Roo.apply(Roo.bootstrap.Modal, {
3091 * Button config that displays a single OK button
3100 * Button config that displays Yes and No buttons
3116 * Button config that displays OK and Cancel buttons
3131 * Button config that displays Yes, No and Cancel buttons
3155 * messagebox - can be used as a replace
3159 * @class Roo.MessageBox
3160 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3164 Roo.Msg.alert('Status', 'Changes saved successfully.');
3166 // Prompt for user data:
3167 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3169 // process text value...
3173 // Show a dialog using config options:
3175 title:'Save Changes?',
3176 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3177 buttons: Roo.Msg.YESNOCANCEL,
3184 Roo.bootstrap.MessageBox = function(){
3185 var dlg, opt, mask, waitTimer;
3186 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3187 var buttons, activeTextEl, bwidth;
3191 var handleButton = function(button){
3193 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3197 var handleHide = function(){
3199 dlg.el.removeClass(opt.cls);
3202 // Roo.TaskMgr.stop(waitTimer);
3203 // waitTimer = null;
3208 var updateButtons = function(b){
3211 buttons["ok"].hide();
3212 buttons["cancel"].hide();
3213 buttons["yes"].hide();
3214 buttons["no"].hide();
3215 //dlg.footer.dom.style.display = 'none';
3218 dlg.footerEl.dom.style.display = '';
3219 for(var k in buttons){
3220 if(typeof buttons[k] != "function"){
3223 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3224 width += buttons[k].el.getWidth()+15;
3234 var handleEsc = function(d, k, e){
3235 if(opt && opt.closable !== false){
3245 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3246 * @return {Roo.BasicDialog} The BasicDialog element
3248 getDialog : function(){
3250 dlg = new Roo.bootstrap.Modal( {
3253 //constraintoviewport:false,
3255 //collapsible : false,
3260 //buttonAlign:"center",
3261 closeClick : function(){
3262 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3265 handleButton("cancel");
3270 dlg.on("hide", handleHide);
3272 //dlg.addKeyListener(27, handleEsc);
3274 this.buttons = buttons;
3275 var bt = this.buttonText;
3276 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3277 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3278 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3279 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3281 bodyEl = dlg.bodyEl.createChild({
3283 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3284 '<textarea class="roo-mb-textarea"></textarea>' +
3285 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3287 msgEl = bodyEl.dom.firstChild;
3288 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3289 textboxEl.enableDisplayMode();
3290 textboxEl.addKeyListener([10,13], function(){
3291 if(dlg.isVisible() && opt && opt.buttons){
3294 }else if(opt.buttons.yes){
3295 handleButton("yes");
3299 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3300 textareaEl.enableDisplayMode();
3301 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3302 progressEl.enableDisplayMode();
3304 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3305 var pf = progressEl.dom.firstChild;
3307 pp = Roo.get(pf.firstChild);
3308 pp.setHeight(pf.offsetHeight);
3316 * Updates the message box body text
3317 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3318 * the XHTML-compliant non-breaking space character '&#160;')
3319 * @return {Roo.MessageBox} This message box
3321 updateText : function(text)
3323 if(!dlg.isVisible() && !opt.width){
3324 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3325 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3327 msgEl.innerHTML = text || ' ';
3329 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3330 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3332 Math.min(opt.width || cw , this.maxWidth),
3333 Math.max(opt.minWidth || this.minWidth, bwidth)
3336 activeTextEl.setWidth(w);
3338 if(dlg.isVisible()){
3339 dlg.fixedcenter = false;
3341 // to big, make it scroll. = But as usual stupid IE does not support
3344 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3345 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3346 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3348 bodyEl.dom.style.height = '';
3349 bodyEl.dom.style.overflowY = '';
3352 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3354 bodyEl.dom.style.overflowX = '';
3357 dlg.setContentSize(w, bodyEl.getHeight());
3358 if(dlg.isVisible()){
3359 dlg.fixedcenter = true;
3365 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3366 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3367 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3368 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3369 * @return {Roo.MessageBox} This message box
3371 updateProgress : function(value, text){
3373 this.updateText(text);
3376 if (pp) { // weird bug on my firefox - for some reason this is not defined
3377 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3378 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3384 * Returns true if the message box is currently displayed
3385 * @return {Boolean} True if the message box is visible, else false
3387 isVisible : function(){
3388 return dlg && dlg.isVisible();
3392 * Hides the message box if it is displayed
3395 if(this.isVisible()){
3401 * Displays a new message box, or reinitializes an existing message box, based on the config options
3402 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3403 * The following config object properties are supported:
3405 Property Type Description
3406 ---------- --------------- ------------------------------------------------------------------------------------
3407 animEl String/Element An id or Element from which the message box should animate as it opens and
3408 closes (defaults to undefined)
3409 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3410 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3411 closable Boolean False to hide the top-right close button (defaults to true). Note that
3412 progress and wait dialogs will ignore this property and always hide the
3413 close button as they can only be closed programmatically.
3414 cls String A custom CSS class to apply to the message box element
3415 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3416 displayed (defaults to 75)
3417 fn Function A callback function to execute after closing the dialog. The arguments to the
3418 function will be btn (the name of the button that was clicked, if applicable,
3419 e.g. "ok"), and text (the value of the active text field, if applicable).
3420 Progress and wait dialogs will ignore this option since they do not respond to
3421 user actions and can only be closed programmatically, so any required function
3422 should be called by the same code after it closes the dialog.
3423 icon String A CSS class that provides a background image to be used as an icon for
3424 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3425 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3426 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3427 modal Boolean False to allow user interaction with the page while the message box is
3428 displayed (defaults to true)
3429 msg String A string that will replace the existing message box body text (defaults
3430 to the XHTML-compliant non-breaking space character ' ')
3431 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3432 progress Boolean True to display a progress bar (defaults to false)
3433 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3434 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3435 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3436 title String The title text
3437 value String The string value to set into the active textbox element if displayed
3438 wait Boolean True to display a progress bar (defaults to false)
3439 width Number The width of the dialog in pixels
3446 msg: 'Please enter your address:',
3448 buttons: Roo.MessageBox.OKCANCEL,
3451 animEl: 'addAddressBtn'
3454 * @param {Object} config Configuration options
3455 * @return {Roo.MessageBox} This message box
3457 show : function(options)
3460 // this causes nightmares if you show one dialog after another
3461 // especially on callbacks..
3463 if(this.isVisible()){
3466 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3467 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3468 Roo.log("New Dialog Message:" + options.msg )
3469 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3470 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3473 var d = this.getDialog();
3475 d.setTitle(opt.title || " ");
3476 d.closeEl.setDisplayed(opt.closable !== false);
3477 activeTextEl = textboxEl;
3478 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3483 textareaEl.setHeight(typeof opt.multiline == "number" ?
3484 opt.multiline : this.defaultTextHeight);
3485 activeTextEl = textareaEl;
3494 progressEl.setDisplayed(opt.progress === true);
3495 this.updateProgress(0);
3496 activeTextEl.dom.value = opt.value || "";
3498 dlg.setDefaultButton(activeTextEl);
3500 var bs = opt.buttons;
3504 }else if(bs && bs.yes){
3505 db = buttons["yes"];
3507 dlg.setDefaultButton(db);
3509 bwidth = updateButtons(opt.buttons);
3510 this.updateText(opt.msg);
3512 d.el.addClass(opt.cls);
3514 d.proxyDrag = opt.proxyDrag === true;
3515 d.modal = opt.modal !== false;
3516 d.mask = opt.modal !== false ? mask : false;
3518 // force it to the end of the z-index stack so it gets a cursor in FF
3519 document.body.appendChild(dlg.el.dom);
3520 d.animateTarget = null;
3521 d.show(options.animEl);
3527 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3528 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3529 * and closing the message box when the process is complete.
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @return {Roo.MessageBox} This message box
3534 progress : function(title, msg){
3541 minWidth: this.minProgressWidth,
3548 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3549 * If a callback function is passed it will be called after the user clicks the button, and the
3550 * id of the button that was clicked will be passed as the only parameter to the callback
3551 * (could also be the top-right close button).
3552 * @param {String} title The title bar text
3553 * @param {String} msg The message box body text
3554 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3555 * @param {Object} scope (optional) The scope of the callback function
3556 * @return {Roo.MessageBox} This message box
3558 alert : function(title, msg, fn, scope)
3573 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3574 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3575 * You are responsible for closing the message box when the process is complete.
3576 * @param {String} msg The message box body text
3577 * @param {String} title (optional) The title bar text
3578 * @return {Roo.MessageBox} This message box
3580 wait : function(msg, title){
3591 waitTimer = Roo.TaskMgr.start({
3593 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3601 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3602 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3603 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3604 * @param {String} title The title bar text
3605 * @param {String} msg The message box body text
3606 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3607 * @param {Object} scope (optional) The scope of the callback function
3608 * @return {Roo.MessageBox} This message box
3610 confirm : function(title, msg, fn, scope){
3614 buttons: this.YESNO,
3623 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3624 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3625 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3626 * (could also be the top-right close button) and the text that was entered will be passed as the two
3627 * parameters to the callback.
3628 * @param {String} title The title bar text
3629 * @param {String} msg The message box body text
3630 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3631 * @param {Object} scope (optional) The scope of the callback function
3632 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3633 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3634 * @return {Roo.MessageBox} This message box
3636 prompt : function(title, msg, fn, scope, multiline){
3640 buttons: this.OKCANCEL,
3645 multiline: multiline,
3652 * Button config that displays a single OK button
3657 * Button config that displays Yes and No buttons
3660 YESNO : {yes:true, no:true},
3662 * Button config that displays OK and Cancel buttons
3665 OKCANCEL : {ok:true, cancel:true},
3667 * Button config that displays Yes, No and Cancel buttons
3670 YESNOCANCEL : {yes:true, no:true, cancel:true},
3673 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3676 defaultTextHeight : 75,
3678 * The maximum width in pixels of the message box (defaults to 600)
3683 * The minimum width in pixels of the message box (defaults to 100)
3688 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3689 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3692 minProgressWidth : 250,
3694 * An object containing the default button text strings that can be overriden for localized language support.
3695 * Supported properties are: ok, cancel, yes and no.
3696 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3709 * Shorthand for {@link Roo.MessageBox}
3711 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3712 Roo.Msg = Roo.Msg || Roo.MessageBox;
3721 * @class Roo.bootstrap.Navbar
3722 * @extends Roo.bootstrap.Component
3723 * Bootstrap Navbar class
3726 * Create a new Navbar
3727 * @param {Object} config The config object
3731 Roo.bootstrap.Navbar = function(config){
3732 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3736 * @event beforetoggle
3737 * Fire before toggle the menu
3738 * @param {Roo.EventObject} e
3740 "beforetoggle" : true
3744 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3753 getAutoCreate : function(){
3756 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3760 initEvents :function ()
3762 //Roo.log(this.el.select('.navbar-toggle',true));
3763 this.el.select('.navbar-toggle',true).on('click', function() {
3764 if(this.fireEvent('beforetoggle', this) !== false){
3765 this.el.select('.navbar-collapse',true).toggleClass('in');
3775 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3777 var size = this.el.getSize();
3778 this.maskEl.setSize(size.width, size.height);
3779 this.maskEl.enableDisplayMode("block");
3788 getChildContainer : function()
3790 if (this.el.select('.collapse').getCount()) {
3791 return this.el.select('.collapse',true).first();
3824 * @class Roo.bootstrap.NavSimplebar
3825 * @extends Roo.bootstrap.Navbar
3826 * Bootstrap Sidebar class
3828 * @cfg {Boolean} inverse is inverted color
3830 * @cfg {String} type (nav | pills | tabs)
3831 * @cfg {Boolean} arrangement stacked | justified
3832 * @cfg {String} align (left | right) alignment
3834 * @cfg {Boolean} main (true|false) main nav bar? default false
3835 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3837 * @cfg {String} tag (header|footer|nav|div) default is nav
3843 * Create a new Sidebar
3844 * @param {Object} config The config object
3848 Roo.bootstrap.NavSimplebar = function(config){
3849 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3852 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3868 getAutoCreate : function(){
3872 tag : this.tag || 'div',
3885 this.type = this.type || 'nav';
3886 if (['tabs','pills'].indexOf(this.type)!==-1) {
3887 cfg.cn[0].cls += ' nav-' + this.type
3891 if (this.type!=='nav') {
3892 Roo.log('nav type must be nav/tabs/pills')
3894 cfg.cn[0].cls += ' navbar-nav'
3900 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3901 cfg.cn[0].cls += ' nav-' + this.arrangement;
3905 if (this.align === 'right') {
3906 cfg.cn[0].cls += ' navbar-right';
3910 cfg.cls += ' navbar-inverse';
3937 * @class Roo.bootstrap.NavHeaderbar
3938 * @extends Roo.bootstrap.NavSimplebar
3939 * Bootstrap Sidebar class
3941 * @cfg {String} brand what is brand
3942 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3943 * @cfg {String} brand_href href of the brand
3944 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3945 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3946 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3947 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3950 * Create a new Sidebar
3951 * @param {Object} config The config object
3955 Roo.bootstrap.NavHeaderbar = function(config){
3956 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3960 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3967 desktopCenter : false,
3970 getAutoCreate : function(){
3973 tag: this.nav || 'nav',
3980 if (this.desktopCenter) {
3981 cn.push({cls : 'container', cn : []});
3988 cls: 'navbar-header',
3993 cls: 'navbar-toggle',
3994 'data-toggle': 'collapse',
3999 html: 'Toggle navigation'
4021 cls: 'collapse navbar-collapse',
4025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
4027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4028 cfg.cls += ' navbar-' + this.position;
4030 // tag can override this..
4032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
4035 if (this.brand !== '') {
4038 href: this.brand_href ? this.brand_href : '#',
4039 cls: 'navbar-brand',
4047 cfg.cls += ' main-nav';
4055 getHeaderChildContainer : function()
4057 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4058 return this.el.select('.navbar-header',true).first();
4061 return this.getChildContainer();
4065 initEvents : function()
4067 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4069 if (this.autohide) {
4074 Roo.get(document).on('scroll',function(e) {
4075 var ns = Roo.get(document).getScroll().top;
4076 var os = prevScroll;
4080 ft.removeClass('slideDown');
4081 ft.addClass('slideUp');
4084 ft.removeClass('slideUp');
4085 ft.addClass('slideDown');
4106 * @class Roo.bootstrap.NavSidebar
4107 * @extends Roo.bootstrap.Navbar
4108 * Bootstrap Sidebar class
4111 * Create a new Sidebar
4112 * @param {Object} config The config object
4116 Roo.bootstrap.NavSidebar = function(config){
4117 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4120 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4122 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4124 getAutoCreate : function(){
4129 cls: 'sidebar sidebar-nav'
4151 * @class Roo.bootstrap.NavGroup
4152 * @extends Roo.bootstrap.Component
4153 * Bootstrap NavGroup class
4154 * @cfg {String} align (left|right)
4155 * @cfg {Boolean} inverse
4156 * @cfg {String} type (nav|pills|tab) default nav
4157 * @cfg {String} navId - reference Id for navbar.
4161 * Create a new nav group
4162 * @param {Object} config The config object
4165 Roo.bootstrap.NavGroup = function(config){
4166 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4169 Roo.bootstrap.NavGroup.register(this);
4173 * Fires when the active item changes
4174 * @param {Roo.bootstrap.NavGroup} this
4175 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4176 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4183 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4194 getAutoCreate : function()
4196 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4203 if (['tabs','pills'].indexOf(this.type)!==-1) {
4204 cfg.cls += ' nav-' + this.type
4206 if (this.type!=='nav') {
4207 Roo.log('nav type must be nav/tabs/pills')
4209 cfg.cls += ' navbar-nav'
4212 if (this.parent() && this.parent().sidebar) {
4215 cls: 'dashboard-menu sidebar-menu'
4221 if (this.form === true) {
4227 if (this.align === 'right') {
4228 cfg.cls += ' navbar-right';
4230 cfg.cls += ' navbar-left';
4234 if (this.align === 'right') {
4235 cfg.cls += ' navbar-right';
4239 cfg.cls += ' navbar-inverse';
4247 * sets the active Navigation item
4248 * @param {Roo.bootstrap.NavItem} the new current navitem
4250 setActiveItem : function(item)
4253 Roo.each(this.navItems, function(v){
4258 v.setActive(false, true);
4265 item.setActive(true, true);
4266 this.fireEvent('changed', this, item, prev);
4271 * gets the active Navigation item
4272 * @return {Roo.bootstrap.NavItem} the current navitem
4274 getActive : function()
4278 Roo.each(this.navItems, function(v){
4289 indexOfNav : function()
4293 Roo.each(this.navItems, function(v,i){
4304 * adds a Navigation item
4305 * @param {Roo.bootstrap.NavItem} the navitem to add
4307 addItem : function(cfg)
4309 var cn = new Roo.bootstrap.NavItem(cfg);
4311 cn.parentId = this.id;
4312 cn.onRender(this.el, null);
4316 * register a Navigation item
4317 * @param {Roo.bootstrap.NavItem} the navitem to add
4319 register : function(item)
4321 this.navItems.push( item);
4322 item.navId = this.navId;
4327 * clear all the Navigation item
4330 clearAll : function()
4333 this.el.dom.innerHTML = '';
4336 getNavItem: function(tabId)
4339 Roo.each(this.navItems, function(e) {
4340 if (e.tabId == tabId) {
4350 setActiveNext : function()
4352 var i = this.indexOfNav(this.getActive());
4353 if (i > this.navItems.length) {
4356 this.setActiveItem(this.navItems[i+1]);
4358 setActivePrev : function()
4360 var i = this.indexOfNav(this.getActive());
4364 this.setActiveItem(this.navItems[i-1]);
4366 clearWasActive : function(except) {
4367 Roo.each(this.navItems, function(e) {
4368 if (e.tabId != except.tabId && e.was_active) {
4369 e.was_active = false;
4376 getWasActive : function ()
4379 Roo.each(this.navItems, function(e) {
4394 Roo.apply(Roo.bootstrap.NavGroup, {
4398 * register a Navigation Group
4399 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4401 register : function(navgrp)
4403 this.groups[navgrp.navId] = navgrp;
4407 * fetch a Navigation Group based on the navigation ID
4408 * @param {string} the navgroup to add
4409 * @returns {Roo.bootstrap.NavGroup} the navgroup
4411 get: function(navId) {
4412 if (typeof(this.groups[navId]) == 'undefined') {
4414 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4416 return this.groups[navId] ;
4431 * @class Roo.bootstrap.NavItem
4432 * @extends Roo.bootstrap.Component
4433 * Bootstrap Navbar.NavItem class
4434 * @cfg {String} href link to
4435 * @cfg {String} html content of button
4436 * @cfg {String} badge text inside badge
4437 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4438 * @cfg {String} glyphicon name of glyphicon
4439 * @cfg {String} icon name of font awesome icon
4440 * @cfg {Boolean} active Is item active
4441 * @cfg {Boolean} disabled Is item disabled
4443 * @cfg {Boolean} preventDefault (true | false) default false
4444 * @cfg {String} tabId the tab that this item activates.
4445 * @cfg {String} tagtype (a|span) render as a href or span?
4446 * @cfg {Boolean} animateRef (true|false) link to element default false
4449 * Create a new Navbar Item
4450 * @param {Object} config The config object
4452 Roo.bootstrap.NavItem = function(config){
4453 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4458 * The raw click event for the entire grid.
4459 * @param {Roo.EventObject} e
4464 * Fires when the active item active state changes
4465 * @param {Roo.bootstrap.NavItem} this
4466 * @param {boolean} state the new state
4472 * Fires when scroll to element
4473 * @param {Roo.bootstrap.NavItem} this
4474 * @param {Object} options
4475 * @param {Roo.EventObject} e
4483 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4491 preventDefault : false,
4498 getAutoCreate : function(){
4507 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4509 if (this.disabled) {
4510 cfg.cls += ' disabled';
4513 if (this.href || this.html || this.glyphicon || this.icon) {
4517 href : this.href || "#",
4518 html: this.html || ''
4523 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4526 if(this.glyphicon) {
4527 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4532 cfg.cn[0].html += " <span class='caret'></span>";
4536 if (this.badge !== '') {
4538 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4546 initEvents: function()
4548 if (typeof (this.menu) != 'undefined') {
4549 this.menu.parentType = this.xtype;
4550 this.menu.triggerEl = this.el;
4551 this.menu = this.addxtype(Roo.apply({}, this.menu));
4554 this.el.select('a',true).on('click', this.onClick, this);
4556 if(this.tagtype == 'span'){
4557 this.el.select('span',true).on('click', this.onClick, this);
4560 // at this point parent should be available..
4561 this.parent().register(this);
4564 onClick : function(e)
4566 if (e.getTarget('.dropdown-menu-item')) {
4567 // did you click on a menu itemm.... - then don't trigger onclick..
4572 this.preventDefault ||
4575 Roo.log("NavItem - prevent Default?");
4579 if (this.disabled) {
4583 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4584 if (tg && tg.transition) {
4585 Roo.log("waiting for the transitionend");
4591 //Roo.log("fire event clicked");
4592 if(this.fireEvent('click', this, e) === false){
4596 if(this.tagtype == 'span'){
4600 //Roo.log(this.href);
4601 var ael = this.el.select('a',true).first();
4604 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4605 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4606 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4607 return; // ignore... - it's a 'hash' to another page.
4609 Roo.log("NavItem - prevent Default?");
4611 this.scrollToElement(e);
4615 var p = this.parent();
4617 if (['tabs','pills'].indexOf(p.type)!==-1) {
4618 if (typeof(p.setActiveItem) !== 'undefined') {
4619 p.setActiveItem(this);
4623 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4624 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4625 // remove the collapsed menu expand...
4626 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4630 isActive: function () {
4633 setActive : function(state, fire, is_was_active)
4635 if (this.active && !state && this.navId) {
4636 this.was_active = true;
4637 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4639 nv.clearWasActive(this);
4643 this.active = state;
4646 this.el.removeClass('active');
4647 } else if (!this.el.hasClass('active')) {
4648 this.el.addClass('active');
4651 this.fireEvent('changed', this, state);
4654 // show a panel if it's registered and related..
4656 if (!this.navId || !this.tabId || !state || is_was_active) {
4660 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4664 var pan = tg.getPanelByName(this.tabId);
4668 // if we can not flip to new panel - go back to old nav highlight..
4669 if (false == tg.showPanel(pan)) {
4670 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4672 var onav = nv.getWasActive();
4674 onav.setActive(true, false, true);
4683 // this should not be here...
4684 setDisabled : function(state)
4686 this.disabled = state;
4688 this.el.removeClass('disabled');
4689 } else if (!this.el.hasClass('disabled')) {
4690 this.el.addClass('disabled');
4696 * Fetch the element to display the tooltip on.
4697 * @return {Roo.Element} defaults to this.el
4699 tooltipEl : function()
4701 return this.el.select('' + this.tagtype + '', true).first();
4704 scrollToElement : function(e)
4706 var c = document.body;
4709 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4711 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4712 c = document.documentElement;
4715 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4721 var o = target.calcOffsetsTo(c);
4728 this.fireEvent('scrollto', this, options, e);
4730 Roo.get(c).scrollTo('top', options.value, true);
4743 * <span> icon </span>
4744 * <span> text </span>
4745 * <span>badge </span>
4749 * @class Roo.bootstrap.NavSidebarItem
4750 * @extends Roo.bootstrap.NavItem
4751 * Bootstrap Navbar.NavSidebarItem class
4752 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4753 * {Boolean} open is the menu open
4754 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4755 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4756 * {String} buttonSize (sm|md|lg)the extra classes for the button
4757 * {Boolean} showArrow show arrow next to the text (default true)
4759 * Create a new Navbar Button
4760 * @param {Object} config The config object
4762 Roo.bootstrap.NavSidebarItem = function(config){
4763 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4768 * The raw click event for the entire grid.
4769 * @param {Roo.EventObject} e
4774 * Fires when the active item active state changes
4775 * @param {Roo.bootstrap.NavSidebarItem} this
4776 * @param {boolean} state the new state
4784 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4786 badgeWeight : 'default',
4792 buttonWeight : 'default',
4798 getAutoCreate : function(){
4803 href : this.href || '#',
4809 if(this.buttonView){
4812 href : this.href || '#',
4813 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4826 cfg.cls += ' active';
4829 if (this.disabled) {
4830 cfg.cls += ' disabled';
4833 cfg.cls += ' open x-open';
4836 if (this.glyphicon || this.icon) {
4837 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4838 a.cn.push({ tag : 'i', cls : c }) ;
4841 if(!this.buttonView){
4844 html : this.html || ''
4851 if (this.badge !== '') {
4852 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4858 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4861 a.cls += ' dropdown-toggle treeview' ;
4867 initEvents : function()
4869 if (typeof (this.menu) != 'undefined') {
4870 this.menu.parentType = this.xtype;
4871 this.menu.triggerEl = this.el;
4872 this.menu = this.addxtype(Roo.apply({}, this.menu));
4875 this.el.on('click', this.onClick, this);
4877 if(this.badge !== ''){
4878 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4883 onClick : function(e)
4890 if(this.preventDefault){
4894 this.fireEvent('click', this);
4897 disable : function()
4899 this.setDisabled(true);
4904 this.setDisabled(false);
4907 setDisabled : function(state)
4909 if(this.disabled == state){
4913 this.disabled = state;
4916 this.el.addClass('disabled');
4920 this.el.removeClass('disabled');
4925 setActive : function(state)
4927 if(this.active == state){
4931 this.active = state;
4934 this.el.addClass('active');
4938 this.el.removeClass('active');
4943 isActive: function ()
4948 setBadge : function(str)
4954 this.badgeEl.dom.innerHTML = str;
4971 * @class Roo.bootstrap.Row
4972 * @extends Roo.bootstrap.Component
4973 * Bootstrap Row class (contains columns...)
4977 * @param {Object} config The config object
4980 Roo.bootstrap.Row = function(config){
4981 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4984 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4986 getAutoCreate : function(){
5005 * @class Roo.bootstrap.Element
5006 * @extends Roo.bootstrap.Component
5007 * Bootstrap Element class
5008 * @cfg {String} html contents of the element
5009 * @cfg {String} tag tag of the element
5010 * @cfg {String} cls class of the element
5011 * @cfg {Boolean} preventDefault (true|false) default false
5012 * @cfg {Boolean} clickable (true|false) default false
5015 * Create a new Element
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Element = function(config){
5020 Roo.bootstrap.Element.superclass.constructor.call(this, config);
5026 * When a element is chick
5027 * @param {Roo.bootstrap.Element} this
5028 * @param {Roo.EventObject} e
5034 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
5039 preventDefault: false,
5042 getAutoCreate : function(){
5046 // cls: this.cls, double assign in parent class Component.js :: onRender
5053 initEvents: function()
5055 Roo.bootstrap.Element.superclass.initEvents.call(this);
5058 this.el.on('click', this.onClick, this);
5063 onClick : function(e)
5065 if(this.preventDefault){
5069 this.fireEvent('click', this, e);
5072 getValue : function()
5074 return this.el.dom.innerHTML;
5077 setValue : function(value)
5079 this.el.dom.innerHTML = value;
5094 * @class Roo.bootstrap.Pagination
5095 * @extends Roo.bootstrap.Component
5096 * Bootstrap Pagination class
5097 * @cfg {String} size xs | sm | md | lg
5098 * @cfg {Boolean} inverse false | true
5101 * Create a new Pagination
5102 * @param {Object} config The config object
5105 Roo.bootstrap.Pagination = function(config){
5106 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5109 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5115 getAutoCreate : function(){
5121 cfg.cls += ' inverse';
5127 cfg.cls += " " + this.cls;
5145 * @class Roo.bootstrap.PaginationItem
5146 * @extends Roo.bootstrap.Component
5147 * Bootstrap PaginationItem class
5148 * @cfg {String} html text
5149 * @cfg {String} href the link
5150 * @cfg {Boolean} preventDefault (true | false) default true
5151 * @cfg {Boolean} active (true | false) default false
5152 * @cfg {Boolean} disabled default false
5156 * Create a new PaginationItem
5157 * @param {Object} config The config object
5161 Roo.bootstrap.PaginationItem = function(config){
5162 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5167 * The raw click event for the entire grid.
5168 * @param {Roo.EventObject} e
5174 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5178 preventDefault: true,
5183 getAutoCreate : function(){
5189 href : this.href ? this.href : '#',
5190 html : this.html ? this.html : ''
5200 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5204 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5210 initEvents: function() {
5212 this.el.on('click', this.onClick, this);
5215 onClick : function(e)
5217 Roo.log('PaginationItem on click ');
5218 if(this.preventDefault){
5226 this.fireEvent('click', this, e);
5242 * @class Roo.bootstrap.Slider
5243 * @extends Roo.bootstrap.Component
5244 * Bootstrap Slider class
5247 * Create a new Slider
5248 * @param {Object} config The config object
5251 Roo.bootstrap.Slider = function(config){
5252 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5255 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5257 getAutoCreate : function(){
5261 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5265 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5277 * Ext JS Library 1.1.1
5278 * Copyright(c) 2006-2007, Ext JS, LLC.
5280 * Originally Released Under LGPL - original licence link has changed is not relivant.
5283 * <script type="text/javascript">
5288 * @class Roo.grid.ColumnModel
5289 * @extends Roo.util.Observable
5290 * This is the default implementation of a ColumnModel used by the Grid. It defines
5291 * the columns in the grid.
5294 var colModel = new Roo.grid.ColumnModel([
5295 {header: "Ticker", width: 60, sortable: true, locked: true},
5296 {header: "Company Name", width: 150, sortable: true},
5297 {header: "Market Cap.", width: 100, sortable: true},
5298 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5299 {header: "Employees", width: 100, sortable: true, resizable: false}
5304 * The config options listed for this class are options which may appear in each
5305 * individual column definition.
5306 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5308 * @param {Object} config An Array of column config objects. See this class's
5309 * config objects for details.
5311 Roo.grid.ColumnModel = function(config){
5313 * The config passed into the constructor
5315 this.config = config;
5318 // if no id, create one
5319 // if the column does not have a dataIndex mapping,
5320 // map it to the order it is in the config
5321 for(var i = 0, len = config.length; i < len; i++){
5323 if(typeof c.dataIndex == "undefined"){
5326 if(typeof c.renderer == "string"){
5327 c.renderer = Roo.util.Format[c.renderer];
5329 if(typeof c.id == "undefined"){
5332 if(c.editor && c.editor.xtype){
5333 c.editor = Roo.factory(c.editor, Roo.grid);
5335 if(c.editor && c.editor.isFormField){
5336 c.editor = new Roo.grid.GridEditor(c.editor);
5338 this.lookup[c.id] = c;
5342 * The width of columns which have no width specified (defaults to 100)
5345 this.defaultWidth = 100;
5348 * Default sortable of columns which have no sortable specified (defaults to false)
5351 this.defaultSortable = false;
5355 * @event widthchange
5356 * Fires when the width of a column changes.
5357 * @param {ColumnModel} this
5358 * @param {Number} columnIndex The column index
5359 * @param {Number} newWidth The new width
5361 "widthchange": true,
5363 * @event headerchange
5364 * Fires when the text of a header changes.
5365 * @param {ColumnModel} this
5366 * @param {Number} columnIndex The column index
5367 * @param {Number} newText The new header text
5369 "headerchange": true,
5371 * @event hiddenchange
5372 * Fires when a column is hidden or "unhidden".
5373 * @param {ColumnModel} this
5374 * @param {Number} columnIndex The column index
5375 * @param {Boolean} hidden true if hidden, false otherwise
5377 "hiddenchange": true,
5379 * @event columnmoved
5380 * Fires when a column is moved.
5381 * @param {ColumnModel} this
5382 * @param {Number} oldIndex
5383 * @param {Number} newIndex
5385 "columnmoved" : true,
5387 * @event columlockchange
5388 * Fires when a column's locked state is changed
5389 * @param {ColumnModel} this
5390 * @param {Number} colIndex
5391 * @param {Boolean} locked true if locked
5393 "columnlockchange" : true
5395 Roo.grid.ColumnModel.superclass.constructor.call(this);
5397 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5399 * @cfg {String} header The header text to display in the Grid view.
5402 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5403 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5404 * specified, the column's index is used as an index into the Record's data Array.
5407 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5408 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5411 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5412 * Defaults to the value of the {@link #defaultSortable} property.
5413 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5416 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5419 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5422 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5425 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5428 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5429 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5430 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5431 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5434 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5437 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5440 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5443 * @cfg {String} cursor (Optional)
5446 * @cfg {String} tooltip (Optional)
5449 * @cfg {Number} xs (Optional)
5452 * @cfg {Number} sm (Optional)
5455 * @cfg {Number} md (Optional)
5458 * @cfg {Number} lg (Optional)
5461 * Returns the id of the column at the specified index.
5462 * @param {Number} index The column index
5463 * @return {String} the id
5465 getColumnId : function(index){
5466 return this.config[index].id;
5470 * Returns the column for a specified id.
5471 * @param {String} id The column id
5472 * @return {Object} the column
5474 getColumnById : function(id){
5475 return this.lookup[id];
5480 * Returns the column for a specified dataIndex.
5481 * @param {String} dataIndex The column dataIndex
5482 * @return {Object|Boolean} the column or false if not found
5484 getColumnByDataIndex: function(dataIndex){
5485 var index = this.findColumnIndex(dataIndex);
5486 return index > -1 ? this.config[index] : false;
5490 * Returns the index for a specified column id.
5491 * @param {String} id The column id
5492 * @return {Number} the index, or -1 if not found
5494 getIndexById : function(id){
5495 for(var i = 0, len = this.config.length; i < len; i++){
5496 if(this.config[i].id == id){
5504 * Returns the index for a specified column dataIndex.
5505 * @param {String} dataIndex The column dataIndex
5506 * @return {Number} the index, or -1 if not found
5509 findColumnIndex : function(dataIndex){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(this.config[i].dataIndex == dataIndex){
5519 moveColumn : function(oldIndex, newIndex){
5520 var c = this.config[oldIndex];
5521 this.config.splice(oldIndex, 1);
5522 this.config.splice(newIndex, 0, c);
5523 this.dataMap = null;
5524 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5527 isLocked : function(colIndex){
5528 return this.config[colIndex].locked === true;
5531 setLocked : function(colIndex, value, suppressEvent){
5532 if(this.isLocked(colIndex) == value){
5535 this.config[colIndex].locked = value;
5537 this.fireEvent("columnlockchange", this, colIndex, value);
5541 getTotalLockedWidth : function(){
5543 for(var i = 0; i < this.config.length; i++){
5544 if(this.isLocked(i) && !this.isHidden(i)){
5545 this.totalWidth += this.getColumnWidth(i);
5551 getLockedCount : function(){
5552 for(var i = 0, len = this.config.length; i < len; i++){
5553 if(!this.isLocked(i)){
5558 return this.config.length;
5562 * Returns the number of columns.
5565 getColumnCount : function(visibleOnly){
5566 if(visibleOnly === true){
5568 for(var i = 0, len = this.config.length; i < len; i++){
5569 if(!this.isHidden(i)){
5575 return this.config.length;
5579 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5580 * @param {Function} fn
5581 * @param {Object} scope (optional)
5582 * @return {Array} result
5584 getColumnsBy : function(fn, scope){
5586 for(var i = 0, len = this.config.length; i < len; i++){
5587 var c = this.config[i];
5588 if(fn.call(scope||this, c, i) === true){
5596 * Returns true if the specified column is sortable.
5597 * @param {Number} col The column index
5600 isSortable : function(col){
5601 if(typeof this.config[col].sortable == "undefined"){
5602 return this.defaultSortable;
5604 return this.config[col].sortable;
5608 * Returns the rendering (formatting) function defined for the column.
5609 * @param {Number} col The column index.
5610 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5612 getRenderer : function(col){
5613 if(!this.config[col].renderer){
5614 return Roo.grid.ColumnModel.defaultRenderer;
5616 return this.config[col].renderer;
5620 * Sets the rendering (formatting) function for a column.
5621 * @param {Number} col The column index
5622 * @param {Function} fn The function to use to process the cell's raw data
5623 * to return HTML markup for the grid view. The render function is called with
5624 * the following parameters:<ul>
5625 * <li>Data value.</li>
5626 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5627 * <li>css A CSS style string to apply to the table cell.</li>
5628 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5629 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5630 * <li>Row index</li>
5631 * <li>Column index</li>
5632 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5634 setRenderer : function(col, fn){
5635 this.config[col].renderer = fn;
5639 * Returns the width for the specified column.
5640 * @param {Number} col The column index
5643 getColumnWidth : function(col){
5644 return this.config[col].width * 1 || this.defaultWidth;
5648 * Sets the width for a column.
5649 * @param {Number} col The column index
5650 * @param {Number} width The new width
5652 setColumnWidth : function(col, width, suppressEvent){
5653 this.config[col].width = width;
5654 this.totalWidth = null;
5656 this.fireEvent("widthchange", this, col, width);
5661 * Returns the total width of all columns.
5662 * @param {Boolean} includeHidden True to include hidden column widths
5665 getTotalWidth : function(includeHidden){
5666 if(!this.totalWidth){
5667 this.totalWidth = 0;
5668 for(var i = 0, len = this.config.length; i < len; i++){
5669 if(includeHidden || !this.isHidden(i)){
5670 this.totalWidth += this.getColumnWidth(i);
5674 return this.totalWidth;
5678 * Returns the header for the specified column.
5679 * @param {Number} col The column index
5682 getColumnHeader : function(col){
5683 return this.config[col].header;
5687 * Sets the header for a column.
5688 * @param {Number} col The column index
5689 * @param {String} header The new header
5691 setColumnHeader : function(col, header){
5692 this.config[col].header = header;
5693 this.fireEvent("headerchange", this, col, header);
5697 * Returns the tooltip for the specified column.
5698 * @param {Number} col The column index
5701 getColumnTooltip : function(col){
5702 return this.config[col].tooltip;
5705 * Sets the tooltip for a column.
5706 * @param {Number} col The column index
5707 * @param {String} tooltip The new tooltip
5709 setColumnTooltip : function(col, tooltip){
5710 this.config[col].tooltip = tooltip;
5714 * Returns the dataIndex for the specified column.
5715 * @param {Number} col The column index
5718 getDataIndex : function(col){
5719 return this.config[col].dataIndex;
5723 * Sets the dataIndex for a column.
5724 * @param {Number} col The column index
5725 * @param {Number} dataIndex The new dataIndex
5727 setDataIndex : function(col, dataIndex){
5728 this.config[col].dataIndex = dataIndex;
5734 * Returns true if the cell is editable.
5735 * @param {Number} colIndex The column index
5736 * @param {Number} rowIndex The row index - this is nto actually used..?
5739 isCellEditable : function(colIndex, rowIndex){
5740 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5744 * Returns the editor defined for the cell/column.
5745 * return false or null to disable editing.
5746 * @param {Number} colIndex The column index
5747 * @param {Number} rowIndex The row index
5750 getCellEditor : function(colIndex, rowIndex){
5751 return this.config[colIndex].editor;
5755 * Sets if a column is editable.
5756 * @param {Number} col The column index
5757 * @param {Boolean} editable True if the column is editable
5759 setEditable : function(col, editable){
5760 this.config[col].editable = editable;
5765 * Returns true if the column is hidden.
5766 * @param {Number} colIndex The column index
5769 isHidden : function(colIndex){
5770 return this.config[colIndex].hidden;
5775 * Returns true if the column width cannot be changed
5777 isFixed : function(colIndex){
5778 return this.config[colIndex].fixed;
5782 * Returns true if the column can be resized
5785 isResizable : function(colIndex){
5786 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5789 * Sets if a column is hidden.
5790 * @param {Number} colIndex The column index
5791 * @param {Boolean} hidden True if the column is hidden
5793 setHidden : function(colIndex, hidden){
5794 this.config[colIndex].hidden = hidden;
5795 this.totalWidth = null;
5796 this.fireEvent("hiddenchange", this, colIndex, hidden);
5800 * Sets the editor for a column.
5801 * @param {Number} col The column index
5802 * @param {Object} editor The editor object
5804 setEditor : function(col, editor){
5805 this.config[col].editor = editor;
5809 Roo.grid.ColumnModel.defaultRenderer = function(value)
5811 if(typeof value == "object") {
5814 if(typeof value == "string" && value.length < 1){
5818 return String.format("{0}", value);
5821 // Alias for backwards compatibility
5822 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5825 * Ext JS Library 1.1.1
5826 * Copyright(c) 2006-2007, Ext JS, LLC.
5828 * Originally Released Under LGPL - original licence link has changed is not relivant.
5831 * <script type="text/javascript">
5835 * @class Roo.LoadMask
5836 * A simple utility class for generically masking elements while loading data. If the element being masked has
5837 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5838 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5839 * element's UpdateManager load indicator and will be destroyed after the initial load.
5841 * Create a new LoadMask
5842 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5843 * @param {Object} config The config object
5845 Roo.LoadMask = function(el, config){
5846 this.el = Roo.get(el);
5847 Roo.apply(this, config);
5849 this.store.on('beforeload', this.onBeforeLoad, this);
5850 this.store.on('load', this.onLoad, this);
5851 this.store.on('loadexception', this.onLoadException, this);
5852 this.removeMask = false;
5854 var um = this.el.getUpdateManager();
5855 um.showLoadIndicator = false; // disable the default indicator
5856 um.on('beforeupdate', this.onBeforeLoad, this);
5857 um.on('update', this.onLoad, this);
5858 um.on('failure', this.onLoad, this);
5859 this.removeMask = true;
5863 Roo.LoadMask.prototype = {
5865 * @cfg {Boolean} removeMask
5866 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5867 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5871 * The text to display in a centered loading message box (defaults to 'Loading...')
5875 * @cfg {String} msgCls
5876 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5878 msgCls : 'x-mask-loading',
5881 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5887 * Disables the mask to prevent it from being displayed
5889 disable : function(){
5890 this.disabled = true;
5894 * Enables the mask so that it can be displayed
5896 enable : function(){
5897 this.disabled = false;
5900 onLoadException : function()
5904 if (typeof(arguments[3]) != 'undefined') {
5905 Roo.MessageBox.alert("Error loading",arguments[3]);
5909 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5910 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5917 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5922 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5926 onBeforeLoad : function(){
5928 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5933 destroy : function(){
5935 this.store.un('beforeload', this.onBeforeLoad, this);
5936 this.store.un('load', this.onLoad, this);
5937 this.store.un('loadexception', this.onLoadException, this);
5939 var um = this.el.getUpdateManager();
5940 um.un('beforeupdate', this.onBeforeLoad, this);
5941 um.un('update', this.onLoad, this);
5942 um.un('failure', this.onLoad, this);
5953 * @class Roo.bootstrap.Table
5954 * @extends Roo.bootstrap.Component
5955 * Bootstrap Table class
5956 * @cfg {String} cls table class
5957 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5958 * @cfg {String} bgcolor Specifies the background color for a table
5959 * @cfg {Number} border Specifies whether the table cells should have borders or not
5960 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5961 * @cfg {Number} cellspacing Specifies the space between cells
5962 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5963 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5964 * @cfg {String} sortable Specifies that the table should be sortable
5965 * @cfg {String} summary Specifies a summary of the content of a table
5966 * @cfg {Number} width Specifies the width of a table
5967 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5969 * @cfg {boolean} striped Should the rows be alternative striped
5970 * @cfg {boolean} bordered Add borders to the table
5971 * @cfg {boolean} hover Add hover highlighting
5972 * @cfg {boolean} condensed Format condensed
5973 * @cfg {boolean} responsive Format condensed
5974 * @cfg {Boolean} loadMask (true|false) default false
5975 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5976 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5977 * @cfg {Boolean} rowSelection (true|false) default false
5978 * @cfg {Boolean} cellSelection (true|false) default false
5979 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5980 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5981 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5982 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5986 * Create a new Table
5987 * @param {Object} config The config object
5990 Roo.bootstrap.Table = function(config){
5991 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5996 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5997 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5998 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5999 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6001 this.sm = this.sm || {xtype: 'RowSelectionModel'};
6003 this.sm.grid = this;
6004 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6005 this.sm = this.selModel;
6006 this.sm.xmodule = this.xmodule || false;
6009 if (this.cm && typeof(this.cm.config) == 'undefined') {
6010 this.colModel = new Roo.grid.ColumnModel(this.cm);
6011 this.cm = this.colModel;
6012 this.cm.xmodule = this.xmodule || false;
6015 this.store= Roo.factory(this.store, Roo.data);
6016 this.ds = this.store;
6017 this.ds.xmodule = this.xmodule || false;
6020 if (this.footer && this.store) {
6021 this.footer.dataSource = this.ds;
6022 this.footer = Roo.factory(this.footer);
6029 * Fires when a cell is clicked
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Roo.Element} el
6032 * @param {Number} rowIndex
6033 * @param {Number} columnIndex
6034 * @param {Roo.EventObject} e
6038 * @event celldblclick
6039 * Fires when a cell is double clicked
6040 * @param {Roo.bootstrap.Table} this
6041 * @param {Roo.Element} el
6042 * @param {Number} rowIndex
6043 * @param {Number} columnIndex
6044 * @param {Roo.EventObject} e
6046 "celldblclick" : true,
6049 * Fires when a row is clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Roo.Element} el
6052 * @param {Number} rowIndex
6053 * @param {Roo.EventObject} e
6057 * @event rowdblclick
6058 * Fires when a row is double clicked
6059 * @param {Roo.bootstrap.Table} this
6060 * @param {Roo.Element} el
6061 * @param {Number} rowIndex
6062 * @param {Roo.EventObject} e
6064 "rowdblclick" : true,
6067 * Fires when a mouseover occur
6068 * @param {Roo.bootstrap.Table} this
6069 * @param {Roo.Element} el
6070 * @param {Number} rowIndex
6071 * @param {Number} columnIndex
6072 * @param {Roo.EventObject} e
6077 * Fires when a mouseout occur
6078 * @param {Roo.bootstrap.Table} this
6079 * @param {Roo.Element} el
6080 * @param {Number} rowIndex
6081 * @param {Number} columnIndex
6082 * @param {Roo.EventObject} e
6087 * Fires when a row is rendered, so you can change add a style to it.
6088 * @param {Roo.bootstrap.Table} this
6089 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6093 * @event rowsrendered
6094 * Fires when all the rows have been rendered
6095 * @param {Roo.bootstrap.Table} this
6097 'rowsrendered' : true,
6099 * @event contextmenu
6100 * The raw contextmenu event for the entire grid.
6101 * @param {Roo.EventObject} e
6103 "contextmenu" : true,
6105 * @event rowcontextmenu
6106 * Fires when a row is right clicked
6107 * @param {Roo.bootstrap.Table} this
6108 * @param {Number} rowIndex
6109 * @param {Roo.EventObject} e
6111 "rowcontextmenu" : true,
6113 * @event cellcontextmenu
6114 * Fires when a cell is right clicked
6115 * @param {Roo.bootstrap.Table} this
6116 * @param {Number} rowIndex
6117 * @param {Number} cellIndex
6118 * @param {Roo.EventObject} e
6120 "cellcontextmenu" : true,
6122 * @event headercontextmenu
6123 * Fires when a header is right clicked
6124 * @param {Roo.bootstrap.Table} this
6125 * @param {Number} columnIndex
6126 * @param {Roo.EventObject} e
6128 "headercontextmenu" : true
6132 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6158 rowSelection : false,
6159 cellSelection : false,
6162 // Roo.Element - the tbody
6164 // Roo.Element - thead element
6167 container: false, // used by gridpanel...
6173 auto_hide_footer : false,
6175 getAutoCreate : function()
6177 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6184 if (this.scrollBody) {
6185 cfg.cls += ' table-body-fixed';
6188 cfg.cls += ' table-striped';
6192 cfg.cls += ' table-hover';
6194 if (this.bordered) {
6195 cfg.cls += ' table-bordered';
6197 if (this.condensed) {
6198 cfg.cls += ' table-condensed';
6200 if (this.responsive) {
6201 cfg.cls += ' table-responsive';
6205 cfg.cls+= ' ' +this.cls;
6208 // this lot should be simplifed...
6221 ].forEach(function(k) {
6229 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6232 if(this.store || this.cm){
6233 if(this.headerShow){
6234 cfg.cn.push(this.renderHeader());
6237 cfg.cn.push(this.renderBody());
6239 if(this.footerShow){
6240 cfg.cn.push(this.renderFooter());
6242 // where does this come from?
6243 //cfg.cls+= ' TableGrid';
6246 return { cn : [ cfg ] };
6249 initEvents : function()
6251 if(!this.store || !this.cm){
6254 if (this.selModel) {
6255 this.selModel.initEvents();
6259 //Roo.log('initEvents with ds!!!!');
6261 this.mainBody = this.el.select('tbody', true).first();
6262 this.mainHead = this.el.select('thead', true).first();
6263 this.mainFoot = this.el.select('tfoot', true).first();
6269 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6270 e.on('click', _this.sort, _this);
6273 this.mainBody.on("click", this.onClick, this);
6274 this.mainBody.on("dblclick", this.onDblClick, this);
6276 // why is this done????? = it breaks dialogs??
6277 //this.parent().el.setStyle('position', 'relative');
6281 this.footer.parentId = this.id;
6282 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6285 this.el.select('tfoot tr td').first().addClass('hide');
6290 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6293 this.store.on('load', this.onLoad, this);
6294 this.store.on('beforeload', this.onBeforeLoad, this);
6295 this.store.on('update', this.onUpdate, this);
6296 this.store.on('add', this.onAdd, this);
6297 this.store.on("clear", this.clear, this);
6299 this.el.on("contextmenu", this.onContextMenu, this);
6301 this.mainBody.on('scroll', this.onBodyScroll, this);
6303 this.cm.on("headerchange", this.onHeaderChange, this);
6305 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6309 onContextMenu : function(e, t)
6311 this.processEvent("contextmenu", e);
6314 processEvent : function(name, e)
6316 if (name != 'touchstart' ) {
6317 this.fireEvent(name, e);
6320 var t = e.getTarget();
6322 var cell = Roo.get(t);
6328 if(cell.findParent('tfoot', false, true)){
6332 if(cell.findParent('thead', false, true)){
6334 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6335 cell = Roo.get(t).findParent('th', false, true);
6337 Roo.log("failed to find th in thead?");
6338 Roo.log(e.getTarget());
6343 var cellIndex = cell.dom.cellIndex;
6345 var ename = name == 'touchstart' ? 'click' : name;
6346 this.fireEvent("header" + ename, this, cellIndex, e);
6351 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6352 cell = Roo.get(t).findParent('td', false, true);
6354 Roo.log("failed to find th in tbody?");
6355 Roo.log(e.getTarget());
6360 var row = cell.findParent('tr', false, true);
6361 var cellIndex = cell.dom.cellIndex;
6362 var rowIndex = row.dom.rowIndex - 1;
6366 this.fireEvent("row" + name, this, rowIndex, e);
6370 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6376 onMouseover : function(e, el)
6378 var cell = Roo.get(el);
6384 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6385 cell = cell.findParent('td', false, true);
6388 var row = cell.findParent('tr', false, true);
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = row.dom.rowIndex - 1; // start from 0
6392 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6396 onMouseout : function(e, el)
6398 var cell = Roo.get(el);
6404 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6405 cell = cell.findParent('td', false, true);
6408 var row = cell.findParent('tr', false, true);
6409 var cellIndex = cell.dom.cellIndex;
6410 var rowIndex = row.dom.rowIndex - 1; // start from 0
6412 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6416 onClick : function(e, el)
6418 var cell = Roo.get(el);
6420 if(!cell || (!this.cellSelection && !this.rowSelection)){
6424 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6425 cell = cell.findParent('td', false, true);
6428 if(!cell || typeof(cell) == 'undefined'){
6432 var row = cell.findParent('tr', false, true);
6434 if(!row || typeof(row) == 'undefined'){
6438 var cellIndex = cell.dom.cellIndex;
6439 var rowIndex = this.getRowIndex(row);
6441 // why??? - should these not be based on SelectionModel?
6442 if(this.cellSelection){
6443 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6446 if(this.rowSelection){
6447 this.fireEvent('rowclick', this, row, rowIndex, e);
6453 onDblClick : function(e,el)
6455 var cell = Roo.get(el);
6457 if(!cell || (!this.cellSelection && !this.rowSelection)){
6461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6462 cell = cell.findParent('td', false, true);
6465 if(!cell || typeof(cell) == 'undefined'){
6469 var row = cell.findParent('tr', false, true);
6471 if(!row || typeof(row) == 'undefined'){
6475 var cellIndex = cell.dom.cellIndex;
6476 var rowIndex = this.getRowIndex(row);
6478 if(this.cellSelection){
6479 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6482 if(this.rowSelection){
6483 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6487 sort : function(e,el)
6489 var col = Roo.get(el);
6491 if(!col.hasClass('sortable')){
6495 var sort = col.attr('sort');
6498 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6502 this.store.sortInfo = {field : sort, direction : dir};
6505 Roo.log("calling footer first");
6506 this.footer.onClick('first');
6509 this.store.load({ params : { start : 0 } });
6513 renderHeader : function()
6521 this.totalWidth = 0;
6523 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6525 var config = cm.config[i];
6529 cls : 'x-hcol-' + i,
6531 html: cm.getColumnHeader(i)
6536 if(typeof(config.sortable) != 'undefined' && config.sortable){
6538 c.html = '<i class="glyphicon"></i>' + c.html;
6541 if(typeof(config.lgHeader) != 'undefined'){
6542 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6545 if(typeof(config.mdHeader) != 'undefined'){
6546 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6549 if(typeof(config.smHeader) != 'undefined'){
6550 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6553 if(typeof(config.xsHeader) != 'undefined'){
6554 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6561 if(typeof(config.tooltip) != 'undefined'){
6562 c.tooltip = config.tooltip;
6565 if(typeof(config.colspan) != 'undefined'){
6566 c.colspan = config.colspan;
6569 if(typeof(config.hidden) != 'undefined' && config.hidden){
6570 c.style += ' display:none;';
6573 if(typeof(config.dataIndex) != 'undefined'){
6574 c.sort = config.dataIndex;
6579 if(typeof(config.align) != 'undefined' && config.align.length){
6580 c.style += ' text-align:' + config.align + ';';
6583 if(typeof(config.width) != 'undefined'){
6584 c.style += ' width:' + config.width + 'px;';
6585 this.totalWidth += config.width;
6587 this.totalWidth += 100; // assume minimum of 100 per column?
6590 if(typeof(config.cls) != 'undefined'){
6591 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6594 ['xs','sm','md','lg'].map(function(size){
6596 if(typeof(config[size]) == 'undefined'){
6600 if (!config[size]) { // 0 = hidden
6601 c.cls += ' hidden-' + size;
6605 c.cls += ' col-' + size + '-' + config[size];
6615 renderBody : function()
6625 colspan : this.cm.getColumnCount()
6635 renderFooter : function()
6645 colspan : this.cm.getColumnCount()
6659 // Roo.log('ds onload');
6664 var ds = this.store;
6666 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6667 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6668 if (_this.store.sortInfo) {
6670 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6671 e.select('i', true).addClass(['glyphicon-arrow-up']);
6674 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6675 e.select('i', true).addClass(['glyphicon-arrow-down']);
6680 var tbody = this.mainBody;
6682 if(ds.getCount() > 0){
6683 ds.data.each(function(d,rowIndex){
6684 var row = this.renderRow(cm, ds, rowIndex);
6686 tbody.createChild(row);
6690 if(row.cellObjects.length){
6691 Roo.each(row.cellObjects, function(r){
6692 _this.renderCellObject(r);
6699 var tfoot = this.el.select('tfoot', true).first();
6701 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6703 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6705 var total = this.ds.getTotalCount();
6707 if(this.footer.pageSize < total){
6708 this.mainFoot.show();
6712 Roo.each(this.el.select('tbody td', true).elements, function(e){
6713 e.on('mouseover', _this.onMouseover, _this);
6716 Roo.each(this.el.select('tbody td', true).elements, function(e){
6717 e.on('mouseout', _this.onMouseout, _this);
6719 this.fireEvent('rowsrendered', this);
6725 onUpdate : function(ds,record)
6727 this.refreshRow(record);
6731 onRemove : function(ds, record, index, isUpdate){
6732 if(isUpdate !== true){
6733 this.fireEvent("beforerowremoved", this, index, record);
6735 var bt = this.mainBody.dom;
6737 var rows = this.el.select('tbody > tr', true).elements;
6739 if(typeof(rows[index]) != 'undefined'){
6740 bt.removeChild(rows[index].dom);
6743 // if(bt.rows[index]){
6744 // bt.removeChild(bt.rows[index]);
6747 if(isUpdate !== true){
6748 //this.stripeRows(index);
6749 //this.syncRowHeights(index, index);
6751 this.fireEvent("rowremoved", this, index, record);
6755 onAdd : function(ds, records, rowIndex)
6757 //Roo.log('on Add called');
6758 // - note this does not handle multiple adding very well..
6759 var bt = this.mainBody.dom;
6760 for (var i =0 ; i < records.length;i++) {
6761 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6762 //Roo.log(records[i]);
6763 //Roo.log(this.store.getAt(rowIndex+i));
6764 this.insertRow(this.store, rowIndex + i, false);
6771 refreshRow : function(record){
6772 var ds = this.store, index;
6773 if(typeof record == 'number'){
6775 record = ds.getAt(index);
6777 index = ds.indexOf(record);
6779 this.insertRow(ds, index, true);
6781 this.onRemove(ds, record, index+1, true);
6783 //this.syncRowHeights(index, index);
6785 this.fireEvent("rowupdated", this, index, record);
6788 insertRow : function(dm, rowIndex, isUpdate){
6791 this.fireEvent("beforerowsinserted", this, rowIndex);
6793 //var s = this.getScrollState();
6794 var row = this.renderRow(this.cm, this.store, rowIndex);
6795 // insert before rowIndex..
6796 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6800 if(row.cellObjects.length){
6801 Roo.each(row.cellObjects, function(r){
6802 _this.renderCellObject(r);
6807 this.fireEvent("rowsinserted", this, rowIndex);
6808 //this.syncRowHeights(firstRow, lastRow);
6809 //this.stripeRows(firstRow);
6816 getRowDom : function(rowIndex)
6818 var rows = this.el.select('tbody > tr', true).elements;
6820 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6823 // returns the object tree for a tr..
6826 renderRow : function(cm, ds, rowIndex)
6828 var d = ds.getAt(rowIndex);
6832 cls : 'x-row-' + rowIndex,
6836 var cellObjects = [];
6838 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6839 var config = cm.config[i];
6841 var renderer = cm.getRenderer(i);
6845 if(typeof(renderer) !== 'undefined'){
6846 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6848 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6849 // and are rendered into the cells after the row is rendered - using the id for the element.
6851 if(typeof(value) === 'object'){
6861 rowIndex : rowIndex,
6866 this.fireEvent('rowclass', this, rowcfg);
6870 cls : rowcfg.rowClass + ' x-col-' + i,
6872 html: (typeof(value) === 'object') ? '' : value
6879 if(typeof(config.colspan) != 'undefined'){
6880 td.colspan = config.colspan;
6883 if(typeof(config.hidden) != 'undefined' && config.hidden){
6884 td.style += ' display:none;';
6887 if(typeof(config.align) != 'undefined' && config.align.length){
6888 td.style += ' text-align:' + config.align + ';';
6890 if(typeof(config.valign) != 'undefined' && config.valign.length){
6891 td.style += ' vertical-align:' + config.valign + ';';
6894 if(typeof(config.width) != 'undefined'){
6895 td.style += ' width:' + config.width + 'px;';
6898 if(typeof(config.cursor) != 'undefined'){
6899 td.style += ' cursor:' + config.cursor + ';';
6902 if(typeof(config.cls) != 'undefined'){
6903 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6906 ['xs','sm','md','lg'].map(function(size){
6908 if(typeof(config[size]) == 'undefined'){
6912 if (!config[size]) { // 0 = hidden
6913 td.cls += ' hidden-' + size;
6917 td.cls += ' col-' + size + '-' + config[size];
6925 row.cellObjects = cellObjects;
6933 onBeforeLoad : function()
6942 this.el.select('tbody', true).first().dom.innerHTML = '';
6945 * Show or hide a row.
6946 * @param {Number} rowIndex to show or hide
6947 * @param {Boolean} state hide
6949 setRowVisibility : function(rowIndex, state)
6951 var bt = this.mainBody.dom;
6953 var rows = this.el.select('tbody > tr', true).elements;
6955 if(typeof(rows[rowIndex]) == 'undefined'){
6958 rows[rowIndex].dom.style.display = state ? '' : 'none';
6962 getSelectionModel : function(){
6964 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6966 return this.selModel;
6969 * Render the Roo.bootstrap object from renderder
6971 renderCellObject : function(r)
6975 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6977 var t = r.cfg.render(r.container);
6980 Roo.each(r.cfg.cn, function(c){
6982 container: t.getChildContainer(),
6985 _this.renderCellObject(child);
6990 getRowIndex : function(row)
6994 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7005 * Returns the grid's underlying element = used by panel.Grid
7006 * @return {Element} The element
7008 getGridEl : function(){
7012 * Forces a resize - used by panel.Grid
7013 * @return {Element} The element
7015 autoSize : function()
7017 //var ctr = Roo.get(this.container.dom.parentElement);
7018 var ctr = Roo.get(this.el.dom);
7020 var thd = this.getGridEl().select('thead',true).first();
7021 var tbd = this.getGridEl().select('tbody', true).first();
7022 var tfd = this.getGridEl().select('tfoot', true).first();
7024 var cw = ctr.getWidth();
7028 tbd.setSize(ctr.getWidth(),
7029 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7031 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7034 cw = Math.max(cw, this.totalWidth);
7035 this.getGridEl().select('tr',true).setWidth(cw);
7036 // resize 'expandable coloumn?
7038 return; // we doe not have a view in this design..
7041 onBodyScroll: function()
7043 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7045 this.mainHead.setStyle({
7046 'position' : 'relative',
7047 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7053 var scrollHeight = this.mainBody.dom.scrollHeight;
7055 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7057 var height = this.mainBody.getHeight();
7059 if(scrollHeight - height == scrollTop) {
7061 var total = this.ds.getTotalCount();
7063 if(this.footer.cursor + this.footer.pageSize < total){
7065 this.footer.ds.load({
7067 start : this.footer.cursor + this.footer.pageSize,
7068 limit : this.footer.pageSize
7078 onHeaderChange : function()
7080 var header = this.renderHeader();
7081 var table = this.el.select('table', true).first();
7083 this.mainHead.remove();
7084 this.mainHead = table.createChild(header, this.mainBody, false);
7087 onHiddenChange : function(colModel, colIndex, hidden)
7089 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7090 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7092 this.CSS.updateRule(thSelector, "display", "");
7093 this.CSS.updateRule(tdSelector, "display", "");
7096 this.CSS.updateRule(thSelector, "display", "none");
7097 this.CSS.updateRule(tdSelector, "display", "none");
7100 this.onHeaderChange();
7104 setColumnWidth: function(col_index, width)
7106 // width = "md-2 xs-2..."
7107 if(!this.colModel.config[col_index]) {
7111 var w = width.split(" ");
7113 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7115 var h_rows = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7117 if(rows.length != h_rows.length) {
7121 for(var i = 0; i < rows.length; i++) {
7123 for(var j = 0; w.length; j++) {
7125 var size_cls = w[j].split("-");
7127 if(!Number.isInteger(size_cls[1] * 1)) {
7131 if(!this.colModel.config[col_index][size_cls[0]]) {
7135 rows[i].classList.replace(
7136 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7137 "col-"+size_cls[0]+"-"+size_cls[1]
7140 h_rows[i].classList.replace(
7141 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7142 "col-"+size_cls[0]+"-"+size_cls[1]
7145 this.colModel.config[col_index][size_cls[0]] = size_cls[1]
7161 * @class Roo.bootstrap.TableCell
7162 * @extends Roo.bootstrap.Component
7163 * Bootstrap TableCell class
7164 * @cfg {String} html cell contain text
7165 * @cfg {String} cls cell class
7166 * @cfg {String} tag cell tag (td|th) default td
7167 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7168 * @cfg {String} align Aligns the content in a cell
7169 * @cfg {String} axis Categorizes cells
7170 * @cfg {String} bgcolor Specifies the background color of a cell
7171 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7172 * @cfg {Number} colspan Specifies the number of columns a cell should span
7173 * @cfg {String} headers Specifies one or more header cells a cell is related to
7174 * @cfg {Number} height Sets the height of a cell
7175 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7176 * @cfg {Number} rowspan Sets the number of rows a cell should span
7177 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7178 * @cfg {String} valign Vertical aligns the content in a cell
7179 * @cfg {Number} width Specifies the width of a cell
7182 * Create a new TableCell
7183 * @param {Object} config The config object
7186 Roo.bootstrap.TableCell = function(config){
7187 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7190 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7210 getAutoCreate : function(){
7211 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7231 cfg.align=this.align
7237 cfg.bgcolor=this.bgcolor
7240 cfg.charoff=this.charoff
7243 cfg.colspan=this.colspan
7246 cfg.headers=this.headers
7249 cfg.height=this.height
7252 cfg.nowrap=this.nowrap
7255 cfg.rowspan=this.rowspan
7258 cfg.scope=this.scope
7261 cfg.valign=this.valign
7264 cfg.width=this.width
7283 * @class Roo.bootstrap.TableRow
7284 * @extends Roo.bootstrap.Component
7285 * Bootstrap TableRow class
7286 * @cfg {String} cls row class
7287 * @cfg {String} align Aligns the content in a table row
7288 * @cfg {String} bgcolor Specifies a background color for a table row
7289 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7290 * @cfg {String} valign Vertical aligns the content in a table row
7293 * Create a new TableRow
7294 * @param {Object} config The config object
7297 Roo.bootstrap.TableRow = function(config){
7298 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7301 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7309 getAutoCreate : function(){
7310 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7320 cfg.align = this.align;
7323 cfg.bgcolor = this.bgcolor;
7326 cfg.charoff = this.charoff;
7329 cfg.valign = this.valign;
7347 * @class Roo.bootstrap.TableBody
7348 * @extends Roo.bootstrap.Component
7349 * Bootstrap TableBody class
7350 * @cfg {String} cls element class
7351 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7352 * @cfg {String} align Aligns the content inside the element
7353 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7354 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7357 * Create a new TableBody
7358 * @param {Object} config The config object
7361 Roo.bootstrap.TableBody = function(config){
7362 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7365 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7373 getAutoCreate : function(){
7374 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7388 cfg.align = this.align;
7391 cfg.charoff = this.charoff;
7394 cfg.valign = this.valign;
7401 // initEvents : function()
7408 // this.store = Roo.factory(this.store, Roo.data);
7409 // this.store.on('load', this.onLoad, this);
7411 // this.store.load();
7415 // onLoad: function ()
7417 // this.fireEvent('load', this);
7427 * Ext JS Library 1.1.1
7428 * Copyright(c) 2006-2007, Ext JS, LLC.
7430 * Originally Released Under LGPL - original licence link has changed is not relivant.
7433 * <script type="text/javascript">
7436 // as we use this in bootstrap.
7437 Roo.namespace('Roo.form');
7439 * @class Roo.form.Action
7440 * Internal Class used to handle form actions
7442 * @param {Roo.form.BasicForm} el The form element or its id
7443 * @param {Object} config Configuration options
7448 // define the action interface
7449 Roo.form.Action = function(form, options){
7451 this.options = options || {};
7454 * Client Validation Failed
7457 Roo.form.Action.CLIENT_INVALID = 'client';
7459 * Server Validation Failed
7462 Roo.form.Action.SERVER_INVALID = 'server';
7464 * Connect to Server Failed
7467 Roo.form.Action.CONNECT_FAILURE = 'connect';
7469 * Reading Data from Server Failed
7472 Roo.form.Action.LOAD_FAILURE = 'load';
7474 Roo.form.Action.prototype = {
7476 failureType : undefined,
7477 response : undefined,
7481 run : function(options){
7486 success : function(response){
7491 handleResponse : function(response){
7495 // default connection failure
7496 failure : function(response){
7498 this.response = response;
7499 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7500 this.form.afterAction(this, false);
7503 processResponse : function(response){
7504 this.response = response;
7505 if(!response.responseText){
7508 this.result = this.handleResponse(response);
7512 // utility functions used internally
7513 getUrl : function(appendParams){
7514 var url = this.options.url || this.form.url || this.form.el.dom.action;
7516 var p = this.getParams();
7518 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7524 getMethod : function(){
7525 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7528 getParams : function(){
7529 var bp = this.form.baseParams;
7530 var p = this.options.params;
7532 if(typeof p == "object"){
7533 p = Roo.urlEncode(Roo.applyIf(p, bp));
7534 }else if(typeof p == 'string' && bp){
7535 p += '&' + Roo.urlEncode(bp);
7538 p = Roo.urlEncode(bp);
7543 createCallback : function(){
7545 success: this.success,
7546 failure: this.failure,
7548 timeout: (this.form.timeout*1000),
7549 upload: this.form.fileUpload ? this.success : undefined
7554 Roo.form.Action.Submit = function(form, options){
7555 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7558 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7561 haveProgress : false,
7562 uploadComplete : false,
7564 // uploadProgress indicator.
7565 uploadProgress : function()
7567 if (!this.form.progressUrl) {
7571 if (!this.haveProgress) {
7572 Roo.MessageBox.progress("Uploading", "Uploading");
7574 if (this.uploadComplete) {
7575 Roo.MessageBox.hide();
7579 this.haveProgress = true;
7581 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7583 var c = new Roo.data.Connection();
7585 url : this.form.progressUrl,
7590 success : function(req){
7591 //console.log(data);
7595 rdata = Roo.decode(req.responseText)
7597 Roo.log("Invalid data from server..");
7601 if (!rdata || !rdata.success) {
7603 Roo.MessageBox.alert(Roo.encode(rdata));
7606 var data = rdata.data;
7608 if (this.uploadComplete) {
7609 Roo.MessageBox.hide();
7614 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7615 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7618 this.uploadProgress.defer(2000,this);
7621 failure: function(data) {
7622 Roo.log('progress url failed ');
7633 // run get Values on the form, so it syncs any secondary forms.
7634 this.form.getValues();
7636 var o = this.options;
7637 var method = this.getMethod();
7638 var isPost = method == 'POST';
7639 if(o.clientValidation === false || this.form.isValid()){
7641 if (this.form.progressUrl) {
7642 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7643 (new Date() * 1) + '' + Math.random());
7648 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7649 form:this.form.el.dom,
7650 url:this.getUrl(!isPost),
7652 params:isPost ? this.getParams() : null,
7653 isUpload: this.form.fileUpload
7656 this.uploadProgress();
7658 }else if (o.clientValidation !== false){ // client validation failed
7659 this.failureType = Roo.form.Action.CLIENT_INVALID;
7660 this.form.afterAction(this, false);
7664 success : function(response)
7666 this.uploadComplete= true;
7667 if (this.haveProgress) {
7668 Roo.MessageBox.hide();
7672 var result = this.processResponse(response);
7673 if(result === true || result.success){
7674 this.form.afterAction(this, true);
7678 this.form.markInvalid(result.errors);
7679 this.failureType = Roo.form.Action.SERVER_INVALID;
7681 this.form.afterAction(this, false);
7683 failure : function(response)
7685 this.uploadComplete= true;
7686 if (this.haveProgress) {
7687 Roo.MessageBox.hide();
7690 this.response = response;
7691 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7692 this.form.afterAction(this, false);
7695 handleResponse : function(response){
7696 if(this.form.errorReader){
7697 var rs = this.form.errorReader.read(response);
7700 for(var i = 0, len = rs.records.length; i < len; i++) {
7701 var r = rs.records[i];
7705 if(errors.length < 1){
7709 success : rs.success,
7715 ret = Roo.decode(response.responseText);
7719 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7729 Roo.form.Action.Load = function(form, options){
7730 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7731 this.reader = this.form.reader;
7734 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7739 Roo.Ajax.request(Roo.apply(
7740 this.createCallback(), {
7741 method:this.getMethod(),
7742 url:this.getUrl(false),
7743 params:this.getParams()
7747 success : function(response){
7749 var result = this.processResponse(response);
7750 if(result === true || !result.success || !result.data){
7751 this.failureType = Roo.form.Action.LOAD_FAILURE;
7752 this.form.afterAction(this, false);
7755 this.form.clearInvalid();
7756 this.form.setValues(result.data);
7757 this.form.afterAction(this, true);
7760 handleResponse : function(response){
7761 if(this.form.reader){
7762 var rs = this.form.reader.read(response);
7763 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7765 success : rs.success,
7769 return Roo.decode(response.responseText);
7773 Roo.form.Action.ACTION_TYPES = {
7774 'load' : Roo.form.Action.Load,
7775 'submit' : Roo.form.Action.Submit
7784 * @class Roo.bootstrap.Form
7785 * @extends Roo.bootstrap.Component
7786 * Bootstrap Form class
7787 * @cfg {String} method GET | POST (default POST)
7788 * @cfg {String} labelAlign top | left (default top)
7789 * @cfg {String} align left | right - for navbars
7790 * @cfg {Boolean} loadMask load mask when submit (default true)
7795 * @param {Object} config The config object
7799 Roo.bootstrap.Form = function(config){
7801 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7803 Roo.bootstrap.Form.popover.apply();
7807 * @event clientvalidation
7808 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7809 * @param {Form} this
7810 * @param {Boolean} valid true if the form has passed client-side validation
7812 clientvalidation: true,
7814 * @event beforeaction
7815 * Fires before any action is performed. Return false to cancel the action.
7816 * @param {Form} this
7817 * @param {Action} action The action to be performed
7821 * @event actionfailed
7822 * Fires when an action fails.
7823 * @param {Form} this
7824 * @param {Action} action The action that failed
7826 actionfailed : true,
7828 * @event actioncomplete
7829 * Fires when an action is completed.
7830 * @param {Form} this
7831 * @param {Action} action The action that completed
7833 actioncomplete : true
7837 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7840 * @cfg {String} method
7841 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7846 * The URL to use for form actions if one isn't supplied in the action options.
7849 * @cfg {Boolean} fileUpload
7850 * Set to true if this form is a file upload.
7854 * @cfg {Object} baseParams
7855 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7859 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7863 * @cfg {Sting} align (left|right) for navbar forms
7868 activeAction : null,
7871 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7872 * element by passing it or its id or mask the form itself by passing in true.
7875 waitMsgTarget : false,
7880 * @cfg {Boolean} errorMask (true|false) default false
7885 * @cfg {Number} maskOffset Default 100
7890 * @cfg {Boolean} maskBody
7894 getAutoCreate : function(){
7898 method : this.method || 'POST',
7899 id : this.id || Roo.id(),
7902 if (this.parent().xtype.match(/^Nav/)) {
7903 cfg.cls = 'navbar-form navbar-' + this.align;
7907 if (this.labelAlign == 'left' ) {
7908 cfg.cls += ' form-horizontal';
7914 initEvents : function()
7916 this.el.on('submit', this.onSubmit, this);
7917 // this was added as random key presses on the form where triggering form submit.
7918 this.el.on('keypress', function(e) {
7919 if (e.getCharCode() != 13) {
7922 // we might need to allow it for textareas.. and some other items.
7923 // check e.getTarget().
7925 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7929 Roo.log("keypress blocked");
7937 onSubmit : function(e){
7942 * Returns true if client-side validation on the form is successful.
7945 isValid : function(){
7946 var items = this.getItems();
7950 items.each(function(f){
7956 Roo.log('invalid field: ' + f.name);
7960 if(!target && f.el.isVisible(true)){
7966 if(this.errorMask && !valid){
7967 Roo.bootstrap.Form.popover.mask(this, target);
7974 * Returns true if any fields in this form have changed since their original load.
7977 isDirty : function(){
7979 var items = this.getItems();
7980 items.each(function(f){
7990 * Performs a predefined action (submit or load) or custom actions you define on this form.
7991 * @param {String} actionName The name of the action type
7992 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7993 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7994 * accept other config options):
7996 Property Type Description
7997 ---------------- --------------- ----------------------------------------------------------------------------------
7998 url String The url for the action (defaults to the form's url)
7999 method String The form method to use (defaults to the form's method, or POST if not defined)
8000 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
8001 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
8002 validate the form on the client (defaults to false)
8004 * @return {BasicForm} this
8006 doAction : function(action, options){
8007 if(typeof action == 'string'){
8008 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8010 if(this.fireEvent('beforeaction', this, action) !== false){
8011 this.beforeAction(action);
8012 action.run.defer(100, action);
8018 beforeAction : function(action){
8019 var o = action.options;
8024 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8026 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8029 // not really supported yet.. ??
8031 //if(this.waitMsgTarget === true){
8032 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8033 //}else if(this.waitMsgTarget){
8034 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8035 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8037 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8043 afterAction : function(action, success){
8044 this.activeAction = null;
8045 var o = action.options;
8050 Roo.get(document.body).unmask();
8056 //if(this.waitMsgTarget === true){
8057 // this.el.unmask();
8058 //}else if(this.waitMsgTarget){
8059 // this.waitMsgTarget.unmask();
8061 // Roo.MessageBox.updateProgress(1);
8062 // Roo.MessageBox.hide();
8069 Roo.callback(o.success, o.scope, [this, action]);
8070 this.fireEvent('actioncomplete', this, action);
8074 // failure condition..
8075 // we have a scenario where updates need confirming.
8076 // eg. if a locking scenario exists..
8077 // we look for { errors : { needs_confirm : true }} in the response.
8079 (typeof(action.result) != 'undefined') &&
8080 (typeof(action.result.errors) != 'undefined') &&
8081 (typeof(action.result.errors.needs_confirm) != 'undefined')
8084 Roo.log("not supported yet");
8087 Roo.MessageBox.confirm(
8088 "Change requires confirmation",
8089 action.result.errorMsg,
8094 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8104 Roo.callback(o.failure, o.scope, [this, action]);
8105 // show an error message if no failed handler is set..
8106 if (!this.hasListener('actionfailed')) {
8107 Roo.log("need to add dialog support");
8109 Roo.MessageBox.alert("Error",
8110 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8111 action.result.errorMsg :
8112 "Saving Failed, please check your entries or try again"
8117 this.fireEvent('actionfailed', this, action);
8122 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8123 * @param {String} id The value to search for
8126 findField : function(id){
8127 var items = this.getItems();
8128 var field = items.get(id);
8130 items.each(function(f){
8131 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8138 return field || null;
8141 * Mark fields in this form invalid in bulk.
8142 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8143 * @return {BasicForm} this
8145 markInvalid : function(errors){
8146 if(errors instanceof Array){
8147 for(var i = 0, len = errors.length; i < len; i++){
8148 var fieldError = errors[i];
8149 var f = this.findField(fieldError.id);
8151 f.markInvalid(fieldError.msg);
8157 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8158 field.markInvalid(errors[id]);
8162 //Roo.each(this.childForms || [], function (f) {
8163 // f.markInvalid(errors);
8170 * Set values for fields in this form in bulk.
8171 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8172 * @return {BasicForm} this
8174 setValues : function(values){
8175 if(values instanceof Array){ // array of objects
8176 for(var i = 0, len = values.length; i < len; i++){
8178 var f = this.findField(v.id);
8180 f.setValue(v.value);
8181 if(this.trackResetOnLoad){
8182 f.originalValue = f.getValue();
8186 }else{ // object hash
8189 if(typeof values[id] != 'function' && (field = this.findField(id))){
8191 if (field.setFromData &&
8193 field.displayField &&
8194 // combos' with local stores can
8195 // be queried via setValue()
8196 // to set their value..
8197 (field.store && !field.store.isLocal)
8201 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8202 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8203 field.setFromData(sd);
8205 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8207 field.setFromData(values);
8210 field.setValue(values[id]);
8214 if(this.trackResetOnLoad){
8215 field.originalValue = field.getValue();
8221 //Roo.each(this.childForms || [], function (f) {
8222 // f.setValues(values);
8229 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8230 * they are returned as an array.
8231 * @param {Boolean} asString
8234 getValues : function(asString){
8235 //if (this.childForms) {
8236 // copy values from the child forms
8237 // Roo.each(this.childForms, function (f) {
8238 // this.setValues(f.getValues());
8244 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8245 if(asString === true){
8248 return Roo.urlDecode(fs);
8252 * Returns the fields in this form as an object with key/value pairs.
8253 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8256 getFieldValues : function(with_hidden)
8258 var items = this.getItems();
8260 items.each(function(f){
8266 var v = f.getValue();
8268 if (f.inputType =='radio') {
8269 if (typeof(ret[f.getName()]) == 'undefined') {
8270 ret[f.getName()] = ''; // empty..
8273 if (!f.el.dom.checked) {
8281 if(f.xtype == 'MoneyField'){
8282 ret[f.currencyName] = f.getCurrency();
8285 // not sure if this supported any more..
8286 if ((typeof(v) == 'object') && f.getRawValue) {
8287 v = f.getRawValue() ; // dates..
8289 // combo boxes where name != hiddenName...
8290 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8291 ret[f.name] = f.getRawValue();
8293 ret[f.getName()] = v;
8300 * Clears all invalid messages in this form.
8301 * @return {BasicForm} this
8303 clearInvalid : function(){
8304 var items = this.getItems();
8306 items.each(function(f){
8315 * @return {BasicForm} this
8318 var items = this.getItems();
8319 items.each(function(f){
8323 Roo.each(this.childForms || [], function (f) {
8331 getItems : function()
8333 var r=new Roo.util.MixedCollection(false, function(o){
8334 return o.id || (o.id = Roo.id());
8336 var iter = function(el) {
8343 Roo.each(el.items,function(e) {
8352 hideFields : function(items)
8354 Roo.each(items, function(i){
8356 var f = this.findField(i);
8367 showFields : function(items)
8369 Roo.each(items, function(i){
8371 var f = this.findField(i);
8384 Roo.apply(Roo.bootstrap.Form, {
8411 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8412 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8413 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8414 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8417 this.maskEl.top.enableDisplayMode("block");
8418 this.maskEl.left.enableDisplayMode("block");
8419 this.maskEl.bottom.enableDisplayMode("block");
8420 this.maskEl.right.enableDisplayMode("block");
8422 this.toolTip = new Roo.bootstrap.Tooltip({
8423 cls : 'roo-form-error-popover',
8425 'left' : ['r-l', [-2,0], 'right'],
8426 'right' : ['l-r', [2,0], 'left'],
8427 'bottom' : ['tl-bl', [0,2], 'top'],
8428 'top' : [ 'bl-tl', [0,-2], 'bottom']
8432 this.toolTip.render(Roo.get(document.body));
8434 this.toolTip.el.enableDisplayMode("block");
8436 Roo.get(document.body).on('click', function(){
8440 Roo.get(document.body).on('touchstart', function(){
8444 this.isApplied = true
8447 mask : function(form, target)
8451 this.target = target;
8453 if(!this.form.errorMask || !target.el){
8457 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8459 Roo.log(scrollable);
8461 var ot = this.target.el.calcOffsetsTo(scrollable);
8463 var scrollTo = ot[1] - this.form.maskOffset;
8465 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8467 scrollable.scrollTo('top', scrollTo);
8469 var box = this.target.el.getBox();
8471 var zIndex = Roo.bootstrap.Modal.zIndex++;
8474 this.maskEl.top.setStyle('position', 'absolute');
8475 this.maskEl.top.setStyle('z-index', zIndex);
8476 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8477 this.maskEl.top.setLeft(0);
8478 this.maskEl.top.setTop(0);
8479 this.maskEl.top.show();
8481 this.maskEl.left.setStyle('position', 'absolute');
8482 this.maskEl.left.setStyle('z-index', zIndex);
8483 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8484 this.maskEl.left.setLeft(0);
8485 this.maskEl.left.setTop(box.y - this.padding);
8486 this.maskEl.left.show();
8488 this.maskEl.bottom.setStyle('position', 'absolute');
8489 this.maskEl.bottom.setStyle('z-index', zIndex);
8490 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8491 this.maskEl.bottom.setLeft(0);
8492 this.maskEl.bottom.setTop(box.bottom + this.padding);
8493 this.maskEl.bottom.show();
8495 this.maskEl.right.setStyle('position', 'absolute');
8496 this.maskEl.right.setStyle('z-index', zIndex);
8497 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8498 this.maskEl.right.setLeft(box.right + this.padding);
8499 this.maskEl.right.setTop(box.y - this.padding);
8500 this.maskEl.right.show();
8502 this.toolTip.bindEl = this.target.el;
8504 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8506 var tip = this.target.blankText;
8508 if(this.target.getValue() !== '' ) {
8510 if (this.target.invalidText.length) {
8511 tip = this.target.invalidText;
8512 } else if (this.target.regexText.length){
8513 tip = this.target.regexText;
8517 this.toolTip.show(tip);
8519 this.intervalID = window.setInterval(function() {
8520 Roo.bootstrap.Form.popover.unmask();
8523 window.onwheel = function(){ return false;};
8525 (function(){ this.isMasked = true; }).defer(500, this);
8531 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8535 this.maskEl.top.setStyle('position', 'absolute');
8536 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8537 this.maskEl.top.hide();
8539 this.maskEl.left.setStyle('position', 'absolute');
8540 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8541 this.maskEl.left.hide();
8543 this.maskEl.bottom.setStyle('position', 'absolute');
8544 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8545 this.maskEl.bottom.hide();
8547 this.maskEl.right.setStyle('position', 'absolute');
8548 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8549 this.maskEl.right.hide();
8551 this.toolTip.hide();
8553 this.toolTip.el.hide();
8555 window.onwheel = function(){ return true;};
8557 if(this.intervalID){
8558 window.clearInterval(this.intervalID);
8559 this.intervalID = false;
8562 this.isMasked = false;
8572 * Ext JS Library 1.1.1
8573 * Copyright(c) 2006-2007, Ext JS, LLC.
8575 * Originally Released Under LGPL - original licence link has changed is not relivant.
8578 * <script type="text/javascript">
8581 * @class Roo.form.VTypes
8582 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8585 Roo.form.VTypes = function(){
8586 // closure these in so they are only created once.
8587 var alpha = /^[a-zA-Z_]+$/;
8588 var alphanum = /^[a-zA-Z0-9_]+$/;
8589 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8590 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8592 // All these messages and functions are configurable
8595 * The function used to validate email addresses
8596 * @param {String} value The email address
8598 'email' : function(v){
8599 return email.test(v);
8602 * The error text to display when the email validation function returns false
8605 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8607 * The keystroke filter mask to be applied on email input
8610 'emailMask' : /[a-z0-9_\.\-@]/i,
8613 * The function used to validate URLs
8614 * @param {String} value The URL
8616 'url' : function(v){
8620 * The error text to display when the url validation function returns false
8623 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8626 * The function used to validate alpha values
8627 * @param {String} value The value
8629 'alpha' : function(v){
8630 return alpha.test(v);
8633 * The error text to display when the alpha validation function returns false
8636 'alphaText' : 'This field should only contain letters and _',
8638 * The keystroke filter mask to be applied on alpha input
8641 'alphaMask' : /[a-z_]/i,
8644 * The function used to validate alphanumeric values
8645 * @param {String} value The value
8647 'alphanum' : function(v){
8648 return alphanum.test(v);
8651 * The error text to display when the alphanumeric validation function returns false
8654 'alphanumText' : 'This field should only contain letters, numbers and _',
8656 * The keystroke filter mask to be applied on alphanumeric input
8659 'alphanumMask' : /[a-z0-9_]/i
8669 * @class Roo.bootstrap.Input
8670 * @extends Roo.bootstrap.Component
8671 * Bootstrap Input class
8672 * @cfg {Boolean} disabled is it disabled
8673 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8674 * @cfg {String} name name of the input
8675 * @cfg {string} fieldLabel - the label associated
8676 * @cfg {string} placeholder - placeholder to put in text.
8677 * @cfg {string} before - input group add on before
8678 * @cfg {string} after - input group add on after
8679 * @cfg {string} size - (lg|sm) or leave empty..
8680 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8681 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8682 * @cfg {Number} md colspan out of 12 for computer-sized screens
8683 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8684 * @cfg {string} value default value of the input
8685 * @cfg {Number} labelWidth set the width of label
8686 * @cfg {Number} labellg set the width of label (1-12)
8687 * @cfg {Number} labelmd set the width of label (1-12)
8688 * @cfg {Number} labelsm set the width of label (1-12)
8689 * @cfg {Number} labelxs set the width of label (1-12)
8690 * @cfg {String} labelAlign (top|left)
8691 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8692 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8693 * @cfg {String} indicatorpos (left|right) default left
8694 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8695 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8697 * @cfg {String} align (left|center|right) Default left
8698 * @cfg {Boolean} forceFeedback (true|false) Default false
8701 * Create a new Input
8702 * @param {Object} config The config object
8705 Roo.bootstrap.Input = function(config){
8707 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8712 * Fires when this field receives input focus.
8713 * @param {Roo.form.Field} this
8718 * Fires when this field loses input focus.
8719 * @param {Roo.form.Field} this
8724 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8725 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8726 * @param {Roo.form.Field} this
8727 * @param {Roo.EventObject} e The event object
8732 * Fires just before the field blurs if the field value has changed.
8733 * @param {Roo.form.Field} this
8734 * @param {Mixed} newValue The new value
8735 * @param {Mixed} oldValue The original value
8740 * Fires after the field has been marked as invalid.
8741 * @param {Roo.form.Field} this
8742 * @param {String} msg The validation message
8747 * Fires after the field has been validated with no errors.
8748 * @param {Roo.form.Field} this
8753 * Fires after the key up
8754 * @param {Roo.form.Field} this
8755 * @param {Roo.EventObject} e The event Object
8761 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8763 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8764 automatic validation (defaults to "keyup").
8766 validationEvent : "keyup",
8768 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8770 validateOnBlur : true,
8772 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8774 validationDelay : 250,
8776 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8778 focusClass : "x-form-focus", // not needed???
8782 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8784 invalidClass : "has-warning",
8787 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8789 validClass : "has-success",
8792 * @cfg {Boolean} hasFeedback (true|false) default true
8797 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8799 invalidFeedbackClass : "glyphicon-warning-sign",
8802 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8804 validFeedbackClass : "glyphicon-ok",
8807 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8809 selectOnFocus : false,
8812 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8816 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8821 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8823 disableKeyFilter : false,
8826 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8830 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8834 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8836 blankText : "Please complete this mandatory field",
8839 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8843 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8845 maxLength : Number.MAX_VALUE,
8847 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8849 minLengthText : "The minimum length for this field is {0}",
8851 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8853 maxLengthText : "The maximum length for this field is {0}",
8857 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8858 * If available, this function will be called only after the basic validators all return true, and will be passed the
8859 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8863 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8864 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8865 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8869 * @cfg {String} regexText -- Depricated - use Invalid Text
8874 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8880 autocomplete: false,
8899 formatedValue : false,
8900 forceFeedback : false,
8902 indicatorpos : 'left',
8912 parentLabelAlign : function()
8915 while (parent.parent()) {
8916 parent = parent.parent();
8917 if (typeof(parent.labelAlign) !='undefined') {
8918 return parent.labelAlign;
8925 getAutoCreate : function()
8927 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8933 if(this.inputType != 'hidden'){
8934 cfg.cls = 'form-group' //input-group
8940 type : this.inputType,
8942 cls : 'form-control',
8943 placeholder : this.placeholder || '',
8944 autocomplete : this.autocomplete || 'new-password'
8947 if(this.capture.length){
8948 input.capture = this.capture;
8951 if(this.accept.length){
8952 input.accept = this.accept + "/*";
8956 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8959 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8960 input.maxLength = this.maxLength;
8963 if (this.disabled) {
8964 input.disabled=true;
8967 if (this.readOnly) {
8968 input.readonly=true;
8972 input.name = this.name;
8976 input.cls += ' input-' + this.size;
8980 ['xs','sm','md','lg'].map(function(size){
8981 if (settings[size]) {
8982 cfg.cls += ' col-' + size + '-' + settings[size];
8986 var inputblock = input;
8990 cls: 'glyphicon form-control-feedback'
8993 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8996 cls : 'has-feedback',
9004 if (this.before || this.after) {
9007 cls : 'input-group',
9011 if (this.before && typeof(this.before) == 'string') {
9013 inputblock.cn.push({
9015 cls : 'roo-input-before input-group-addon',
9019 if (this.before && typeof(this.before) == 'object') {
9020 this.before = Roo.factory(this.before);
9022 inputblock.cn.push({
9024 cls : 'roo-input-before input-group-' +
9025 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9029 inputblock.cn.push(input);
9031 if (this.after && typeof(this.after) == 'string') {
9032 inputblock.cn.push({
9034 cls : 'roo-input-after input-group-addon',
9038 if (this.after && typeof(this.after) == 'object') {
9039 this.after = Roo.factory(this.after);
9041 inputblock.cn.push({
9043 cls : 'roo-input-after input-group-' +
9044 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
9048 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9049 inputblock.cls += ' has-feedback';
9050 inputblock.cn.push(feedback);
9054 if (align ==='left' && this.fieldLabel.length) {
9056 cfg.cls += ' roo-form-group-label-left';
9061 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9062 tooltip : 'This field is required'
9067 cls : 'control-label',
9068 html : this.fieldLabel
9079 var labelCfg = cfg.cn[1];
9080 var contentCfg = cfg.cn[2];
9082 if(this.indicatorpos == 'right'){
9087 cls : 'control-label',
9091 html : this.fieldLabel
9095 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9096 tooltip : 'This field is required'
9109 labelCfg = cfg.cn[0];
9110 contentCfg = cfg.cn[1];
9114 if(this.labelWidth > 12){
9115 labelCfg.style = "width: " + this.labelWidth + 'px';
9118 if(this.labelWidth < 13 && this.labelmd == 0){
9119 this.labelmd = this.labelWidth;
9122 if(this.labellg > 0){
9123 labelCfg.cls += ' col-lg-' + this.labellg;
9124 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9127 if(this.labelmd > 0){
9128 labelCfg.cls += ' col-md-' + this.labelmd;
9129 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9132 if(this.labelsm > 0){
9133 labelCfg.cls += ' col-sm-' + this.labelsm;
9134 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9137 if(this.labelxs > 0){
9138 labelCfg.cls += ' col-xs-' + this.labelxs;
9139 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9143 } else if ( this.fieldLabel.length) {
9148 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9149 tooltip : 'This field is required'
9153 //cls : 'input-group-addon',
9154 html : this.fieldLabel
9162 if(this.indicatorpos == 'right'){
9167 //cls : 'input-group-addon',
9168 html : this.fieldLabel
9173 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9174 tooltip : 'This field is required'
9194 if (this.parentType === 'Navbar' && this.parent().bar) {
9195 cfg.cls += ' navbar-form';
9198 if (this.parentType === 'NavGroup') {
9199 cfg.cls += ' navbar-form';
9207 * return the real input element.
9209 inputEl: function ()
9211 return this.el.select('input.form-control',true).first();
9214 tooltipEl : function()
9216 return this.inputEl();
9219 indicatorEl : function()
9221 var indicator = this.el.select('i.roo-required-indicator',true).first();
9231 setDisabled : function(v)
9233 var i = this.inputEl().dom;
9235 i.removeAttribute('disabled');
9239 i.setAttribute('disabled','true');
9241 initEvents : function()
9244 this.inputEl().on("keydown" , this.fireKey, this);
9245 this.inputEl().on("focus", this.onFocus, this);
9246 this.inputEl().on("blur", this.onBlur, this);
9248 this.inputEl().relayEvent('keyup', this);
9250 this.indicator = this.indicatorEl();
9253 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9256 // reference to original value for reset
9257 this.originalValue = this.getValue();
9258 //Roo.form.TextField.superclass.initEvents.call(this);
9259 if(this.validationEvent == 'keyup'){
9260 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9261 this.inputEl().on('keyup', this.filterValidation, this);
9263 else if(this.validationEvent !== false){
9264 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9267 if(this.selectOnFocus){
9268 this.on("focus", this.preFocus, this);
9271 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9272 this.inputEl().on("keypress", this.filterKeys, this);
9274 this.inputEl().relayEvent('keypress', this);
9277 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9278 this.el.on("click", this.autoSize, this);
9281 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9282 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9285 if (typeof(this.before) == 'object') {
9286 this.before.render(this.el.select('.roo-input-before',true).first());
9288 if (typeof(this.after) == 'object') {
9289 this.after.render(this.el.select('.roo-input-after',true).first());
9292 this.inputEl().on('change', this.onChange, this);
9295 filterValidation : function(e){
9296 if(!e.isNavKeyPress()){
9297 this.validationTask.delay(this.validationDelay);
9301 * Validates the field value
9302 * @return {Boolean} True if the value is valid, else false
9304 validate : function(){
9305 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9306 if(this.disabled || this.validateValue(this.getRawValue())){
9317 * Validates a value according to the field's validation rules and marks the field as invalid
9318 * if the validation fails
9319 * @param {Mixed} value The value to validate
9320 * @return {Boolean} True if the value is valid, else false
9322 validateValue : function(value)
9324 if(this.getVisibilityEl().hasClass('hidden')){
9328 if(value.length < 1) { // if it's blank
9329 if(this.allowBlank){
9335 if(value.length < this.minLength){
9338 if(value.length > this.maxLength){
9342 var vt = Roo.form.VTypes;
9343 if(!vt[this.vtype](value, this)){
9347 if(typeof this.validator == "function"){
9348 var msg = this.validator(value);
9352 if (typeof(msg) == 'string') {
9353 this.invalidText = msg;
9357 if(this.regex && !this.regex.test(value)){
9365 fireKey : function(e){
9366 //Roo.log('field ' + e.getKey());
9367 if(e.isNavKeyPress()){
9368 this.fireEvent("specialkey", this, e);
9371 focus : function (selectText){
9373 this.inputEl().focus();
9374 if(selectText === true){
9375 this.inputEl().dom.select();
9381 onFocus : function(){
9382 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9383 // this.el.addClass(this.focusClass);
9386 this.hasFocus = true;
9387 this.startValue = this.getValue();
9388 this.fireEvent("focus", this);
9392 beforeBlur : Roo.emptyFn,
9396 onBlur : function(){
9398 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9399 //this.el.removeClass(this.focusClass);
9401 this.hasFocus = false;
9402 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9405 var v = this.getValue();
9406 if(String(v) !== String(this.startValue)){
9407 this.fireEvent('change', this, v, this.startValue);
9409 this.fireEvent("blur", this);
9412 onChange : function(e)
9414 var v = this.getValue();
9415 if(String(v) !== String(this.startValue)){
9416 this.fireEvent('change', this, v, this.startValue);
9422 * Resets the current field value to the originally loaded value and clears any validation messages
9425 this.setValue(this.originalValue);
9429 * Returns the name of the field
9430 * @return {Mixed} name The name field
9432 getName: function(){
9436 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9437 * @return {Mixed} value The field value
9439 getValue : function(){
9441 var v = this.inputEl().getValue();
9446 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9447 * @return {Mixed} value The field value
9449 getRawValue : function(){
9450 var v = this.inputEl().getValue();
9456 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9457 * @param {Mixed} value The value to set
9459 setRawValue : function(v){
9460 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9463 selectText : function(start, end){
9464 var v = this.getRawValue();
9466 start = start === undefined ? 0 : start;
9467 end = end === undefined ? v.length : end;
9468 var d = this.inputEl().dom;
9469 if(d.setSelectionRange){
9470 d.setSelectionRange(start, end);
9471 }else if(d.createTextRange){
9472 var range = d.createTextRange();
9473 range.moveStart("character", start);
9474 range.moveEnd("character", v.length-end);
9481 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9482 * @param {Mixed} value The value to set
9484 setValue : function(v){
9487 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9493 processValue : function(value){
9494 if(this.stripCharsRe){
9495 var newValue = value.replace(this.stripCharsRe, '');
9496 if(newValue !== value){
9497 this.setRawValue(newValue);
9504 preFocus : function(){
9506 if(this.selectOnFocus){
9507 this.inputEl().dom.select();
9510 filterKeys : function(e){
9512 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9515 var c = e.getCharCode(), cc = String.fromCharCode(c);
9516 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9519 if(!this.maskRe.test(cc)){
9524 * Clear any invalid styles/messages for this field
9526 clearInvalid : function(){
9528 if(!this.el || this.preventMark){ // not rendered
9533 this.el.removeClass(this.invalidClass);
9535 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9537 var feedback = this.el.select('.form-control-feedback', true).first();
9540 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9546 this.indicator.removeClass('visible');
9547 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9550 this.fireEvent('valid', this);
9554 * Mark this field as valid
9556 markValid : function()
9558 if(!this.el || this.preventMark){ // not rendered...
9562 this.el.removeClass([this.invalidClass, this.validClass]);
9564 var feedback = this.el.select('.form-control-feedback', true).first();
9567 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9571 this.indicator.removeClass('visible');
9572 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9579 if(this.allowBlank && !this.getRawValue().length){
9583 this.el.addClass(this.validClass);
9585 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9587 var feedback = this.el.select('.form-control-feedback', true).first();
9590 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9591 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9596 this.fireEvent('valid', this);
9600 * Mark this field as invalid
9601 * @param {String} msg The validation message
9603 markInvalid : function(msg)
9605 if(!this.el || this.preventMark){ // not rendered
9609 this.el.removeClass([this.invalidClass, this.validClass]);
9611 var feedback = this.el.select('.form-control-feedback', true).first();
9614 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9621 if(this.allowBlank && !this.getRawValue().length){
9626 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9627 this.indicator.addClass('visible');
9630 this.el.addClass(this.invalidClass);
9632 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9634 var feedback = this.el.select('.form-control-feedback', true).first();
9637 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9639 if(this.getValue().length || this.forceFeedback){
9640 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9647 this.fireEvent('invalid', this, msg);
9650 SafariOnKeyDown : function(event)
9652 // this is a workaround for a password hang bug on chrome/ webkit.
9653 if (this.inputEl().dom.type != 'password') {
9657 var isSelectAll = false;
9659 if(this.inputEl().dom.selectionEnd > 0){
9660 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9662 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9663 event.preventDefault();
9668 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9670 event.preventDefault();
9671 // this is very hacky as keydown always get's upper case.
9673 var cc = String.fromCharCode(event.getCharCode());
9674 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9678 adjustWidth : function(tag, w){
9679 tag = tag.toLowerCase();
9680 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9681 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9685 if(tag == 'textarea'){
9688 }else if(Roo.isOpera){
9692 if(tag == 'textarea'){
9700 setFieldLabel : function(v)
9707 var ar = this.el.select('label > span',true);
9709 if (ar.elements.length) {
9710 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9711 this.fieldLabel = v;
9715 var br = this.el.select('label',true);
9717 if(br.elements.length) {
9718 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9719 this.fieldLabel = v;
9723 Roo.log('Cannot Found any of label > span || label in input');
9727 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9728 this.fieldLabel = v;
9743 * @class Roo.bootstrap.TextArea
9744 * @extends Roo.bootstrap.Input
9745 * Bootstrap TextArea class
9746 * @cfg {Number} cols Specifies the visible width of a text area
9747 * @cfg {Number} rows Specifies the visible number of lines in a text area
9748 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9749 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9750 * @cfg {string} html text
9753 * Create a new TextArea
9754 * @param {Object} config The config object
9757 Roo.bootstrap.TextArea = function(config){
9758 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9762 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9772 getAutoCreate : function(){
9774 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9780 if(this.inputType != 'hidden'){
9781 cfg.cls = 'form-group' //input-group
9789 value : this.value || '',
9790 html: this.html || '',
9791 cls : 'form-control',
9792 placeholder : this.placeholder || ''
9796 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9797 input.maxLength = this.maxLength;
9801 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9805 input.cols = this.cols;
9808 if (this.readOnly) {
9809 input.readonly = true;
9813 input.name = this.name;
9817 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9821 ['xs','sm','md','lg'].map(function(size){
9822 if (settings[size]) {
9823 cfg.cls += ' col-' + size + '-' + settings[size];
9827 var inputblock = input;
9829 if(this.hasFeedback && !this.allowBlank){
9833 cls: 'glyphicon form-control-feedback'
9837 cls : 'has-feedback',
9846 if (this.before || this.after) {
9849 cls : 'input-group',
9853 inputblock.cn.push({
9855 cls : 'input-group-addon',
9860 inputblock.cn.push(input);
9862 if(this.hasFeedback && !this.allowBlank){
9863 inputblock.cls += ' has-feedback';
9864 inputblock.cn.push(feedback);
9868 inputblock.cn.push({
9870 cls : 'input-group-addon',
9877 if (align ==='left' && this.fieldLabel.length) {
9882 cls : 'control-label',
9883 html : this.fieldLabel
9894 if(this.labelWidth > 12){
9895 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9898 if(this.labelWidth < 13 && this.labelmd == 0){
9899 this.labelmd = this.labelWidth;
9902 if(this.labellg > 0){
9903 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9904 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9907 if(this.labelmd > 0){
9908 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9909 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9912 if(this.labelsm > 0){
9913 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9914 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9917 if(this.labelxs > 0){
9918 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9919 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9922 } else if ( this.fieldLabel.length) {
9927 //cls : 'input-group-addon',
9928 html : this.fieldLabel
9946 if (this.disabled) {
9947 input.disabled=true;
9954 * return the real textarea element.
9956 inputEl: function ()
9958 return this.el.select('textarea.form-control',true).first();
9962 * Clear any invalid styles/messages for this field
9964 clearInvalid : function()
9967 if(!this.el || this.preventMark){ // not rendered
9971 var label = this.el.select('label', true).first();
9972 var icon = this.el.select('i.fa-star', true).first();
9978 this.el.removeClass(this.invalidClass);
9980 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9982 var feedback = this.el.select('.form-control-feedback', true).first();
9985 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9990 this.fireEvent('valid', this);
9994 * Mark this field as valid
9996 markValid : function()
9998 if(!this.el || this.preventMark){ // not rendered
10002 this.el.removeClass([this.invalidClass, this.validClass]);
10004 var feedback = this.el.select('.form-control-feedback', true).first();
10007 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10010 if(this.disabled || this.allowBlank){
10014 var label = this.el.select('label', true).first();
10015 var icon = this.el.select('i.fa-star', true).first();
10021 this.el.addClass(this.validClass);
10023 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10025 var feedback = this.el.select('.form-control-feedback', true).first();
10028 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10029 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10034 this.fireEvent('valid', this);
10038 * Mark this field as invalid
10039 * @param {String} msg The validation message
10041 markInvalid : function(msg)
10043 if(!this.el || this.preventMark){ // not rendered
10047 this.el.removeClass([this.invalidClass, this.validClass]);
10049 var feedback = this.el.select('.form-control-feedback', true).first();
10052 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10055 if(this.disabled || this.allowBlank){
10059 var label = this.el.select('label', true).first();
10060 var icon = this.el.select('i.fa-star', true).first();
10062 if(!this.getValue().length && label && !icon){
10063 this.el.createChild({
10065 cls : 'text-danger fa fa-lg fa-star',
10066 tooltip : 'This field is required',
10067 style : 'margin-right:5px;'
10071 this.el.addClass(this.invalidClass);
10073 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10075 var feedback = this.el.select('.form-control-feedback', true).first();
10078 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10080 if(this.getValue().length || this.forceFeedback){
10081 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10088 this.fireEvent('invalid', this, msg);
10096 * trigger field - base class for combo..
10101 * @class Roo.bootstrap.TriggerField
10102 * @extends Roo.bootstrap.Input
10103 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10104 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10105 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10106 * for which you can provide a custom implementation. For example:
10108 var trigger = new Roo.bootstrap.TriggerField();
10109 trigger.onTriggerClick = myTriggerFn;
10110 trigger.applyTo('my-field');
10113 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10114 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10115 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10116 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10117 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10120 * Create a new TriggerField.
10121 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10122 * to the base TextField)
10124 Roo.bootstrap.TriggerField = function(config){
10125 this.mimicing = false;
10126 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10129 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10131 * @cfg {String} triggerClass A CSS class to apply to the trigger
10134 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10139 * @cfg {Boolean} removable (true|false) special filter default false
10143 /** @cfg {Boolean} grow @hide */
10144 /** @cfg {Number} growMin @hide */
10145 /** @cfg {Number} growMax @hide */
10151 autoSize: Roo.emptyFn,
10155 deferHeight : true,
10158 actionMode : 'wrap',
10163 getAutoCreate : function(){
10165 var align = this.labelAlign || this.parentLabelAlign();
10170 cls: 'form-group' //input-group
10177 type : this.inputType,
10178 cls : 'form-control',
10179 autocomplete: 'new-password',
10180 placeholder : this.placeholder || ''
10184 input.name = this.name;
10187 input.cls += ' input-' + this.size;
10190 if (this.disabled) {
10191 input.disabled=true;
10194 var inputblock = input;
10196 if(this.hasFeedback && !this.allowBlank){
10200 cls: 'glyphicon form-control-feedback'
10203 if(this.removable && !this.editable && !this.tickable){
10205 cls : 'has-feedback',
10211 cls : 'roo-combo-removable-btn close'
10218 cls : 'has-feedback',
10227 if(this.removable && !this.editable && !this.tickable){
10229 cls : 'roo-removable',
10235 cls : 'roo-combo-removable-btn close'
10242 if (this.before || this.after) {
10245 cls : 'input-group',
10249 inputblock.cn.push({
10251 cls : 'input-group-addon',
10256 inputblock.cn.push(input);
10258 if(this.hasFeedback && !this.allowBlank){
10259 inputblock.cls += ' has-feedback';
10260 inputblock.cn.push(feedback);
10264 inputblock.cn.push({
10266 cls : 'input-group-addon',
10279 cls: 'form-hidden-field'
10293 cls: 'form-hidden-field'
10297 cls: 'roo-select2-choices',
10301 cls: 'roo-select2-search-field',
10314 cls: 'roo-select2-container input-group',
10319 // cls: 'typeahead typeahead-long dropdown-menu',
10320 // style: 'display:none'
10325 if(!this.multiple && this.showToggleBtn){
10331 if (this.caret != false) {
10334 cls: 'fa fa-' + this.caret
10341 cls : 'input-group-addon btn dropdown-toggle',
10346 cls: 'combobox-clear',
10360 combobox.cls += ' roo-select2-container-multi';
10363 if (align ==='left' && this.fieldLabel.length) {
10365 cfg.cls += ' roo-form-group-label-left';
10370 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10371 tooltip : 'This field is required'
10376 cls : 'control-label',
10377 html : this.fieldLabel
10389 var labelCfg = cfg.cn[1];
10390 var contentCfg = cfg.cn[2];
10392 if(this.indicatorpos == 'right'){
10397 cls : 'control-label',
10401 html : this.fieldLabel
10405 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10406 tooltip : 'This field is required'
10419 labelCfg = cfg.cn[0];
10420 contentCfg = cfg.cn[1];
10423 if(this.labelWidth > 12){
10424 labelCfg.style = "width: " + this.labelWidth + 'px';
10427 if(this.labelWidth < 13 && this.labelmd == 0){
10428 this.labelmd = this.labelWidth;
10431 if(this.labellg > 0){
10432 labelCfg.cls += ' col-lg-' + this.labellg;
10433 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10436 if(this.labelmd > 0){
10437 labelCfg.cls += ' col-md-' + this.labelmd;
10438 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10441 if(this.labelsm > 0){
10442 labelCfg.cls += ' col-sm-' + this.labelsm;
10443 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10446 if(this.labelxs > 0){
10447 labelCfg.cls += ' col-xs-' + this.labelxs;
10448 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10451 } else if ( this.fieldLabel.length) {
10452 // Roo.log(" label");
10456 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10457 tooltip : 'This field is required'
10461 //cls : 'input-group-addon',
10462 html : this.fieldLabel
10470 if(this.indicatorpos == 'right'){
10478 html : this.fieldLabel
10482 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10483 tooltip : 'This field is required'
10496 // Roo.log(" no label && no align");
10503 ['xs','sm','md','lg'].map(function(size){
10504 if (settings[size]) {
10505 cfg.cls += ' col-' + size + '-' + settings[size];
10516 onResize : function(w, h){
10517 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10518 // if(typeof w == 'number'){
10519 // var x = w - this.trigger.getWidth();
10520 // this.inputEl().setWidth(this.adjustWidth('input', x));
10521 // this.trigger.setStyle('left', x+'px');
10526 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10529 getResizeEl : function(){
10530 return this.inputEl();
10534 getPositionEl : function(){
10535 return this.inputEl();
10539 alignErrorIcon : function(){
10540 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10544 initEvents : function(){
10548 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10549 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10550 if(!this.multiple && this.showToggleBtn){
10551 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10552 if(this.hideTrigger){
10553 this.trigger.setDisplayed(false);
10555 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10559 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10562 if(this.removable && !this.editable && !this.tickable){
10563 var close = this.closeTriggerEl();
10566 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10567 close.on('click', this.removeBtnClick, this, close);
10571 //this.trigger.addClassOnOver('x-form-trigger-over');
10572 //this.trigger.addClassOnClick('x-form-trigger-click');
10575 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10579 closeTriggerEl : function()
10581 var close = this.el.select('.roo-combo-removable-btn', true).first();
10582 return close ? close : false;
10585 removeBtnClick : function(e, h, el)
10587 e.preventDefault();
10589 if(this.fireEvent("remove", this) !== false){
10591 this.fireEvent("afterremove", this)
10595 createList : function()
10597 this.list = Roo.get(document.body).createChild({
10599 cls: 'typeahead typeahead-long dropdown-menu',
10600 style: 'display:none'
10603 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10608 initTrigger : function(){
10613 onDestroy : function(){
10615 this.trigger.removeAllListeners();
10616 // this.trigger.remove();
10619 // this.wrap.remove();
10621 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10625 onFocus : function(){
10626 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10628 if(!this.mimicing){
10629 this.wrap.addClass('x-trigger-wrap-focus');
10630 this.mimicing = true;
10631 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10632 if(this.monitorTab){
10633 this.el.on("keydown", this.checkTab, this);
10640 checkTab : function(e){
10641 if(e.getKey() == e.TAB){
10642 this.triggerBlur();
10647 onBlur : function(){
10652 mimicBlur : function(e, t){
10654 if(!this.wrap.contains(t) && this.validateBlur()){
10655 this.triggerBlur();
10661 triggerBlur : function(){
10662 this.mimicing = false;
10663 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10664 if(this.monitorTab){
10665 this.el.un("keydown", this.checkTab, this);
10667 //this.wrap.removeClass('x-trigger-wrap-focus');
10668 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10672 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10673 validateBlur : function(e, t){
10678 onDisable : function(){
10679 this.inputEl().dom.disabled = true;
10680 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10682 // this.wrap.addClass('x-item-disabled');
10687 onEnable : function(){
10688 this.inputEl().dom.disabled = false;
10689 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10691 // this.el.removeClass('x-item-disabled');
10696 onShow : function(){
10697 var ae = this.getActionEl();
10700 ae.dom.style.display = '';
10701 ae.dom.style.visibility = 'visible';
10707 onHide : function(){
10708 var ae = this.getActionEl();
10709 ae.dom.style.display = 'none';
10713 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10714 * by an implementing function.
10716 * @param {EventObject} e
10718 onTriggerClick : Roo.emptyFn
10722 * Ext JS Library 1.1.1
10723 * Copyright(c) 2006-2007, Ext JS, LLC.
10725 * Originally Released Under LGPL - original licence link has changed is not relivant.
10728 * <script type="text/javascript">
10733 * @class Roo.data.SortTypes
10735 * Defines the default sorting (casting?) comparison functions used when sorting data.
10737 Roo.data.SortTypes = {
10739 * Default sort that does nothing
10740 * @param {Mixed} s The value being converted
10741 * @return {Mixed} The comparison value
10743 none : function(s){
10748 * The regular expression used to strip tags
10752 stripTagsRE : /<\/?[^>]+>/gi,
10755 * Strips all HTML tags to sort on text only
10756 * @param {Mixed} s The value being converted
10757 * @return {String} The comparison value
10759 asText : function(s){
10760 return String(s).replace(this.stripTagsRE, "");
10764 * Strips all HTML tags to sort on text only - Case insensitive
10765 * @param {Mixed} s The value being converted
10766 * @return {String} The comparison value
10768 asUCText : function(s){
10769 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10773 * Case insensitive string
10774 * @param {Mixed} s The value being converted
10775 * @return {String} The comparison value
10777 asUCString : function(s) {
10778 return String(s).toUpperCase();
10783 * @param {Mixed} s The value being converted
10784 * @return {Number} The comparison value
10786 asDate : function(s) {
10790 if(s instanceof Date){
10791 return s.getTime();
10793 return Date.parse(String(s));
10798 * @param {Mixed} s The value being converted
10799 * @return {Float} The comparison value
10801 asFloat : function(s) {
10802 var val = parseFloat(String(s).replace(/,/g, ""));
10811 * @param {Mixed} s The value being converted
10812 * @return {Number} The comparison value
10814 asInt : function(s) {
10815 var val = parseInt(String(s).replace(/,/g, ""));
10823 * Ext JS Library 1.1.1
10824 * Copyright(c) 2006-2007, Ext JS, LLC.
10826 * Originally Released Under LGPL - original licence link has changed is not relivant.
10829 * <script type="text/javascript">
10833 * @class Roo.data.Record
10834 * Instances of this class encapsulate both record <em>definition</em> information, and record
10835 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10836 * to access Records cached in an {@link Roo.data.Store} object.<br>
10838 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10839 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10842 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10844 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10845 * {@link #create}. The parameters are the same.
10846 * @param {Array} data An associative Array of data values keyed by the field name.
10847 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10848 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10849 * not specified an integer id is generated.
10851 Roo.data.Record = function(data, id){
10852 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10857 * Generate a constructor for a specific record layout.
10858 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10859 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10860 * Each field definition object may contain the following properties: <ul>
10861 * <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,
10862 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10863 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10864 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10865 * is being used, then this is a string containing the javascript expression to reference the data relative to
10866 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10867 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10868 * this may be omitted.</p></li>
10869 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10870 * <ul><li>auto (Default, implies no conversion)</li>
10875 * <li>date</li></ul></p></li>
10876 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10877 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10878 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10879 * by the Reader into an object that will be stored in the Record. It is passed the
10880 * following parameters:<ul>
10881 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10883 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10885 * <br>usage:<br><pre><code>
10886 var TopicRecord = Roo.data.Record.create(
10887 {name: 'title', mapping: 'topic_title'},
10888 {name: 'author', mapping: 'username'},
10889 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10890 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10891 {name: 'lastPoster', mapping: 'user2'},
10892 {name: 'excerpt', mapping: 'post_text'}
10895 var myNewRecord = new TopicRecord({
10896 title: 'Do my job please',
10899 lastPost: new Date(),
10900 lastPoster: 'Animal',
10901 excerpt: 'No way dude!'
10903 myStore.add(myNewRecord);
10908 Roo.data.Record.create = function(o){
10909 var f = function(){
10910 f.superclass.constructor.apply(this, arguments);
10912 Roo.extend(f, Roo.data.Record);
10913 var p = f.prototype;
10914 p.fields = new Roo.util.MixedCollection(false, function(field){
10917 for(var i = 0, len = o.length; i < len; i++){
10918 p.fields.add(new Roo.data.Field(o[i]));
10920 f.getField = function(name){
10921 return p.fields.get(name);
10926 Roo.data.Record.AUTO_ID = 1000;
10927 Roo.data.Record.EDIT = 'edit';
10928 Roo.data.Record.REJECT = 'reject';
10929 Roo.data.Record.COMMIT = 'commit';
10931 Roo.data.Record.prototype = {
10933 * Readonly flag - true if this record has been modified.
10942 join : function(store){
10943 this.store = store;
10947 * Set the named field to the specified value.
10948 * @param {String} name The name of the field to set.
10949 * @param {Object} value The value to set the field to.
10951 set : function(name, value){
10952 if(this.data[name] == value){
10956 if(!this.modified){
10957 this.modified = {};
10959 if(typeof this.modified[name] == 'undefined'){
10960 this.modified[name] = this.data[name];
10962 this.data[name] = value;
10963 if(!this.editing && this.store){
10964 this.store.afterEdit(this);
10969 * Get the value of the named field.
10970 * @param {String} name The name of the field to get the value of.
10971 * @return {Object} The value of the field.
10973 get : function(name){
10974 return this.data[name];
10978 beginEdit : function(){
10979 this.editing = true;
10980 this.modified = {};
10984 cancelEdit : function(){
10985 this.editing = false;
10986 delete this.modified;
10990 endEdit : function(){
10991 this.editing = false;
10992 if(this.dirty && this.store){
10993 this.store.afterEdit(this);
10998 * Usually called by the {@link Roo.data.Store} which owns the Record.
10999 * Rejects all changes made to the Record since either creation, or the last commit operation.
11000 * Modified fields are reverted to their original values.
11002 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11003 * of reject operations.
11005 reject : function(){
11006 var m = this.modified;
11008 if(typeof m[n] != "function"){
11009 this.data[n] = m[n];
11012 this.dirty = false;
11013 delete this.modified;
11014 this.editing = false;
11016 this.store.afterReject(this);
11021 * Usually called by the {@link Roo.data.Store} which owns the Record.
11022 * Commits all changes made to the Record since either creation, or the last commit operation.
11024 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11025 * of commit operations.
11027 commit : function(){
11028 this.dirty = false;
11029 delete this.modified;
11030 this.editing = false;
11032 this.store.afterCommit(this);
11037 hasError : function(){
11038 return this.error != null;
11042 clearError : function(){
11047 * Creates a copy of this record.
11048 * @param {String} id (optional) A new record id if you don't want to use this record's id
11051 copy : function(newId) {
11052 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11056 * Ext JS Library 1.1.1
11057 * Copyright(c) 2006-2007, Ext JS, LLC.
11059 * Originally Released Under LGPL - original licence link has changed is not relivant.
11062 * <script type="text/javascript">
11068 * @class Roo.data.Store
11069 * @extends Roo.util.Observable
11070 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11071 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11073 * 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
11074 * has no knowledge of the format of the data returned by the Proxy.<br>
11076 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11077 * instances from the data object. These records are cached and made available through accessor functions.
11079 * Creates a new Store.
11080 * @param {Object} config A config object containing the objects needed for the Store to access data,
11081 * and read the data into Records.
11083 Roo.data.Store = function(config){
11084 this.data = new Roo.util.MixedCollection(false);
11085 this.data.getKey = function(o){
11088 this.baseParams = {};
11090 this.paramNames = {
11095 "multisort" : "_multisort"
11098 if(config && config.data){
11099 this.inlineData = config.data;
11100 delete config.data;
11103 Roo.apply(this, config);
11105 if(this.reader){ // reader passed
11106 this.reader = Roo.factory(this.reader, Roo.data);
11107 this.reader.xmodule = this.xmodule || false;
11108 if(!this.recordType){
11109 this.recordType = this.reader.recordType;
11111 if(this.reader.onMetaChange){
11112 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11116 if(this.recordType){
11117 this.fields = this.recordType.prototype.fields;
11119 this.modified = [];
11123 * @event datachanged
11124 * Fires when the data cache has changed, and a widget which is using this Store
11125 * as a Record cache should refresh its view.
11126 * @param {Store} this
11128 datachanged : true,
11130 * @event metachange
11131 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11132 * @param {Store} this
11133 * @param {Object} meta The JSON metadata
11138 * Fires when Records have been added to the Store
11139 * @param {Store} this
11140 * @param {Roo.data.Record[]} records The array of Records added
11141 * @param {Number} index The index at which the record(s) were added
11146 * Fires when a Record has been removed from the Store
11147 * @param {Store} this
11148 * @param {Roo.data.Record} record The Record that was removed
11149 * @param {Number} index The index at which the record was removed
11154 * Fires when a Record has been updated
11155 * @param {Store} this
11156 * @param {Roo.data.Record} record The Record that was updated
11157 * @param {String} operation The update operation being performed. Value may be one of:
11159 Roo.data.Record.EDIT
11160 Roo.data.Record.REJECT
11161 Roo.data.Record.COMMIT
11167 * Fires when the data cache has been cleared.
11168 * @param {Store} this
11172 * @event beforeload
11173 * Fires before a request is made for a new data object. If the beforeload handler returns false
11174 * the load action will be canceled.
11175 * @param {Store} this
11176 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11180 * @event beforeloadadd
11181 * Fires after a new set of Records has been loaded.
11182 * @param {Store} this
11183 * @param {Roo.data.Record[]} records The Records that were loaded
11184 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11186 beforeloadadd : true,
11189 * Fires after a new set of Records has been loaded, before they are added to the store.
11190 * @param {Store} this
11191 * @param {Roo.data.Record[]} records The Records that were loaded
11192 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11193 * @params {Object} return from reader
11197 * @event loadexception
11198 * Fires if an exception occurs in the Proxy during loading.
11199 * Called with the signature of the Proxy's "loadexception" event.
11200 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11203 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11204 * @param {Object} load options
11205 * @param {Object} jsonData from your request (normally this contains the Exception)
11207 loadexception : true
11211 this.proxy = Roo.factory(this.proxy, Roo.data);
11212 this.proxy.xmodule = this.xmodule || false;
11213 this.relayEvents(this.proxy, ["loadexception"]);
11215 this.sortToggle = {};
11216 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11218 Roo.data.Store.superclass.constructor.call(this);
11220 if(this.inlineData){
11221 this.loadData(this.inlineData);
11222 delete this.inlineData;
11226 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11228 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11229 * without a remote query - used by combo/forms at present.
11233 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11236 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11239 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11240 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11243 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11244 * on any HTTP request
11247 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11250 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11254 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11255 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11257 remoteSort : false,
11260 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11261 * loaded or when a record is removed. (defaults to false).
11263 pruneModifiedRecords : false,
11266 lastOptions : null,
11269 * Add Records to the Store and fires the add event.
11270 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11272 add : function(records){
11273 records = [].concat(records);
11274 for(var i = 0, len = records.length; i < len; i++){
11275 records[i].join(this);
11277 var index = this.data.length;
11278 this.data.addAll(records);
11279 this.fireEvent("add", this, records, index);
11283 * Remove a Record from the Store and fires the remove event.
11284 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11286 remove : function(record){
11287 var index = this.data.indexOf(record);
11288 this.data.removeAt(index);
11290 if(this.pruneModifiedRecords){
11291 this.modified.remove(record);
11293 this.fireEvent("remove", this, record, index);
11297 * Remove all Records from the Store and fires the clear event.
11299 removeAll : function(){
11301 if(this.pruneModifiedRecords){
11302 this.modified = [];
11304 this.fireEvent("clear", this);
11308 * Inserts Records to the Store at the given index and fires the add event.
11309 * @param {Number} index The start index at which to insert the passed Records.
11310 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11312 insert : function(index, records){
11313 records = [].concat(records);
11314 for(var i = 0, len = records.length; i < len; i++){
11315 this.data.insert(index, records[i]);
11316 records[i].join(this);
11318 this.fireEvent("add", this, records, index);
11322 * Get the index within the cache of the passed Record.
11323 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11324 * @return {Number} The index of the passed Record. Returns -1 if not found.
11326 indexOf : function(record){
11327 return this.data.indexOf(record);
11331 * Get the index within the cache of the Record with the passed id.
11332 * @param {String} id The id of the Record to find.
11333 * @return {Number} The index of the Record. Returns -1 if not found.
11335 indexOfId : function(id){
11336 return this.data.indexOfKey(id);
11340 * Get the Record with the specified id.
11341 * @param {String} id The id of the Record to find.
11342 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11344 getById : function(id){
11345 return this.data.key(id);
11349 * Get the Record at the specified index.
11350 * @param {Number} index The index of the Record to find.
11351 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11353 getAt : function(index){
11354 return this.data.itemAt(index);
11358 * Returns a range of Records between specified indices.
11359 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11360 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11361 * @return {Roo.data.Record[]} An array of Records
11363 getRange : function(start, end){
11364 return this.data.getRange(start, end);
11368 storeOptions : function(o){
11369 o = Roo.apply({}, o);
11372 this.lastOptions = o;
11376 * Loads the Record cache from the configured Proxy using the configured Reader.
11378 * If using remote paging, then the first load call must specify the <em>start</em>
11379 * and <em>limit</em> properties in the options.params property to establish the initial
11380 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11382 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11383 * and this call will return before the new data has been loaded. Perform any post-processing
11384 * in a callback function, or in a "load" event handler.</strong>
11386 * @param {Object} options An object containing properties which control loading options:<ul>
11387 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11388 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11389 * passed the following arguments:<ul>
11390 * <li>r : Roo.data.Record[]</li>
11391 * <li>options: Options object from the load call</li>
11392 * <li>success: Boolean success indicator</li></ul></li>
11393 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11394 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11397 load : function(options){
11398 options = options || {};
11399 if(this.fireEvent("beforeload", this, options) !== false){
11400 this.storeOptions(options);
11401 var p = Roo.apply(options.params || {}, this.baseParams);
11402 // if meta was not loaded from remote source.. try requesting it.
11403 if (!this.reader.metaFromRemote) {
11404 p._requestMeta = 1;
11406 if(this.sortInfo && this.remoteSort){
11407 var pn = this.paramNames;
11408 p[pn["sort"]] = this.sortInfo.field;
11409 p[pn["dir"]] = this.sortInfo.direction;
11411 if (this.multiSort) {
11412 var pn = this.paramNames;
11413 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11416 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11421 * Reloads the Record cache from the configured Proxy using the configured Reader and
11422 * the options from the last load operation performed.
11423 * @param {Object} options (optional) An object containing properties which may override the options
11424 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11425 * the most recently used options are reused).
11427 reload : function(options){
11428 this.load(Roo.applyIf(options||{}, this.lastOptions));
11432 // Called as a callback by the Reader during a load operation.
11433 loadRecords : function(o, options, success){
11434 if(!o || success === false){
11435 if(success !== false){
11436 this.fireEvent("load", this, [], options, o);
11438 if(options.callback){
11439 options.callback.call(options.scope || this, [], options, false);
11443 // if data returned failure - throw an exception.
11444 if (o.success === false) {
11445 // show a message if no listener is registered.
11446 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11447 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11449 // loadmask wil be hooked into this..
11450 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11453 var r = o.records, t = o.totalRecords || r.length;
11455 this.fireEvent("beforeloadadd", this, r, options, o);
11457 if(!options || options.add !== true){
11458 if(this.pruneModifiedRecords){
11459 this.modified = [];
11461 for(var i = 0, len = r.length; i < len; i++){
11465 this.data = this.snapshot;
11466 delete this.snapshot;
11469 this.data.addAll(r);
11470 this.totalLength = t;
11472 this.fireEvent("datachanged", this);
11474 this.totalLength = Math.max(t, this.data.length+r.length);
11478 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11480 var e = new Roo.data.Record({});
11482 e.set(this.parent.displayField, this.parent.emptyTitle);
11483 e.set(this.parent.valueField, '');
11488 this.fireEvent("load", this, r, options, o);
11489 if(options.callback){
11490 options.callback.call(options.scope || this, r, options, true);
11496 * Loads data from a passed data block. A Reader which understands the format of the data
11497 * must have been configured in the constructor.
11498 * @param {Object} data The data block from which to read the Records. The format of the data expected
11499 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11500 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11502 loadData : function(o, append){
11503 var r = this.reader.readRecords(o);
11504 this.loadRecords(r, {add: append}, true);
11508 * Gets the number of cached records.
11510 * <em>If using paging, this may not be the total size of the dataset. If the data object
11511 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11512 * the data set size</em>
11514 getCount : function(){
11515 return this.data.length || 0;
11519 * Gets the total number of records in the dataset as returned by the server.
11521 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11522 * the dataset size</em>
11524 getTotalCount : function(){
11525 return this.totalLength || 0;
11529 * Returns the sort state of the Store as an object with two properties:
11531 field {String} The name of the field by which the Records are sorted
11532 direction {String} The sort order, "ASC" or "DESC"
11535 getSortState : function(){
11536 return this.sortInfo;
11540 applySort : function(){
11541 if(this.sortInfo && !this.remoteSort){
11542 var s = this.sortInfo, f = s.field;
11543 var st = this.fields.get(f).sortType;
11544 var fn = function(r1, r2){
11545 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11546 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11548 this.data.sort(s.direction, fn);
11549 if(this.snapshot && this.snapshot != this.data){
11550 this.snapshot.sort(s.direction, fn);
11556 * Sets the default sort column and order to be used by the next load operation.
11557 * @param {String} fieldName The name of the field to sort by.
11558 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11560 setDefaultSort : function(field, dir){
11561 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11565 * Sort the Records.
11566 * If remote sorting is used, the sort is performed on the server, and the cache is
11567 * reloaded. If local sorting is used, the cache is sorted internally.
11568 * @param {String} fieldName The name of the field to sort by.
11569 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11571 sort : function(fieldName, dir){
11572 var f = this.fields.get(fieldName);
11574 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11576 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11577 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11582 this.sortToggle[f.name] = dir;
11583 this.sortInfo = {field: f.name, direction: dir};
11584 if(!this.remoteSort){
11586 this.fireEvent("datachanged", this);
11588 this.load(this.lastOptions);
11593 * Calls the specified function for each of the Records in the cache.
11594 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11595 * Returning <em>false</em> aborts and exits the iteration.
11596 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11598 each : function(fn, scope){
11599 this.data.each(fn, scope);
11603 * Gets all records modified since the last commit. Modified records are persisted across load operations
11604 * (e.g., during paging).
11605 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11607 getModifiedRecords : function(){
11608 return this.modified;
11612 createFilterFn : function(property, value, anyMatch){
11613 if(!value.exec){ // not a regex
11614 value = String(value);
11615 if(value.length == 0){
11618 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11620 return function(r){
11621 return value.test(r.data[property]);
11626 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11627 * @param {String} property A field on your records
11628 * @param {Number} start The record index to start at (defaults to 0)
11629 * @param {Number} end The last record index to include (defaults to length - 1)
11630 * @return {Number} The sum
11632 sum : function(property, start, end){
11633 var rs = this.data.items, v = 0;
11634 start = start || 0;
11635 end = (end || end === 0) ? end : rs.length-1;
11637 for(var i = start; i <= end; i++){
11638 v += (rs[i].data[property] || 0);
11644 * Filter the records by a specified property.
11645 * @param {String} field A field on your records
11646 * @param {String/RegExp} value Either a string that the field
11647 * should start with or a RegExp to test against the field
11648 * @param {Boolean} anyMatch True to match any part not just the beginning
11650 filter : function(property, value, anyMatch){
11651 var fn = this.createFilterFn(property, value, anyMatch);
11652 return fn ? this.filterBy(fn) : this.clearFilter();
11656 * Filter by a function. The specified function will be called with each
11657 * record in this data source. If the function returns true the record is included,
11658 * otherwise it is filtered.
11659 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11660 * @param {Object} scope (optional) The scope of the function (defaults to this)
11662 filterBy : function(fn, scope){
11663 this.snapshot = this.snapshot || this.data;
11664 this.data = this.queryBy(fn, scope||this);
11665 this.fireEvent("datachanged", this);
11669 * Query the records by a specified property.
11670 * @param {String} field A field on your records
11671 * @param {String/RegExp} value Either a string that the field
11672 * should start with or a RegExp to test against the field
11673 * @param {Boolean} anyMatch True to match any part not just the beginning
11674 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11676 query : function(property, value, anyMatch){
11677 var fn = this.createFilterFn(property, value, anyMatch);
11678 return fn ? this.queryBy(fn) : this.data.clone();
11682 * Query by a function. The specified function will be called with each
11683 * record in this data source. If the function returns true the record is included
11685 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11686 * @param {Object} scope (optional) The scope of the function (defaults to this)
11687 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11689 queryBy : function(fn, scope){
11690 var data = this.snapshot || this.data;
11691 return data.filterBy(fn, scope||this);
11695 * Collects unique values for a particular dataIndex from this store.
11696 * @param {String} dataIndex The property to collect
11697 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11698 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11699 * @return {Array} An array of the unique values
11701 collect : function(dataIndex, allowNull, bypassFilter){
11702 var d = (bypassFilter === true && this.snapshot) ?
11703 this.snapshot.items : this.data.items;
11704 var v, sv, r = [], l = {};
11705 for(var i = 0, len = d.length; i < len; i++){
11706 v = d[i].data[dataIndex];
11708 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11717 * Revert to a view of the Record cache with no filtering applied.
11718 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11720 clearFilter : function(suppressEvent){
11721 if(this.snapshot && this.snapshot != this.data){
11722 this.data = this.snapshot;
11723 delete this.snapshot;
11724 if(suppressEvent !== true){
11725 this.fireEvent("datachanged", this);
11731 afterEdit : function(record){
11732 if(this.modified.indexOf(record) == -1){
11733 this.modified.push(record);
11735 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11739 afterReject : function(record){
11740 this.modified.remove(record);
11741 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11745 afterCommit : function(record){
11746 this.modified.remove(record);
11747 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11751 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11752 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11754 commitChanges : function(){
11755 var m = this.modified.slice(0);
11756 this.modified = [];
11757 for(var i = 0, len = m.length; i < len; i++){
11763 * Cancel outstanding changes on all changed records.
11765 rejectChanges : function(){
11766 var m = this.modified.slice(0);
11767 this.modified = [];
11768 for(var i = 0, len = m.length; i < len; i++){
11773 onMetaChange : function(meta, rtype, o){
11774 this.recordType = rtype;
11775 this.fields = rtype.prototype.fields;
11776 delete this.snapshot;
11777 this.sortInfo = meta.sortInfo || this.sortInfo;
11778 this.modified = [];
11779 this.fireEvent('metachange', this, this.reader.meta);
11782 moveIndex : function(data, type)
11784 var index = this.indexOf(data);
11786 var newIndex = index + type;
11790 this.insert(newIndex, data);
11795 * Ext JS Library 1.1.1
11796 * Copyright(c) 2006-2007, Ext JS, LLC.
11798 * Originally Released Under LGPL - original licence link has changed is not relivant.
11801 * <script type="text/javascript">
11805 * @class Roo.data.SimpleStore
11806 * @extends Roo.data.Store
11807 * Small helper class to make creating Stores from Array data easier.
11808 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11809 * @cfg {Array} fields An array of field definition objects, or field name strings.
11810 * @cfg {Array} data The multi-dimensional array of data
11812 * @param {Object} config
11814 Roo.data.SimpleStore = function(config){
11815 Roo.data.SimpleStore.superclass.constructor.call(this, {
11817 reader: new Roo.data.ArrayReader({
11820 Roo.data.Record.create(config.fields)
11822 proxy : new Roo.data.MemoryProxy(config.data)
11826 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11828 * Ext JS Library 1.1.1
11829 * Copyright(c) 2006-2007, Ext JS, LLC.
11831 * Originally Released Under LGPL - original licence link has changed is not relivant.
11834 * <script type="text/javascript">
11839 * @extends Roo.data.Store
11840 * @class Roo.data.JsonStore
11841 * Small helper class to make creating Stores for JSON data easier. <br/>
11843 var store = new Roo.data.JsonStore({
11844 url: 'get-images.php',
11846 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11849 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11850 * JsonReader and HttpProxy (unless inline data is provided).</b>
11851 * @cfg {Array} fields An array of field definition objects, or field name strings.
11853 * @param {Object} config
11855 Roo.data.JsonStore = function(c){
11856 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11857 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11858 reader: new Roo.data.JsonReader(c, c.fields)
11861 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11863 * Ext JS Library 1.1.1
11864 * Copyright(c) 2006-2007, Ext JS, LLC.
11866 * Originally Released Under LGPL - original licence link has changed is not relivant.
11869 * <script type="text/javascript">
11873 Roo.data.Field = function(config){
11874 if(typeof config == "string"){
11875 config = {name: config};
11877 Roo.apply(this, config);
11880 this.type = "auto";
11883 var st = Roo.data.SortTypes;
11884 // named sortTypes are supported, here we look them up
11885 if(typeof this.sortType == "string"){
11886 this.sortType = st[this.sortType];
11889 // set default sortType for strings and dates
11890 if(!this.sortType){
11893 this.sortType = st.asUCString;
11896 this.sortType = st.asDate;
11899 this.sortType = st.none;
11904 var stripRe = /[\$,%]/g;
11906 // prebuilt conversion function for this field, instead of
11907 // switching every time we're reading a value
11909 var cv, dateFormat = this.dateFormat;
11914 cv = function(v){ return v; };
11917 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11921 return v !== undefined && v !== null && v !== '' ?
11922 parseInt(String(v).replace(stripRe, ""), 10) : '';
11927 return v !== undefined && v !== null && v !== '' ?
11928 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11933 cv = function(v){ return v === true || v === "true" || v == 1; };
11940 if(v instanceof Date){
11944 if(dateFormat == "timestamp"){
11945 return new Date(v*1000);
11947 return Date.parseDate(v, dateFormat);
11949 var parsed = Date.parse(v);
11950 return parsed ? new Date(parsed) : null;
11959 Roo.data.Field.prototype = {
11967 * Ext JS Library 1.1.1
11968 * Copyright(c) 2006-2007, Ext JS, LLC.
11970 * Originally Released Under LGPL - original licence link has changed is not relivant.
11973 * <script type="text/javascript">
11976 // Base class for reading structured data from a data source. This class is intended to be
11977 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11980 * @class Roo.data.DataReader
11981 * Base class for reading structured data from a data source. This class is intended to be
11982 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11985 Roo.data.DataReader = function(meta, recordType){
11989 this.recordType = recordType instanceof Array ?
11990 Roo.data.Record.create(recordType) : recordType;
11993 Roo.data.DataReader.prototype = {
11995 * Create an empty record
11996 * @param {Object} data (optional) - overlay some values
11997 * @return {Roo.data.Record} record created.
11999 newRow : function(d) {
12001 this.recordType.prototype.fields.each(function(c) {
12003 case 'int' : da[c.name] = 0; break;
12004 case 'date' : da[c.name] = new Date(); break;
12005 case 'float' : da[c.name] = 0.0; break;
12006 case 'boolean' : da[c.name] = false; break;
12007 default : da[c.name] = ""; break;
12011 return new this.recordType(Roo.apply(da, d));
12016 * Ext JS Library 1.1.1
12017 * Copyright(c) 2006-2007, Ext JS, LLC.
12019 * Originally Released Under LGPL - original licence link has changed is not relivant.
12022 * <script type="text/javascript">
12026 * @class Roo.data.DataProxy
12027 * @extends Roo.data.Observable
12028 * This class is an abstract base class for implementations which provide retrieval of
12029 * unformatted data objects.<br>
12031 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12032 * (of the appropriate type which knows how to parse the data object) to provide a block of
12033 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12035 * Custom implementations must implement the load method as described in
12036 * {@link Roo.data.HttpProxy#load}.
12038 Roo.data.DataProxy = function(){
12041 * @event beforeload
12042 * Fires before a network request is made to retrieve a data object.
12043 * @param {Object} This DataProxy object.
12044 * @param {Object} params The params parameter to the load function.
12049 * Fires before the load method's callback is called.
12050 * @param {Object} This DataProxy object.
12051 * @param {Object} o The data object.
12052 * @param {Object} arg The callback argument object passed to the load function.
12056 * @event loadexception
12057 * Fires if an Exception occurs during data retrieval.
12058 * @param {Object} This DataProxy object.
12059 * @param {Object} o The data object.
12060 * @param {Object} arg The callback argument object passed to the load function.
12061 * @param {Object} e The Exception.
12063 loadexception : true
12065 Roo.data.DataProxy.superclass.constructor.call(this);
12068 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12071 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12075 * Ext JS Library 1.1.1
12076 * Copyright(c) 2006-2007, Ext JS, LLC.
12078 * Originally Released Under LGPL - original licence link has changed is not relivant.
12081 * <script type="text/javascript">
12084 * @class Roo.data.MemoryProxy
12085 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12086 * to the Reader when its load method is called.
12088 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12090 Roo.data.MemoryProxy = function(data){
12094 Roo.data.MemoryProxy.superclass.constructor.call(this);
12098 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12101 * Load data from the requested source (in this case an in-memory
12102 * data object passed to the constructor), read the data object into
12103 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12104 * process that block using the passed callback.
12105 * @param {Object} params This parameter is not used by the MemoryProxy class.
12106 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12107 * object into a block of Roo.data.Records.
12108 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12109 * The function must be passed <ul>
12110 * <li>The Record block object</li>
12111 * <li>The "arg" argument from the load function</li>
12112 * <li>A boolean success indicator</li>
12114 * @param {Object} scope The scope in which to call the callback
12115 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12117 load : function(params, reader, callback, scope, arg){
12118 params = params || {};
12121 result = reader.readRecords(this.data);
12123 this.fireEvent("loadexception", this, arg, null, e);
12124 callback.call(scope, null, arg, false);
12127 callback.call(scope, result, arg, true);
12131 update : function(params, records){
12136 * Ext JS Library 1.1.1
12137 * Copyright(c) 2006-2007, Ext JS, LLC.
12139 * Originally Released Under LGPL - original licence link has changed is not relivant.
12142 * <script type="text/javascript">
12145 * @class Roo.data.HttpProxy
12146 * @extends Roo.data.DataProxy
12147 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12148 * configured to reference a certain URL.<br><br>
12150 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12151 * from which the running page was served.<br><br>
12153 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12155 * Be aware that to enable the browser to parse an XML document, the server must set
12156 * the Content-Type header in the HTTP response to "text/xml".
12158 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12159 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12160 * will be used to make the request.
12162 Roo.data.HttpProxy = function(conn){
12163 Roo.data.HttpProxy.superclass.constructor.call(this);
12164 // is conn a conn config or a real conn?
12166 this.useAjax = !conn || !conn.events;
12170 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12171 // thse are take from connection...
12174 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12177 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12178 * extra parameters to each request made by this object. (defaults to undefined)
12181 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12182 * to each request made by this object. (defaults to undefined)
12185 * @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)
12188 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12191 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12197 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12201 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12202 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12203 * a finer-grained basis than the DataProxy events.
12205 getConnection : function(){
12206 return this.useAjax ? Roo.Ajax : this.conn;
12210 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12211 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12212 * process that block using the passed callback.
12213 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12214 * for the request to the remote server.
12215 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12216 * object into a block of Roo.data.Records.
12217 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12218 * The function must be passed <ul>
12219 * <li>The Record block object</li>
12220 * <li>The "arg" argument from the load function</li>
12221 * <li>A boolean success indicator</li>
12223 * @param {Object} scope The scope in which to call the callback
12224 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12226 load : function(params, reader, callback, scope, arg){
12227 if(this.fireEvent("beforeload", this, params) !== false){
12229 params : params || {},
12231 callback : callback,
12236 callback : this.loadResponse,
12240 Roo.applyIf(o, this.conn);
12241 if(this.activeRequest){
12242 Roo.Ajax.abort(this.activeRequest);
12244 this.activeRequest = Roo.Ajax.request(o);
12246 this.conn.request(o);
12249 callback.call(scope||this, null, arg, false);
12254 loadResponse : function(o, success, response){
12255 delete this.activeRequest;
12257 this.fireEvent("loadexception", this, o, response);
12258 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12263 result = o.reader.read(response);
12265 this.fireEvent("loadexception", this, o, response, e);
12266 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12270 this.fireEvent("load", this, o, o.request.arg);
12271 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12275 update : function(dataSet){
12280 updateResponse : function(dataSet){
12285 * Ext JS Library 1.1.1
12286 * Copyright(c) 2006-2007, Ext JS, LLC.
12288 * Originally Released Under LGPL - original licence link has changed is not relivant.
12291 * <script type="text/javascript">
12295 * @class Roo.data.ScriptTagProxy
12296 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12297 * other than the originating domain of the running page.<br><br>
12299 * <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
12300 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12302 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12303 * source code that is used as the source inside a <script> tag.<br><br>
12305 * In order for the browser to process the returned data, the server must wrap the data object
12306 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12307 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12308 * depending on whether the callback name was passed:
12311 boolean scriptTag = false;
12312 String cb = request.getParameter("callback");
12315 response.setContentType("text/javascript");
12317 response.setContentType("application/x-json");
12319 Writer out = response.getWriter();
12321 out.write(cb + "(");
12323 out.print(dataBlock.toJsonString());
12330 * @param {Object} config A configuration object.
12332 Roo.data.ScriptTagProxy = function(config){
12333 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12334 Roo.apply(this, config);
12335 this.head = document.getElementsByTagName("head")[0];
12338 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12340 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12342 * @cfg {String} url The URL from which to request the data object.
12345 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12349 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12350 * the server the name of the callback function set up by the load call to process the returned data object.
12351 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12352 * javascript output which calls this named function passing the data object as its only parameter.
12354 callbackParam : "callback",
12356 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12357 * name to the request.
12362 * Load data from the configured URL, read the data object into
12363 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12364 * process that block using the passed callback.
12365 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12366 * for the request to the remote server.
12367 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12368 * object into a block of Roo.data.Records.
12369 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12370 * The function must be passed <ul>
12371 * <li>The Record block object</li>
12372 * <li>The "arg" argument from the load function</li>
12373 * <li>A boolean success indicator</li>
12375 * @param {Object} scope The scope in which to call the callback
12376 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12378 load : function(params, reader, callback, scope, arg){
12379 if(this.fireEvent("beforeload", this, params) !== false){
12381 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12383 var url = this.url;
12384 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12386 url += "&_dc=" + (new Date().getTime());
12388 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12391 cb : "stcCallback"+transId,
12392 scriptId : "stcScript"+transId,
12396 callback : callback,
12402 window[trans.cb] = function(o){
12403 conn.handleResponse(o, trans);
12406 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12408 if(this.autoAbort !== false){
12412 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12414 var script = document.createElement("script");
12415 script.setAttribute("src", url);
12416 script.setAttribute("type", "text/javascript");
12417 script.setAttribute("id", trans.scriptId);
12418 this.head.appendChild(script);
12420 this.trans = trans;
12422 callback.call(scope||this, null, arg, false);
12427 isLoading : function(){
12428 return this.trans ? true : false;
12432 * Abort the current server request.
12434 abort : function(){
12435 if(this.isLoading()){
12436 this.destroyTrans(this.trans);
12441 destroyTrans : function(trans, isLoaded){
12442 this.head.removeChild(document.getElementById(trans.scriptId));
12443 clearTimeout(trans.timeoutId);
12445 window[trans.cb] = undefined;
12447 delete window[trans.cb];
12450 // if hasn't been loaded, wait for load to remove it to prevent script error
12451 window[trans.cb] = function(){
12452 window[trans.cb] = undefined;
12454 delete window[trans.cb];
12461 handleResponse : function(o, trans){
12462 this.trans = false;
12463 this.destroyTrans(trans, true);
12466 result = trans.reader.readRecords(o);
12468 this.fireEvent("loadexception", this, o, trans.arg, e);
12469 trans.callback.call(trans.scope||window, null, trans.arg, false);
12472 this.fireEvent("load", this, o, trans.arg);
12473 trans.callback.call(trans.scope||window, result, trans.arg, true);
12477 handleFailure : function(trans){
12478 this.trans = false;
12479 this.destroyTrans(trans, false);
12480 this.fireEvent("loadexception", this, null, trans.arg);
12481 trans.callback.call(trans.scope||window, null, trans.arg, false);
12485 * Ext JS Library 1.1.1
12486 * Copyright(c) 2006-2007, Ext JS, LLC.
12488 * Originally Released Under LGPL - original licence link has changed is not relivant.
12491 * <script type="text/javascript">
12495 * @class Roo.data.JsonReader
12496 * @extends Roo.data.DataReader
12497 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12498 * based on mappings in a provided Roo.data.Record constructor.
12500 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12501 * in the reply previously.
12506 var RecordDef = Roo.data.Record.create([
12507 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12508 {name: 'occupation'} // This field will use "occupation" as the mapping.
12510 var myReader = new Roo.data.JsonReader({
12511 totalProperty: "results", // The property which contains the total dataset size (optional)
12512 root: "rows", // The property which contains an Array of row objects
12513 id: "id" // The property within each row object that provides an ID for the record (optional)
12517 * This would consume a JSON file like this:
12519 { 'results': 2, 'rows': [
12520 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12521 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12524 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12525 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12526 * paged from the remote server.
12527 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12528 * @cfg {String} root name of the property which contains the Array of row objects.
12529 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12530 * @cfg {Array} fields Array of field definition objects
12532 * Create a new JsonReader
12533 * @param {Object} meta Metadata configuration options
12534 * @param {Object} recordType Either an Array of field definition objects,
12535 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12537 Roo.data.JsonReader = function(meta, recordType){
12540 // set some defaults:
12541 Roo.applyIf(meta, {
12542 totalProperty: 'total',
12543 successProperty : 'success',
12548 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12550 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12553 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12554 * Used by Store query builder to append _requestMeta to params.
12557 metaFromRemote : false,
12559 * This method is only used by a DataProxy which has retrieved data from a remote server.
12560 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12561 * @return {Object} data A data block which is used by an Roo.data.Store object as
12562 * a cache of Roo.data.Records.
12564 read : function(response){
12565 var json = response.responseText;
12567 var o = /* eval:var:o */ eval("("+json+")");
12569 throw {message: "JsonReader.read: Json object not found"};
12575 this.metaFromRemote = true;
12576 this.meta = o.metaData;
12577 this.recordType = Roo.data.Record.create(o.metaData.fields);
12578 this.onMetaChange(this.meta, this.recordType, o);
12580 return this.readRecords(o);
12583 // private function a store will implement
12584 onMetaChange : function(meta, recordType, o){
12591 simpleAccess: function(obj, subsc) {
12598 getJsonAccessor: function(){
12600 return function(expr) {
12602 return(re.test(expr))
12603 ? new Function("obj", "return obj." + expr)
12608 return Roo.emptyFn;
12613 * Create a data block containing Roo.data.Records from an XML document.
12614 * @param {Object} o An object which contains an Array of row objects in the property specified
12615 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12616 * which contains the total size of the dataset.
12617 * @return {Object} data A data block which is used by an Roo.data.Store object as
12618 * a cache of Roo.data.Records.
12620 readRecords : function(o){
12622 * After any data loads, the raw JSON data is available for further custom processing.
12626 var s = this.meta, Record = this.recordType,
12627 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12629 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12631 if(s.totalProperty) {
12632 this.getTotal = this.getJsonAccessor(s.totalProperty);
12634 if(s.successProperty) {
12635 this.getSuccess = this.getJsonAccessor(s.successProperty);
12637 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12639 var g = this.getJsonAccessor(s.id);
12640 this.getId = function(rec) {
12642 return (r === undefined || r === "") ? null : r;
12645 this.getId = function(){return null;};
12648 for(var jj = 0; jj < fl; jj++){
12650 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12651 this.ef[jj] = this.getJsonAccessor(map);
12655 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12656 if(s.totalProperty){
12657 var vt = parseInt(this.getTotal(o), 10);
12662 if(s.successProperty){
12663 var vs = this.getSuccess(o);
12664 if(vs === false || vs === 'false'){
12669 for(var i = 0; i < c; i++){
12672 var id = this.getId(n);
12673 for(var j = 0; j < fl; j++){
12675 var v = this.ef[j](n);
12677 Roo.log('missing convert for ' + f.name);
12681 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12683 var record = new Record(values, id);
12685 records[i] = record;
12691 totalRecords : totalRecords
12696 * Ext JS Library 1.1.1
12697 * Copyright(c) 2006-2007, Ext JS, LLC.
12699 * Originally Released Under LGPL - original licence link has changed is not relivant.
12702 * <script type="text/javascript">
12706 * @class Roo.data.ArrayReader
12707 * @extends Roo.data.DataReader
12708 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12709 * Each element of that Array represents a row of data fields. The
12710 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12711 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12715 var RecordDef = Roo.data.Record.create([
12716 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12717 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12719 var myReader = new Roo.data.ArrayReader({
12720 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12724 * This would consume an Array like this:
12726 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12728 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12730 * Create a new JsonReader
12731 * @param {Object} meta Metadata configuration options.
12732 * @param {Object} recordType Either an Array of field definition objects
12733 * as specified to {@link Roo.data.Record#create},
12734 * or an {@link Roo.data.Record} object
12735 * created using {@link Roo.data.Record#create}.
12737 Roo.data.ArrayReader = function(meta, recordType){
12738 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12741 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12743 * Create a data block containing Roo.data.Records from an XML document.
12744 * @param {Object} o An Array of row objects which represents the dataset.
12745 * @return {Object} data A data block which is used by an Roo.data.Store object as
12746 * a cache of Roo.data.Records.
12748 readRecords : function(o){
12749 var sid = this.meta ? this.meta.id : null;
12750 var recordType = this.recordType, fields = recordType.prototype.fields;
12753 for(var i = 0; i < root.length; i++){
12756 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12757 for(var j = 0, jlen = fields.length; j < jlen; j++){
12758 var f = fields.items[j];
12759 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12760 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12762 values[f.name] = v;
12764 var record = new recordType(values, id);
12766 records[records.length] = record;
12770 totalRecords : records.length
12779 * @class Roo.bootstrap.ComboBox
12780 * @extends Roo.bootstrap.TriggerField
12781 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12782 * @cfg {Boolean} append (true|false) default false
12783 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12784 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12785 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12786 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12787 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12788 * @cfg {Boolean} animate default true
12789 * @cfg {Boolean} emptyResultText only for touch device
12790 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12791 * @cfg {String} emptyTitle default ''
12793 * Create a new ComboBox.
12794 * @param {Object} config Configuration options
12796 Roo.bootstrap.ComboBox = function(config){
12797 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12801 * Fires when the dropdown list is expanded
12802 * @param {Roo.bootstrap.ComboBox} combo This combo box
12807 * Fires when the dropdown list is collapsed
12808 * @param {Roo.bootstrap.ComboBox} combo This combo box
12812 * @event beforeselect
12813 * Fires before a list item is selected. Return false to cancel the selection.
12814 * @param {Roo.bootstrap.ComboBox} combo This combo box
12815 * @param {Roo.data.Record} record The data record returned from the underlying store
12816 * @param {Number} index The index of the selected item in the dropdown list
12818 'beforeselect' : true,
12821 * Fires when a list item is selected
12822 * @param {Roo.bootstrap.ComboBox} combo This combo box
12823 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12824 * @param {Number} index The index of the selected item in the dropdown list
12828 * @event beforequery
12829 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12830 * The event object passed has these properties:
12831 * @param {Roo.bootstrap.ComboBox} combo This combo box
12832 * @param {String} query The query
12833 * @param {Boolean} forceAll true to force "all" query
12834 * @param {Boolean} cancel true to cancel the query
12835 * @param {Object} e The query event object
12837 'beforequery': true,
12840 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12841 * @param {Roo.bootstrap.ComboBox} combo This combo box
12846 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12847 * @param {Roo.bootstrap.ComboBox} combo This combo box
12848 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12853 * Fires when the remove value from the combobox array
12854 * @param {Roo.bootstrap.ComboBox} combo This combo box
12858 * @event afterremove
12859 * Fires when the remove value from the combobox array
12860 * @param {Roo.bootstrap.ComboBox} combo This combo box
12862 'afterremove' : true,
12864 * @event specialfilter
12865 * Fires when specialfilter
12866 * @param {Roo.bootstrap.ComboBox} combo This combo box
12868 'specialfilter' : true,
12871 * Fires when tick the element
12872 * @param {Roo.bootstrap.ComboBox} combo This combo box
12876 * @event touchviewdisplay
12877 * Fires when touch view require special display (default is using displayField)
12878 * @param {Roo.bootstrap.ComboBox} combo This combo box
12879 * @param {Object} cfg set html .
12881 'touchviewdisplay' : true
12886 this.tickItems = [];
12888 this.selectedIndex = -1;
12889 if(this.mode == 'local'){
12890 if(config.queryDelay === undefined){
12891 this.queryDelay = 10;
12893 if(config.minChars === undefined){
12899 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12902 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12903 * rendering into an Roo.Editor, defaults to false)
12906 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12907 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12910 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12913 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12914 * the dropdown list (defaults to undefined, with no header element)
12918 * @cfg {String/Roo.Template} tpl The template to use to render the output
12922 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12924 listWidth: undefined,
12926 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12927 * mode = 'remote' or 'text' if mode = 'local')
12929 displayField: undefined,
12932 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12933 * mode = 'remote' or 'value' if mode = 'local').
12934 * Note: use of a valueField requires the user make a selection
12935 * in order for a value to be mapped.
12937 valueField: undefined,
12939 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12944 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12945 * field's data value (defaults to the underlying DOM element's name)
12947 hiddenName: undefined,
12949 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12953 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12955 selectedClass: 'active',
12958 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12962 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12963 * anchor positions (defaults to 'tl-bl')
12965 listAlign: 'tl-bl?',
12967 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12971 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12972 * query specified by the allQuery config option (defaults to 'query')
12974 triggerAction: 'query',
12976 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12977 * (defaults to 4, does not apply if editable = false)
12981 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12982 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12986 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12987 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12991 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12992 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12996 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12997 * when editable = true (defaults to false)
12999 selectOnFocus:false,
13001 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13003 queryParam: 'query',
13005 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
13006 * when mode = 'remote' (defaults to 'Loading...')
13008 loadingText: 'Loading...',
13010 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13014 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13018 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13019 * traditional select (defaults to true)
13023 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13027 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13031 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13032 * listWidth has a higher value)
13036 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13037 * allow the user to set arbitrary text into the field (defaults to false)
13039 forceSelection:false,
13041 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13042 * if typeAhead = true (defaults to 250)
13044 typeAheadDelay : 250,
13046 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13047 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13049 valueNotFoundText : undefined,
13051 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13053 blockFocus : false,
13056 * @cfg {Boolean} disableClear Disable showing of clear button.
13058 disableClear : false,
13060 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
13062 alwaysQuery : false,
13065 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
13070 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13072 invalidClass : "has-warning",
13075 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13077 validClass : "has-success",
13080 * @cfg {Boolean} specialFilter (true|false) special filter default false
13082 specialFilter : false,
13085 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13087 mobileTouchView : true,
13090 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13092 useNativeIOS : false,
13095 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13097 mobile_restrict_height : false,
13099 ios_options : false,
13111 btnPosition : 'right',
13112 triggerList : true,
13113 showToggleBtn : true,
13115 emptyResultText: 'Empty',
13116 triggerText : 'Select',
13119 // element that contains real text value.. (when hidden is used..)
13121 getAutoCreate : function()
13126 * Render classic select for iso
13129 if(Roo.isIOS && this.useNativeIOS){
13130 cfg = this.getAutoCreateNativeIOS();
13138 if(Roo.isTouch && this.mobileTouchView){
13139 cfg = this.getAutoCreateTouchView();
13146 if(!this.tickable){
13147 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13152 * ComboBox with tickable selections
13155 var align = this.labelAlign || this.parentLabelAlign();
13158 cls : 'form-group roo-combobox-tickable' //input-group
13161 var btn_text_select = '';
13162 var btn_text_done = '';
13163 var btn_text_cancel = '';
13165 if (this.btn_text_show) {
13166 btn_text_select = 'Select';
13167 btn_text_done = 'Done';
13168 btn_text_cancel = 'Cancel';
13173 cls : 'tickable-buttons',
13178 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13179 //html : this.triggerText
13180 html: btn_text_select
13186 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13188 html: btn_text_done
13194 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13196 html: btn_text_cancel
13202 buttons.cn.unshift({
13204 cls: 'roo-select2-search-field-input'
13210 Roo.each(buttons.cn, function(c){
13212 c.cls += ' btn-' + _this.size;
13215 if (_this.disabled) {
13226 cls: 'form-hidden-field'
13230 cls: 'roo-select2-choices',
13234 cls: 'roo-select2-search-field',
13245 cls: 'roo-select2-container input-group roo-select2-container-multi',
13250 // cls: 'typeahead typeahead-long dropdown-menu',
13251 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13256 if(this.hasFeedback && !this.allowBlank){
13260 cls: 'glyphicon form-control-feedback'
13263 combobox.cn.push(feedback);
13267 if (align ==='left' && this.fieldLabel.length) {
13269 cfg.cls += ' roo-form-group-label-left';
13274 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13275 tooltip : 'This field is required'
13280 cls : 'control-label',
13281 html : this.fieldLabel
13293 var labelCfg = cfg.cn[1];
13294 var contentCfg = cfg.cn[2];
13297 if(this.indicatorpos == 'right'){
13303 cls : 'control-label',
13307 html : this.fieldLabel
13311 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13312 tooltip : 'This field is required'
13327 labelCfg = cfg.cn[0];
13328 contentCfg = cfg.cn[1];
13332 if(this.labelWidth > 12){
13333 labelCfg.style = "width: " + this.labelWidth + 'px';
13336 if(this.labelWidth < 13 && this.labelmd == 0){
13337 this.labelmd = this.labelWidth;
13340 if(this.labellg > 0){
13341 labelCfg.cls += ' col-lg-' + this.labellg;
13342 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13345 if(this.labelmd > 0){
13346 labelCfg.cls += ' col-md-' + this.labelmd;
13347 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13350 if(this.labelsm > 0){
13351 labelCfg.cls += ' col-sm-' + this.labelsm;
13352 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13355 if(this.labelxs > 0){
13356 labelCfg.cls += ' col-xs-' + this.labelxs;
13357 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13361 } else if ( this.fieldLabel.length) {
13362 // Roo.log(" label");
13366 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13367 tooltip : 'This field is required'
13371 //cls : 'input-group-addon',
13372 html : this.fieldLabel
13377 if(this.indicatorpos == 'right'){
13381 //cls : 'input-group-addon',
13382 html : this.fieldLabel
13386 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13387 tooltip : 'This field is required'
13396 // Roo.log(" no label && no align");
13403 ['xs','sm','md','lg'].map(function(size){
13404 if (settings[size]) {
13405 cfg.cls += ' col-' + size + '-' + settings[size];
13413 _initEventsCalled : false,
13416 initEvents: function()
13418 if (this._initEventsCalled) { // as we call render... prevent looping...
13421 this._initEventsCalled = true;
13424 throw "can not find store for combo";
13427 this.indicator = this.indicatorEl();
13429 this.store = Roo.factory(this.store, Roo.data);
13430 this.store.parent = this;
13432 // if we are building from html. then this element is so complex, that we can not really
13433 // use the rendered HTML.
13434 // so we have to trash and replace the previous code.
13435 if (Roo.XComponent.build_from_html) {
13436 // remove this element....
13437 var e = this.el.dom, k=0;
13438 while (e ) { e = e.previousSibling; ++k;}
13443 this.rendered = false;
13445 this.render(this.parent().getChildContainer(true), k);
13448 if(Roo.isIOS && this.useNativeIOS){
13449 this.initIOSView();
13457 if(Roo.isTouch && this.mobileTouchView){
13458 this.initTouchView();
13463 this.initTickableEvents();
13467 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13469 if(this.hiddenName){
13471 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13473 this.hiddenField.dom.value =
13474 this.hiddenValue !== undefined ? this.hiddenValue :
13475 this.value !== undefined ? this.value : '';
13477 // prevent input submission
13478 this.el.dom.removeAttribute('name');
13479 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13484 // this.el.dom.setAttribute('autocomplete', 'off');
13487 var cls = 'x-combo-list';
13489 //this.list = new Roo.Layer({
13490 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13496 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13497 _this.list.setWidth(lw);
13500 this.list.on('mouseover', this.onViewOver, this);
13501 this.list.on('mousemove', this.onViewMove, this);
13502 this.list.on('scroll', this.onViewScroll, this);
13505 this.list.swallowEvent('mousewheel');
13506 this.assetHeight = 0;
13509 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13510 this.assetHeight += this.header.getHeight();
13513 this.innerList = this.list.createChild({cls:cls+'-inner'});
13514 this.innerList.on('mouseover', this.onViewOver, this);
13515 this.innerList.on('mousemove', this.onViewMove, this);
13516 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13518 if(this.allowBlank && !this.pageSize && !this.disableClear){
13519 this.footer = this.list.createChild({cls:cls+'-ft'});
13520 this.pageTb = new Roo.Toolbar(this.footer);
13524 this.footer = this.list.createChild({cls:cls+'-ft'});
13525 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13526 {pageSize: this.pageSize});
13530 if (this.pageTb && this.allowBlank && !this.disableClear) {
13532 this.pageTb.add(new Roo.Toolbar.Fill(), {
13533 cls: 'x-btn-icon x-btn-clear',
13535 handler: function()
13538 _this.clearValue();
13539 _this.onSelect(false, -1);
13544 this.assetHeight += this.footer.getHeight();
13549 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13552 this.view = new Roo.View(this.list, this.tpl, {
13553 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13555 //this.view.wrapEl.setDisplayed(false);
13556 this.view.on('click', this.onViewClick, this);
13559 this.store.on('beforeload', this.onBeforeLoad, this);
13560 this.store.on('load', this.onLoad, this);
13561 this.store.on('loadexception', this.onLoadException, this);
13563 if(this.resizable){
13564 this.resizer = new Roo.Resizable(this.list, {
13565 pinned:true, handles:'se'
13567 this.resizer.on('resize', function(r, w, h){
13568 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13569 this.listWidth = w;
13570 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13571 this.restrictHeight();
13573 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13576 if(!this.editable){
13577 this.editable = true;
13578 this.setEditable(false);
13583 if (typeof(this.events.add.listeners) != 'undefined') {
13585 this.addicon = this.wrap.createChild(
13586 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13588 this.addicon.on('click', function(e) {
13589 this.fireEvent('add', this);
13592 if (typeof(this.events.edit.listeners) != 'undefined') {
13594 this.editicon = this.wrap.createChild(
13595 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13596 if (this.addicon) {
13597 this.editicon.setStyle('margin-left', '40px');
13599 this.editicon.on('click', function(e) {
13601 // we fire even if inothing is selected..
13602 this.fireEvent('edit', this, this.lastData );
13608 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13609 "up" : function(e){
13610 this.inKeyMode = true;
13614 "down" : function(e){
13615 if(!this.isExpanded()){
13616 this.onTriggerClick();
13618 this.inKeyMode = true;
13623 "enter" : function(e){
13624 // this.onViewClick();
13628 if(this.fireEvent("specialkey", this, e)){
13629 this.onViewClick(false);
13635 "esc" : function(e){
13639 "tab" : function(e){
13642 if(this.fireEvent("specialkey", this, e)){
13643 this.onViewClick(false);
13651 doRelay : function(foo, bar, hname){
13652 if(hname == 'down' || this.scope.isExpanded()){
13653 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13662 this.queryDelay = Math.max(this.queryDelay || 10,
13663 this.mode == 'local' ? 10 : 250);
13666 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13668 if(this.typeAhead){
13669 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13671 if(this.editable !== false){
13672 this.inputEl().on("keyup", this.onKeyUp, this);
13674 if(this.forceSelection){
13675 this.inputEl().on('blur', this.doForce, this);
13679 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13680 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13684 initTickableEvents: function()
13688 if(this.hiddenName){
13690 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13692 this.hiddenField.dom.value =
13693 this.hiddenValue !== undefined ? this.hiddenValue :
13694 this.value !== undefined ? this.value : '';
13696 // prevent input submission
13697 this.el.dom.removeAttribute('name');
13698 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13703 // this.list = this.el.select('ul.dropdown-menu',true).first();
13705 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13706 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13707 if(this.triggerList){
13708 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13711 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13712 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13714 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13715 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13717 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13718 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13720 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13721 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13722 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13725 this.cancelBtn.hide();
13730 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13731 _this.list.setWidth(lw);
13734 this.list.on('mouseover', this.onViewOver, this);
13735 this.list.on('mousemove', this.onViewMove, this);
13737 this.list.on('scroll', this.onViewScroll, this);
13740 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13741 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13744 this.view = new Roo.View(this.list, this.tpl, {
13749 selectedClass: this.selectedClass
13752 //this.view.wrapEl.setDisplayed(false);
13753 this.view.on('click', this.onViewClick, this);
13757 this.store.on('beforeload', this.onBeforeLoad, this);
13758 this.store.on('load', this.onLoad, this);
13759 this.store.on('loadexception', this.onLoadException, this);
13762 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13763 "up" : function(e){
13764 this.inKeyMode = true;
13768 "down" : function(e){
13769 this.inKeyMode = true;
13773 "enter" : function(e){
13774 if(this.fireEvent("specialkey", this, e)){
13775 this.onViewClick(false);
13781 "esc" : function(e){
13782 this.onTickableFooterButtonClick(e, false, false);
13785 "tab" : function(e){
13786 this.fireEvent("specialkey", this, e);
13788 this.onTickableFooterButtonClick(e, false, false);
13795 doRelay : function(e, fn, key){
13796 if(this.scope.isExpanded()){
13797 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13806 this.queryDelay = Math.max(this.queryDelay || 10,
13807 this.mode == 'local' ? 10 : 250);
13810 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13812 if(this.typeAhead){
13813 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13816 if(this.editable !== false){
13817 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13820 this.indicator = this.indicatorEl();
13822 if(this.indicator){
13823 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13824 this.indicator.hide();
13829 onDestroy : function(){
13831 this.view.setStore(null);
13832 this.view.el.removeAllListeners();
13833 this.view.el.remove();
13834 this.view.purgeListeners();
13837 this.list.dom.innerHTML = '';
13841 this.store.un('beforeload', this.onBeforeLoad, this);
13842 this.store.un('load', this.onLoad, this);
13843 this.store.un('loadexception', this.onLoadException, this);
13845 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13849 fireKey : function(e){
13850 if(e.isNavKeyPress() && !this.list.isVisible()){
13851 this.fireEvent("specialkey", this, e);
13856 onResize: function(w, h){
13857 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13859 // if(typeof w != 'number'){
13860 // // we do not handle it!?!?
13863 // var tw = this.trigger.getWidth();
13864 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13865 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13867 // this.inputEl().setWidth( this.adjustWidth('input', x));
13869 // //this.trigger.setStyle('left', x+'px');
13871 // if(this.list && this.listWidth === undefined){
13872 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13873 // this.list.setWidth(lw);
13874 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13882 * Allow or prevent the user from directly editing the field text. If false is passed,
13883 * the user will only be able to select from the items defined in the dropdown list. This method
13884 * is the runtime equivalent of setting the 'editable' config option at config time.
13885 * @param {Boolean} value True to allow the user to directly edit the field text
13887 setEditable : function(value){
13888 if(value == this.editable){
13891 this.editable = value;
13893 this.inputEl().dom.setAttribute('readOnly', true);
13894 this.inputEl().on('mousedown', this.onTriggerClick, this);
13895 this.inputEl().addClass('x-combo-noedit');
13897 this.inputEl().dom.setAttribute('readOnly', false);
13898 this.inputEl().un('mousedown', this.onTriggerClick, this);
13899 this.inputEl().removeClass('x-combo-noedit');
13905 onBeforeLoad : function(combo,opts){
13906 if(!this.hasFocus){
13910 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13912 this.restrictHeight();
13913 this.selectedIndex = -1;
13917 onLoad : function(){
13919 this.hasQuery = false;
13921 if(!this.hasFocus){
13925 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13926 this.loading.hide();
13929 if(this.store.getCount() > 0){
13932 this.restrictHeight();
13933 if(this.lastQuery == this.allQuery){
13934 if(this.editable && !this.tickable){
13935 this.inputEl().dom.select();
13939 !this.selectByValue(this.value, true) &&
13942 !this.store.lastOptions ||
13943 typeof(this.store.lastOptions.add) == 'undefined' ||
13944 this.store.lastOptions.add != true
13947 this.select(0, true);
13950 if(this.autoFocus){
13953 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13954 this.taTask.delay(this.typeAheadDelay);
13958 this.onEmptyResults();
13964 onLoadException : function()
13966 this.hasQuery = false;
13968 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13969 this.loading.hide();
13972 if(this.tickable && this.editable){
13977 // only causes errors at present
13978 //Roo.log(this.store.reader.jsonData);
13979 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13981 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13987 onTypeAhead : function(){
13988 if(this.store.getCount() > 0){
13989 var r = this.store.getAt(0);
13990 var newValue = r.data[this.displayField];
13991 var len = newValue.length;
13992 var selStart = this.getRawValue().length;
13994 if(selStart != len){
13995 this.setRawValue(newValue);
13996 this.selectText(selStart, newValue.length);
14002 onSelect : function(record, index){
14004 if(this.fireEvent('beforeselect', this, record, index) !== false){
14006 this.setFromData(index > -1 ? record.data : false);
14009 this.fireEvent('select', this, record, index);
14014 * Returns the currently selected field value or empty string if no value is set.
14015 * @return {String} value The selected value
14017 getValue : function()
14019 if(Roo.isIOS && this.useNativeIOS){
14020 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14024 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14027 if(this.valueField){
14028 return typeof this.value != 'undefined' ? this.value : '';
14030 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14034 getRawValue : function()
14036 if(Roo.isIOS && this.useNativeIOS){
14037 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14040 var v = this.inputEl().getValue();
14046 * Clears any text/value currently set in the field
14048 clearValue : function(){
14050 if(this.hiddenField){
14051 this.hiddenField.dom.value = '';
14054 this.setRawValue('');
14055 this.lastSelectionText = '';
14056 this.lastData = false;
14058 var close = this.closeTriggerEl();
14069 * Sets the specified value into the field. If the value finds a match, the corresponding record text
14070 * will be displayed in the field. If the value does not match the data value of an existing item,
14071 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14072 * Otherwise the field will be blank (although the value will still be set).
14073 * @param {String} value The value to match
14075 setValue : function(v)
14077 if(Roo.isIOS && this.useNativeIOS){
14078 this.setIOSValue(v);
14088 if(this.valueField){
14089 var r = this.findRecord(this.valueField, v);
14091 text = r.data[this.displayField];
14092 }else if(this.valueNotFoundText !== undefined){
14093 text = this.valueNotFoundText;
14096 this.lastSelectionText = text;
14097 if(this.hiddenField){
14098 this.hiddenField.dom.value = v;
14100 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14103 var close = this.closeTriggerEl();
14106 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14112 * @property {Object} the last set data for the element
14117 * Sets the value of the field based on a object which is related to the record format for the store.
14118 * @param {Object} value the value to set as. or false on reset?
14120 setFromData : function(o){
14127 var dv = ''; // display value
14128 var vv = ''; // value value..
14130 if (this.displayField) {
14131 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14133 // this is an error condition!!!
14134 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14137 if(this.valueField){
14138 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14141 var close = this.closeTriggerEl();
14144 if(dv.length || vv * 1 > 0){
14146 this.blockFocus=true;
14152 if(this.hiddenField){
14153 this.hiddenField.dom.value = vv;
14155 this.lastSelectionText = dv;
14156 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14160 // no hidden field.. - we store the value in 'value', but still display
14161 // display field!!!!
14162 this.lastSelectionText = dv;
14163 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14170 reset : function(){
14171 // overridden so that last data is reset..
14178 this.setValue(this.originalValue);
14179 //this.clearInvalid();
14180 this.lastData = false;
14182 this.view.clearSelections();
14188 findRecord : function(prop, value){
14190 if(this.store.getCount() > 0){
14191 this.store.each(function(r){
14192 if(r.data[prop] == value){
14202 getName: function()
14204 // returns hidden if it's set..
14205 if (!this.rendered) {return ''};
14206 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14210 onViewMove : function(e, t){
14211 this.inKeyMode = false;
14215 onViewOver : function(e, t){
14216 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14219 var item = this.view.findItemFromChild(t);
14222 var index = this.view.indexOf(item);
14223 this.select(index, false);
14228 onViewClick : function(view, doFocus, el, e)
14230 var index = this.view.getSelectedIndexes()[0];
14232 var r = this.store.getAt(index);
14236 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14243 Roo.each(this.tickItems, function(v,k){
14245 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14247 _this.tickItems.splice(k, 1);
14249 if(typeof(e) == 'undefined' && view == false){
14250 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14262 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14263 this.tickItems.push(r.data);
14266 if(typeof(e) == 'undefined' && view == false){
14267 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14274 this.onSelect(r, index);
14276 if(doFocus !== false && !this.blockFocus){
14277 this.inputEl().focus();
14282 restrictHeight : function(){
14283 //this.innerList.dom.style.height = '';
14284 //var inner = this.innerList.dom;
14285 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14286 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14287 //this.list.beginUpdate();
14288 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14289 this.list.alignTo(this.inputEl(), this.listAlign);
14290 this.list.alignTo(this.inputEl(), this.listAlign);
14291 //this.list.endUpdate();
14295 onEmptyResults : function(){
14297 if(this.tickable && this.editable){
14298 this.hasFocus = false;
14299 this.restrictHeight();
14307 * Returns true if the dropdown list is expanded, else false.
14309 isExpanded : function(){
14310 return this.list.isVisible();
14314 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14315 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14316 * @param {String} value The data value of the item to select
14317 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14318 * selected item if it is not currently in view (defaults to true)
14319 * @return {Boolean} True if the value matched an item in the list, else false
14321 selectByValue : function(v, scrollIntoView){
14322 if(v !== undefined && v !== null){
14323 var r = this.findRecord(this.valueField || this.displayField, v);
14325 this.select(this.store.indexOf(r), scrollIntoView);
14333 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14334 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14335 * @param {Number} index The zero-based index of the list item to select
14336 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14337 * selected item if it is not currently in view (defaults to true)
14339 select : function(index, scrollIntoView){
14340 this.selectedIndex = index;
14341 this.view.select(index);
14342 if(scrollIntoView !== false){
14343 var el = this.view.getNode(index);
14345 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14348 this.list.scrollChildIntoView(el, false);
14354 selectNext : function(){
14355 var ct = this.store.getCount();
14357 if(this.selectedIndex == -1){
14359 }else if(this.selectedIndex < ct-1){
14360 this.select(this.selectedIndex+1);
14366 selectPrev : function(){
14367 var ct = this.store.getCount();
14369 if(this.selectedIndex == -1){
14371 }else if(this.selectedIndex != 0){
14372 this.select(this.selectedIndex-1);
14378 onKeyUp : function(e){
14379 if(this.editable !== false && !e.isSpecialKey()){
14380 this.lastKey = e.getKey();
14381 this.dqTask.delay(this.queryDelay);
14386 validateBlur : function(){
14387 return !this.list || !this.list.isVisible();
14391 initQuery : function(){
14393 var v = this.getRawValue();
14395 if(this.tickable && this.editable){
14396 v = this.tickableInputEl().getValue();
14403 doForce : function(){
14404 if(this.inputEl().dom.value.length > 0){
14405 this.inputEl().dom.value =
14406 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14412 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14413 * query allowing the query action to be canceled if needed.
14414 * @param {String} query The SQL query to execute
14415 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14416 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14417 * saved in the current store (defaults to false)
14419 doQuery : function(q, forceAll){
14421 if(q === undefined || q === null){
14426 forceAll: forceAll,
14430 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14435 forceAll = qe.forceAll;
14436 if(forceAll === true || (q.length >= this.minChars)){
14438 this.hasQuery = true;
14440 if(this.lastQuery != q || this.alwaysQuery){
14441 this.lastQuery = q;
14442 if(this.mode == 'local'){
14443 this.selectedIndex = -1;
14445 this.store.clearFilter();
14448 if(this.specialFilter){
14449 this.fireEvent('specialfilter', this);
14454 this.store.filter(this.displayField, q);
14457 this.store.fireEvent("datachanged", this.store);
14464 this.store.baseParams[this.queryParam] = q;
14466 var options = {params : this.getParams(q)};
14469 options.add = true;
14470 options.params.start = this.page * this.pageSize;
14473 this.store.load(options);
14476 * this code will make the page width larger, at the beginning, the list not align correctly,
14477 * we should expand the list on onLoad
14478 * so command out it
14483 this.selectedIndex = -1;
14488 this.loadNext = false;
14492 getParams : function(q){
14494 //p[this.queryParam] = q;
14498 p.limit = this.pageSize;
14504 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14506 collapse : function(){
14507 if(!this.isExpanded()){
14513 this.hasFocus = false;
14517 this.cancelBtn.hide();
14518 this.trigger.show();
14521 this.tickableInputEl().dom.value = '';
14522 this.tickableInputEl().blur();
14527 Roo.get(document).un('mousedown', this.collapseIf, this);
14528 Roo.get(document).un('mousewheel', this.collapseIf, this);
14529 if (!this.editable) {
14530 Roo.get(document).un('keydown', this.listKeyPress, this);
14532 this.fireEvent('collapse', this);
14538 collapseIf : function(e){
14539 var in_combo = e.within(this.el);
14540 var in_list = e.within(this.list);
14541 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14543 if (in_combo || in_list || is_list) {
14544 //e.stopPropagation();
14549 this.onTickableFooterButtonClick(e, false, false);
14557 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14559 expand : function(){
14561 if(this.isExpanded() || !this.hasFocus){
14565 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14566 this.list.setWidth(lw);
14572 this.restrictHeight();
14576 this.tickItems = Roo.apply([], this.item);
14579 this.cancelBtn.show();
14580 this.trigger.hide();
14583 this.tickableInputEl().focus();
14588 Roo.get(document).on('mousedown', this.collapseIf, this);
14589 Roo.get(document).on('mousewheel', this.collapseIf, this);
14590 if (!this.editable) {
14591 Roo.get(document).on('keydown', this.listKeyPress, this);
14594 this.fireEvent('expand', this);
14598 // Implements the default empty TriggerField.onTriggerClick function
14599 onTriggerClick : function(e)
14601 Roo.log('trigger click');
14603 if(this.disabled || !this.triggerList){
14608 this.loadNext = false;
14610 if(this.isExpanded()){
14612 if (!this.blockFocus) {
14613 this.inputEl().focus();
14617 this.hasFocus = true;
14618 if(this.triggerAction == 'all') {
14619 this.doQuery(this.allQuery, true);
14621 this.doQuery(this.getRawValue());
14623 if (!this.blockFocus) {
14624 this.inputEl().focus();
14629 onTickableTriggerClick : function(e)
14636 this.loadNext = false;
14637 this.hasFocus = true;
14639 if(this.triggerAction == 'all') {
14640 this.doQuery(this.allQuery, true);
14642 this.doQuery(this.getRawValue());
14646 onSearchFieldClick : function(e)
14648 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14649 this.onTickableFooterButtonClick(e, false, false);
14653 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
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 listKeyPress : function(e)
14670 //Roo.log('listkeypress');
14671 // scroll to first matching element based on key pres..
14672 if (e.isSpecialKey()) {
14675 var k = String.fromCharCode(e.getKey()).toUpperCase();
14678 var csel = this.view.getSelectedNodes();
14679 var cselitem = false;
14681 var ix = this.view.indexOf(csel[0]);
14682 cselitem = this.store.getAt(ix);
14683 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14689 this.store.each(function(v) {
14691 // start at existing selection.
14692 if (cselitem.id == v.id) {
14698 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14699 match = this.store.indexOf(v);
14705 if (match === false) {
14706 return true; // no more action?
14709 this.view.select(match);
14710 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14711 sn.scrollIntoView(sn.dom.parentNode, false);
14714 onViewScroll : function(e, t){
14716 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){
14720 this.hasQuery = true;
14722 this.loading = this.list.select('.loading', true).first();
14724 if(this.loading === null){
14725 this.list.createChild({
14727 cls: 'loading roo-select2-more-results roo-select2-active',
14728 html: 'Loading more results...'
14731 this.loading = this.list.select('.loading', true).first();
14733 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14735 this.loading.hide();
14738 this.loading.show();
14743 this.loadNext = true;
14745 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14750 addItem : function(o)
14752 var dv = ''; // display value
14754 if (this.displayField) {
14755 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14757 // this is an error condition!!!
14758 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14765 var choice = this.choices.createChild({
14767 cls: 'roo-select2-search-choice',
14776 cls: 'roo-select2-search-choice-close fa fa-times',
14781 }, this.searchField);
14783 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14785 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14793 this.inputEl().dom.value = '';
14798 onRemoveItem : function(e, _self, o)
14800 e.preventDefault();
14802 this.lastItem = Roo.apply([], this.item);
14804 var index = this.item.indexOf(o.data) * 1;
14807 Roo.log('not this item?!');
14811 this.item.splice(index, 1);
14816 this.fireEvent('remove', this, e);
14822 syncValue : function()
14824 if(!this.item.length){
14831 Roo.each(this.item, function(i){
14832 if(_this.valueField){
14833 value.push(i[_this.valueField]);
14840 this.value = value.join(',');
14842 if(this.hiddenField){
14843 this.hiddenField.dom.value = this.value;
14846 this.store.fireEvent("datachanged", this.store);
14851 clearItem : function()
14853 if(!this.multiple){
14859 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14867 if(this.tickable && !Roo.isTouch){
14868 this.view.refresh();
14872 inputEl: function ()
14874 if(Roo.isIOS && this.useNativeIOS){
14875 return this.el.select('select.roo-ios-select', true).first();
14878 if(Roo.isTouch && this.mobileTouchView){
14879 return this.el.select('input.form-control',true).first();
14883 return this.searchField;
14886 return this.el.select('input.form-control',true).first();
14889 onTickableFooterButtonClick : function(e, btn, el)
14891 e.preventDefault();
14893 this.lastItem = Roo.apply([], this.item);
14895 if(btn && btn.name == 'cancel'){
14896 this.tickItems = Roo.apply([], this.item);
14905 Roo.each(this.tickItems, function(o){
14913 validate : function()
14915 if(this.getVisibilityEl().hasClass('hidden')){
14919 var v = this.getRawValue();
14922 v = this.getValue();
14925 if(this.disabled || this.allowBlank || v.length){
14930 this.markInvalid();
14934 tickableInputEl : function()
14936 if(!this.tickable || !this.editable){
14937 return this.inputEl();
14940 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14944 getAutoCreateTouchView : function()
14949 cls: 'form-group' //input-group
14955 type : this.inputType,
14956 cls : 'form-control x-combo-noedit',
14957 autocomplete: 'new-password',
14958 placeholder : this.placeholder || '',
14963 input.name = this.name;
14967 input.cls += ' input-' + this.size;
14970 if (this.disabled) {
14971 input.disabled = true;
14982 inputblock.cls += ' input-group';
14984 inputblock.cn.unshift({
14986 cls : 'input-group-addon',
14991 if(this.removable && !this.multiple){
14992 inputblock.cls += ' roo-removable';
14994 inputblock.cn.push({
14997 cls : 'roo-combo-removable-btn close'
15001 if(this.hasFeedback && !this.allowBlank){
15003 inputblock.cls += ' has-feedback';
15005 inputblock.cn.push({
15007 cls: 'glyphicon form-control-feedback'
15014 inputblock.cls += (this.before) ? '' : ' input-group';
15016 inputblock.cn.push({
15018 cls : 'input-group-addon',
15029 cls: 'form-hidden-field'
15043 cls: 'form-hidden-field'
15047 cls: 'roo-select2-choices',
15051 cls: 'roo-select2-search-field',
15064 cls: 'roo-select2-container input-group roo-touchview-combobox ',
15070 if(!this.multiple && this.showToggleBtn){
15077 if (this.caret != false) {
15080 cls: 'fa fa-' + this.caret
15087 cls : 'input-group-addon btn dropdown-toggle',
15092 cls: 'combobox-clear',
15106 combobox.cls += ' roo-select2-container-multi';
15109 var align = this.labelAlign || this.parentLabelAlign();
15111 if (align ==='left' && this.fieldLabel.length) {
15116 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15117 tooltip : 'This field is required'
15121 cls : 'control-label',
15122 html : this.fieldLabel
15133 var labelCfg = cfg.cn[1];
15134 var contentCfg = cfg.cn[2];
15137 if(this.indicatorpos == 'right'){
15142 cls : 'control-label',
15146 html : this.fieldLabel
15150 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15151 tooltip : 'This field is required'
15164 labelCfg = cfg.cn[0];
15165 contentCfg = cfg.cn[1];
15170 if(this.labelWidth > 12){
15171 labelCfg.style = "width: " + this.labelWidth + 'px';
15174 if(this.labelWidth < 13 && this.labelmd == 0){
15175 this.labelmd = this.labelWidth;
15178 if(this.labellg > 0){
15179 labelCfg.cls += ' col-lg-' + this.labellg;
15180 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15183 if(this.labelmd > 0){
15184 labelCfg.cls += ' col-md-' + this.labelmd;
15185 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15188 if(this.labelsm > 0){
15189 labelCfg.cls += ' col-sm-' + this.labelsm;
15190 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15193 if(this.labelxs > 0){
15194 labelCfg.cls += ' col-xs-' + this.labelxs;
15195 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15199 } else if ( this.fieldLabel.length) {
15203 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15204 tooltip : 'This field is required'
15208 cls : 'control-label',
15209 html : this.fieldLabel
15220 if(this.indicatorpos == 'right'){
15224 cls : 'control-label',
15225 html : this.fieldLabel,
15229 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15230 tooltip : 'This field is required'
15247 var settings = this;
15249 ['xs','sm','md','lg'].map(function(size){
15250 if (settings[size]) {
15251 cfg.cls += ' col-' + size + '-' + settings[size];
15258 initTouchView : function()
15260 this.renderTouchView();
15262 this.touchViewEl.on('scroll', function(){
15263 this.el.dom.scrollTop = 0;
15266 this.originalValue = this.getValue();
15268 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15270 this.inputEl().on("click", this.showTouchView, this);
15271 if (this.triggerEl) {
15272 this.triggerEl.on("click", this.showTouchView, this);
15276 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15277 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15279 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15281 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15282 this.store.on('load', this.onTouchViewLoad, this);
15283 this.store.on('loadexception', this.onTouchViewLoadException, this);
15285 if(this.hiddenName){
15287 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15289 this.hiddenField.dom.value =
15290 this.hiddenValue !== undefined ? this.hiddenValue :
15291 this.value !== undefined ? this.value : '';
15293 this.el.dom.removeAttribute('name');
15294 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15298 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15299 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15302 if(this.removable && !this.multiple){
15303 var close = this.closeTriggerEl();
15305 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15306 close.on('click', this.removeBtnClick, this, close);
15310 * fix the bug in Safari iOS8
15312 this.inputEl().on("focus", function(e){
15313 document.activeElement.blur();
15316 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15323 renderTouchView : function()
15325 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15326 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15328 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15329 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15331 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15332 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15333 this.touchViewBodyEl.setStyle('overflow', 'auto');
15335 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15336 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15338 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15339 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15343 showTouchView : function()
15349 this.touchViewHeaderEl.hide();
15351 if(this.modalTitle.length){
15352 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15353 this.touchViewHeaderEl.show();
15356 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15357 this.touchViewEl.show();
15359 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15361 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15362 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15364 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15366 if(this.modalTitle.length){
15367 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15370 this.touchViewBodyEl.setHeight(bodyHeight);
15374 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15376 this.touchViewEl.addClass('in');
15379 if(this._touchViewMask){
15380 Roo.get(document.body).addClass("x-body-masked");
15381 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15382 this._touchViewMask.setStyle('z-index', 10000);
15383 this._touchViewMask.addClass('show');
15386 this.doTouchViewQuery();
15390 hideTouchView : function()
15392 this.touchViewEl.removeClass('in');
15396 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15398 this.touchViewEl.setStyle('display', 'none');
15401 if(this._touchViewMask){
15402 this._touchViewMask.removeClass('show');
15403 Roo.get(document.body).removeClass("x-body-masked");
15407 setTouchViewValue : function()
15414 Roo.each(this.tickItems, function(o){
15419 this.hideTouchView();
15422 doTouchViewQuery : function()
15431 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15435 if(!this.alwaysQuery || this.mode == 'local'){
15436 this.onTouchViewLoad();
15443 onTouchViewBeforeLoad : function(combo,opts)
15449 onTouchViewLoad : function()
15451 if(this.store.getCount() < 1){
15452 this.onTouchViewEmptyResults();
15456 this.clearTouchView();
15458 var rawValue = this.getRawValue();
15460 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15462 this.tickItems = [];
15464 this.store.data.each(function(d, rowIndex){
15465 var row = this.touchViewListGroup.createChild(template);
15467 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15468 row.addClass(d.data.cls);
15471 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15474 html : d.data[this.displayField]
15477 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15478 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15481 row.removeClass('selected');
15482 if(!this.multiple && this.valueField &&
15483 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15486 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15487 row.addClass('selected');
15490 if(this.multiple && this.valueField &&
15491 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15495 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15496 this.tickItems.push(d.data);
15499 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15503 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15505 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15507 if(this.modalTitle.length){
15508 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15511 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15513 if(this.mobile_restrict_height && listHeight < bodyHeight){
15514 this.touchViewBodyEl.setHeight(listHeight);
15519 if(firstChecked && listHeight > bodyHeight){
15520 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15525 onTouchViewLoadException : function()
15527 this.hideTouchView();
15530 onTouchViewEmptyResults : function()
15532 this.clearTouchView();
15534 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15536 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15540 clearTouchView : function()
15542 this.touchViewListGroup.dom.innerHTML = '';
15545 onTouchViewClick : function(e, el, o)
15547 e.preventDefault();
15550 var rowIndex = o.rowIndex;
15552 var r = this.store.getAt(rowIndex);
15554 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15556 if(!this.multiple){
15557 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15558 c.dom.removeAttribute('checked');
15561 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15563 this.setFromData(r.data);
15565 var close = this.closeTriggerEl();
15571 this.hideTouchView();
15573 this.fireEvent('select', this, r, rowIndex);
15578 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15579 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15580 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15584 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15585 this.addItem(r.data);
15586 this.tickItems.push(r.data);
15590 getAutoCreateNativeIOS : function()
15593 cls: 'form-group' //input-group,
15598 cls : 'roo-ios-select'
15602 combobox.name = this.name;
15605 if (this.disabled) {
15606 combobox.disabled = true;
15609 var settings = this;
15611 ['xs','sm','md','lg'].map(function(size){
15612 if (settings[size]) {
15613 cfg.cls += ' col-' + size + '-' + settings[size];
15623 initIOSView : function()
15625 this.store.on('load', this.onIOSViewLoad, this);
15630 onIOSViewLoad : function()
15632 if(this.store.getCount() < 1){
15636 this.clearIOSView();
15638 if(this.allowBlank) {
15640 var default_text = '-- SELECT --';
15642 if(this.placeholder.length){
15643 default_text = this.placeholder;
15646 if(this.emptyTitle.length){
15647 default_text += ' - ' + this.emptyTitle + ' -';
15650 var opt = this.inputEl().createChild({
15653 html : default_text
15657 o[this.valueField] = 0;
15658 o[this.displayField] = default_text;
15660 this.ios_options.push({
15667 this.store.data.each(function(d, rowIndex){
15671 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15672 html = d.data[this.displayField];
15677 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15678 value = d.data[this.valueField];
15687 if(this.value == d.data[this.valueField]){
15688 option['selected'] = true;
15691 var opt = this.inputEl().createChild(option);
15693 this.ios_options.push({
15700 this.inputEl().on('change', function(){
15701 this.fireEvent('select', this);
15706 clearIOSView: function()
15708 this.inputEl().dom.innerHTML = '';
15710 this.ios_options = [];
15713 setIOSValue: function(v)
15717 if(!this.ios_options){
15721 Roo.each(this.ios_options, function(opts){
15723 opts.el.dom.removeAttribute('selected');
15725 if(opts.data[this.valueField] != v){
15729 opts.el.dom.setAttribute('selected', true);
15735 * @cfg {Boolean} grow
15739 * @cfg {Number} growMin
15743 * @cfg {Number} growMax
15752 Roo.apply(Roo.bootstrap.ComboBox, {
15756 cls: 'modal-header',
15778 cls: 'list-group-item',
15782 cls: 'roo-combobox-list-group-item-value'
15786 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15800 listItemCheckbox : {
15802 cls: 'list-group-item',
15806 cls: 'roo-combobox-list-group-item-value'
15810 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15826 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15831 cls: 'modal-footer',
15839 cls: 'col-xs-6 text-left',
15842 cls: 'btn btn-danger roo-touch-view-cancel',
15848 cls: 'col-xs-6 text-right',
15851 cls: 'btn btn-success roo-touch-view-ok',
15862 Roo.apply(Roo.bootstrap.ComboBox, {
15864 touchViewTemplate : {
15866 cls: 'modal fade roo-combobox-touch-view',
15870 cls: 'modal-dialog',
15871 style : 'position:fixed', // we have to fix position....
15875 cls: 'modal-content',
15877 Roo.bootstrap.ComboBox.header,
15878 Roo.bootstrap.ComboBox.body,
15879 Roo.bootstrap.ComboBox.footer
15888 * Ext JS Library 1.1.1
15889 * Copyright(c) 2006-2007, Ext JS, LLC.
15891 * Originally Released Under LGPL - original licence link has changed is not relivant.
15894 * <script type="text/javascript">
15899 * @extends Roo.util.Observable
15900 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15901 * This class also supports single and multi selection modes. <br>
15902 * Create a data model bound view:
15904 var store = new Roo.data.Store(...);
15906 var view = new Roo.View({
15908 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15910 singleSelect: true,
15911 selectedClass: "ydataview-selected",
15915 // listen for node click?
15916 view.on("click", function(vw, index, node, e){
15917 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15921 dataModel.load("foobar.xml");
15923 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15925 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15926 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15928 * Note: old style constructor is still suported (container, template, config)
15931 * Create a new View
15932 * @param {Object} config The config object
15935 Roo.View = function(config, depreciated_tpl, depreciated_config){
15937 this.parent = false;
15939 if (typeof(depreciated_tpl) == 'undefined') {
15940 // new way.. - universal constructor.
15941 Roo.apply(this, config);
15942 this.el = Roo.get(this.el);
15945 this.el = Roo.get(config);
15946 this.tpl = depreciated_tpl;
15947 Roo.apply(this, depreciated_config);
15949 this.wrapEl = this.el.wrap().wrap();
15950 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15953 if(typeof(this.tpl) == "string"){
15954 this.tpl = new Roo.Template(this.tpl);
15956 // support xtype ctors..
15957 this.tpl = new Roo.factory(this.tpl, Roo);
15961 this.tpl.compile();
15966 * @event beforeclick
15967 * Fires before a click is processed. Returns false to cancel the default action.
15968 * @param {Roo.View} this
15969 * @param {Number} index The index of the target node
15970 * @param {HTMLElement} node The target node
15971 * @param {Roo.EventObject} e The raw event object
15973 "beforeclick" : true,
15976 * Fires when a template node is clicked.
15977 * @param {Roo.View} this
15978 * @param {Number} index The index of the target node
15979 * @param {HTMLElement} node The target node
15980 * @param {Roo.EventObject} e The raw event object
15985 * Fires when a template node is double clicked.
15986 * @param {Roo.View} this
15987 * @param {Number} index The index of the target node
15988 * @param {HTMLElement} node The target node
15989 * @param {Roo.EventObject} e The raw event object
15993 * @event contextmenu
15994 * Fires when a template node is right clicked.
15995 * @param {Roo.View} this
15996 * @param {Number} index The index of the target node
15997 * @param {HTMLElement} node The target node
15998 * @param {Roo.EventObject} e The raw event object
16000 "contextmenu" : true,
16002 * @event selectionchange
16003 * Fires when the selected nodes change.
16004 * @param {Roo.View} this
16005 * @param {Array} selections Array of the selected nodes
16007 "selectionchange" : true,
16010 * @event beforeselect
16011 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16012 * @param {Roo.View} this
16013 * @param {HTMLElement} node The node to be selected
16014 * @param {Array} selections Array of currently selected nodes
16016 "beforeselect" : true,
16018 * @event preparedata
16019 * Fires on every row to render, to allow you to change the data.
16020 * @param {Roo.View} this
16021 * @param {Object} data to be rendered (change this)
16023 "preparedata" : true
16031 "click": this.onClick,
16032 "dblclick": this.onDblClick,
16033 "contextmenu": this.onContextMenu,
16037 this.selections = [];
16039 this.cmp = new Roo.CompositeElementLite([]);
16041 this.store = Roo.factory(this.store, Roo.data);
16042 this.setStore(this.store, true);
16045 if ( this.footer && this.footer.xtype) {
16047 var fctr = this.wrapEl.appendChild(document.createElement("div"));
16049 this.footer.dataSource = this.store;
16050 this.footer.container = fctr;
16051 this.footer = Roo.factory(this.footer, Roo);
16052 fctr.insertFirst(this.el);
16054 // this is a bit insane - as the paging toolbar seems to detach the el..
16055 // dom.parentNode.parentNode.parentNode
16056 // they get detached?
16060 Roo.View.superclass.constructor.call(this);
16065 Roo.extend(Roo.View, Roo.util.Observable, {
16068 * @cfg {Roo.data.Store} store Data store to load data from.
16073 * @cfg {String|Roo.Element} el The container element.
16078 * @cfg {String|Roo.Template} tpl The template used by this View
16082 * @cfg {String} dataName the named area of the template to use as the data area
16083 * Works with domtemplates roo-name="name"
16087 * @cfg {String} selectedClass The css class to add to selected nodes
16089 selectedClass : "x-view-selected",
16091 * @cfg {String} emptyText The empty text to show when nothing is loaded.
16096 * @cfg {String} text to display on mask (default Loading)
16100 * @cfg {Boolean} multiSelect Allow multiple selection
16102 multiSelect : false,
16104 * @cfg {Boolean} singleSelect Allow single selection
16106 singleSelect: false,
16109 * @cfg {Boolean} toggleSelect - selecting
16111 toggleSelect : false,
16114 * @cfg {Boolean} tickable - selecting
16119 * Returns the element this view is bound to.
16120 * @return {Roo.Element}
16122 getEl : function(){
16123 return this.wrapEl;
16129 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16131 refresh : function(){
16132 //Roo.log('refresh');
16135 // if we are using something like 'domtemplate', then
16136 // the what gets used is:
16137 // t.applySubtemplate(NAME, data, wrapping data..)
16138 // the outer template then get' applied with
16139 // the store 'extra data'
16140 // and the body get's added to the
16141 // roo-name="data" node?
16142 // <span class='roo-tpl-{name}'></span> ?????
16146 this.clearSelections();
16147 this.el.update("");
16149 var records = this.store.getRange();
16150 if(records.length < 1) {
16152 // is this valid?? = should it render a template??
16154 this.el.update(this.emptyText);
16158 if (this.dataName) {
16159 this.el.update(t.apply(this.store.meta)); //????
16160 el = this.el.child('.roo-tpl-' + this.dataName);
16163 for(var i = 0, len = records.length; i < len; i++){
16164 var data = this.prepareData(records[i].data, i, records[i]);
16165 this.fireEvent("preparedata", this, data, i, records[i]);
16167 var d = Roo.apply({}, data);
16170 Roo.apply(d, {'roo-id' : Roo.id()});
16174 Roo.each(this.parent.item, function(item){
16175 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16178 Roo.apply(d, {'roo-data-checked' : 'checked'});
16182 html[html.length] = Roo.util.Format.trim(
16184 t.applySubtemplate(this.dataName, d, this.store.meta) :
16191 el.update(html.join(""));
16192 this.nodes = el.dom.childNodes;
16193 this.updateIndexes(0);
16198 * Function to override to reformat the data that is sent to
16199 * the template for each node.
16200 * DEPRICATED - use the preparedata event handler.
16201 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16202 * a JSON object for an UpdateManager bound view).
16204 prepareData : function(data, index, record)
16206 this.fireEvent("preparedata", this, data, index, record);
16210 onUpdate : function(ds, record){
16211 // Roo.log('on update');
16212 this.clearSelections();
16213 var index = this.store.indexOf(record);
16214 var n = this.nodes[index];
16215 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16216 n.parentNode.removeChild(n);
16217 this.updateIndexes(index, index);
16223 onAdd : function(ds, records, index)
16225 //Roo.log(['on Add', ds, records, index] );
16226 this.clearSelections();
16227 if(this.nodes.length == 0){
16231 var n = this.nodes[index];
16232 for(var i = 0, len = records.length; i < len; i++){
16233 var d = this.prepareData(records[i].data, i, records[i]);
16235 this.tpl.insertBefore(n, d);
16238 this.tpl.append(this.el, d);
16241 this.updateIndexes(index);
16244 onRemove : function(ds, record, index){
16245 // Roo.log('onRemove');
16246 this.clearSelections();
16247 var el = this.dataName ?
16248 this.el.child('.roo-tpl-' + this.dataName) :
16251 el.dom.removeChild(this.nodes[index]);
16252 this.updateIndexes(index);
16256 * Refresh an individual node.
16257 * @param {Number} index
16259 refreshNode : function(index){
16260 this.onUpdate(this.store, this.store.getAt(index));
16263 updateIndexes : function(startIndex, endIndex){
16264 var ns = this.nodes;
16265 startIndex = startIndex || 0;
16266 endIndex = endIndex || ns.length - 1;
16267 for(var i = startIndex; i <= endIndex; i++){
16268 ns[i].nodeIndex = i;
16273 * Changes the data store this view uses and refresh the view.
16274 * @param {Store} store
16276 setStore : function(store, initial){
16277 if(!initial && this.store){
16278 this.store.un("datachanged", this.refresh);
16279 this.store.un("add", this.onAdd);
16280 this.store.un("remove", this.onRemove);
16281 this.store.un("update", this.onUpdate);
16282 this.store.un("clear", this.refresh);
16283 this.store.un("beforeload", this.onBeforeLoad);
16284 this.store.un("load", this.onLoad);
16285 this.store.un("loadexception", this.onLoad);
16289 store.on("datachanged", this.refresh, this);
16290 store.on("add", this.onAdd, this);
16291 store.on("remove", this.onRemove, this);
16292 store.on("update", this.onUpdate, this);
16293 store.on("clear", this.refresh, this);
16294 store.on("beforeload", this.onBeforeLoad, this);
16295 store.on("load", this.onLoad, this);
16296 store.on("loadexception", this.onLoad, this);
16304 * onbeforeLoad - masks the loading area.
16307 onBeforeLoad : function(store,opts)
16309 //Roo.log('onBeforeLoad');
16311 this.el.update("");
16313 this.el.mask(this.mask ? this.mask : "Loading" );
16315 onLoad : function ()
16322 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16323 * @param {HTMLElement} node
16324 * @return {HTMLElement} The template node
16326 findItemFromChild : function(node){
16327 var el = this.dataName ?
16328 this.el.child('.roo-tpl-' + this.dataName,true) :
16331 if(!node || node.parentNode == el){
16334 var p = node.parentNode;
16335 while(p && p != el){
16336 if(p.parentNode == el){
16345 onClick : function(e){
16346 var item = this.findItemFromChild(e.getTarget());
16348 var index = this.indexOf(item);
16349 if(this.onItemClick(item, index, e) !== false){
16350 this.fireEvent("click", this, index, item, e);
16353 this.clearSelections();
16358 onContextMenu : function(e){
16359 var item = this.findItemFromChild(e.getTarget());
16361 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16366 onDblClick : function(e){
16367 var item = this.findItemFromChild(e.getTarget());
16369 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16373 onItemClick : function(item, index, e)
16375 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16378 if (this.toggleSelect) {
16379 var m = this.isSelected(item) ? 'unselect' : 'select';
16382 _t[m](item, true, false);
16385 if(this.multiSelect || this.singleSelect){
16386 if(this.multiSelect && e.shiftKey && this.lastSelection){
16387 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16389 this.select(item, this.multiSelect && e.ctrlKey);
16390 this.lastSelection = item;
16393 if(!this.tickable){
16394 e.preventDefault();
16402 * Get the number of selected nodes.
16405 getSelectionCount : function(){
16406 return this.selections.length;
16410 * Get the currently selected nodes.
16411 * @return {Array} An array of HTMLElements
16413 getSelectedNodes : function(){
16414 return this.selections;
16418 * Get the indexes of the selected nodes.
16421 getSelectedIndexes : function(){
16422 var indexes = [], s = this.selections;
16423 for(var i = 0, len = s.length; i < len; i++){
16424 indexes.push(s[i].nodeIndex);
16430 * Clear all selections
16431 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16433 clearSelections : function(suppressEvent){
16434 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16435 this.cmp.elements = this.selections;
16436 this.cmp.removeClass(this.selectedClass);
16437 this.selections = [];
16438 if(!suppressEvent){
16439 this.fireEvent("selectionchange", this, this.selections);
16445 * Returns true if the passed node is selected
16446 * @param {HTMLElement/Number} node The node or node index
16447 * @return {Boolean}
16449 isSelected : function(node){
16450 var s = this.selections;
16454 node = this.getNode(node);
16455 return s.indexOf(node) !== -1;
16460 * @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
16461 * @param {Boolean} keepExisting (optional) true to keep existing selections
16462 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16464 select : function(nodeInfo, keepExisting, suppressEvent){
16465 if(nodeInfo instanceof Array){
16467 this.clearSelections(true);
16469 for(var i = 0, len = nodeInfo.length; i < len; i++){
16470 this.select(nodeInfo[i], true, true);
16474 var node = this.getNode(nodeInfo);
16475 if(!node || this.isSelected(node)){
16476 return; // already selected.
16479 this.clearSelections(true);
16482 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16483 Roo.fly(node).addClass(this.selectedClass);
16484 this.selections.push(node);
16485 if(!suppressEvent){
16486 this.fireEvent("selectionchange", this, this.selections);
16494 * @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
16495 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16496 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16498 unselect : function(nodeInfo, keepExisting, suppressEvent)
16500 if(nodeInfo instanceof Array){
16501 Roo.each(this.selections, function(s) {
16502 this.unselect(s, nodeInfo);
16506 var node = this.getNode(nodeInfo);
16507 if(!node || !this.isSelected(node)){
16508 //Roo.log("not selected");
16509 return; // not selected.
16513 Roo.each(this.selections, function(s) {
16515 Roo.fly(node).removeClass(this.selectedClass);
16522 this.selections= ns;
16523 this.fireEvent("selectionchange", this, this.selections);
16527 * Gets a template node.
16528 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16529 * @return {HTMLElement} The node or null if it wasn't found
16531 getNode : function(nodeInfo){
16532 if(typeof nodeInfo == "string"){
16533 return document.getElementById(nodeInfo);
16534 }else if(typeof nodeInfo == "number"){
16535 return this.nodes[nodeInfo];
16541 * Gets a range template nodes.
16542 * @param {Number} startIndex
16543 * @param {Number} endIndex
16544 * @return {Array} An array of nodes
16546 getNodes : function(start, end){
16547 var ns = this.nodes;
16548 start = start || 0;
16549 end = typeof end == "undefined" ? ns.length - 1 : end;
16552 for(var i = start; i <= end; i++){
16556 for(var i = start; i >= end; i--){
16564 * Finds the index of the passed node
16565 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16566 * @return {Number} The index of the node or -1
16568 indexOf : function(node){
16569 node = this.getNode(node);
16570 if(typeof node.nodeIndex == "number"){
16571 return node.nodeIndex;
16573 var ns = this.nodes;
16574 for(var i = 0, len = ns.length; i < len; i++){
16585 * based on jquery fullcalendar
16589 Roo.bootstrap = Roo.bootstrap || {};
16591 * @class Roo.bootstrap.Calendar
16592 * @extends Roo.bootstrap.Component
16593 * Bootstrap Calendar class
16594 * @cfg {Boolean} loadMask (true|false) default false
16595 * @cfg {Object} header generate the user specific header of the calendar, default false
16598 * Create a new Container
16599 * @param {Object} config The config object
16604 Roo.bootstrap.Calendar = function(config){
16605 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16609 * Fires when a date is selected
16610 * @param {DatePicker} this
16611 * @param {Date} date The selected date
16615 * @event monthchange
16616 * Fires when the displayed month changes
16617 * @param {DatePicker} this
16618 * @param {Date} date The selected month
16620 'monthchange': true,
16622 * @event evententer
16623 * Fires when mouse over an event
16624 * @param {Calendar} this
16625 * @param {event} Event
16627 'evententer': true,
16629 * @event eventleave
16630 * Fires when the mouse leaves an
16631 * @param {Calendar} this
16634 'eventleave': true,
16636 * @event eventclick
16637 * Fires when the mouse click an
16638 * @param {Calendar} this
16647 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16650 * @cfg {Number} startDay
16651 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16659 getAutoCreate : function(){
16662 var fc_button = function(name, corner, style, content ) {
16663 return Roo.apply({},{
16665 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16667 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16670 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16681 style : 'width:100%',
16688 cls : 'fc-header-left',
16690 fc_button('prev', 'left', 'arrow', '‹' ),
16691 fc_button('next', 'right', 'arrow', '›' ),
16692 { tag: 'span', cls: 'fc-header-space' },
16693 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16701 cls : 'fc-header-center',
16705 cls: 'fc-header-title',
16708 html : 'month / year'
16716 cls : 'fc-header-right',
16718 /* fc_button('month', 'left', '', 'month' ),
16719 fc_button('week', '', '', 'week' ),
16720 fc_button('day', 'right', '', 'day' )
16732 header = this.header;
16735 var cal_heads = function() {
16737 // fixme - handle this.
16739 for (var i =0; i < Date.dayNames.length; i++) {
16740 var d = Date.dayNames[i];
16743 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16744 html : d.substring(0,3)
16748 ret[0].cls += ' fc-first';
16749 ret[6].cls += ' fc-last';
16752 var cal_cell = function(n) {
16755 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16760 cls: 'fc-day-number',
16764 cls: 'fc-day-content',
16768 style: 'position: relative;' // height: 17px;
16780 var cal_rows = function() {
16783 for (var r = 0; r < 6; r++) {
16790 for (var i =0; i < Date.dayNames.length; i++) {
16791 var d = Date.dayNames[i];
16792 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16795 row.cn[0].cls+=' fc-first';
16796 row.cn[0].cn[0].style = 'min-height:90px';
16797 row.cn[6].cls+=' fc-last';
16801 ret[0].cls += ' fc-first';
16802 ret[4].cls += ' fc-prev-last';
16803 ret[5].cls += ' fc-last';
16810 cls: 'fc-border-separate',
16811 style : 'width:100%',
16819 cls : 'fc-first fc-last',
16837 cls : 'fc-content',
16838 style : "position: relative;",
16841 cls : 'fc-view fc-view-month fc-grid',
16842 style : 'position: relative',
16843 unselectable : 'on',
16846 cls : 'fc-event-container',
16847 style : 'position:absolute;z-index:8;top:0;left:0;'
16865 initEvents : function()
16868 throw "can not find store for calendar";
16874 style: "text-align:center",
16878 style: "background-color:white;width:50%;margin:250 auto",
16882 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16893 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16895 var size = this.el.select('.fc-content', true).first().getSize();
16896 this.maskEl.setSize(size.width, size.height);
16897 this.maskEl.enableDisplayMode("block");
16898 if(!this.loadMask){
16899 this.maskEl.hide();
16902 this.store = Roo.factory(this.store, Roo.data);
16903 this.store.on('load', this.onLoad, this);
16904 this.store.on('beforeload', this.onBeforeLoad, this);
16908 this.cells = this.el.select('.fc-day',true);
16909 //Roo.log(this.cells);
16910 this.textNodes = this.el.query('.fc-day-number');
16911 this.cells.addClassOnOver('fc-state-hover');
16913 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16914 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16915 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16916 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16918 this.on('monthchange', this.onMonthChange, this);
16920 this.update(new Date().clearTime());
16923 resize : function() {
16924 var sz = this.el.getSize();
16926 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16927 this.el.select('.fc-day-content div',true).setHeight(34);
16932 showPrevMonth : function(e){
16933 this.update(this.activeDate.add("mo", -1));
16935 showToday : function(e){
16936 this.update(new Date().clearTime());
16939 showNextMonth : function(e){
16940 this.update(this.activeDate.add("mo", 1));
16944 showPrevYear : function(){
16945 this.update(this.activeDate.add("y", -1));
16949 showNextYear : function(){
16950 this.update(this.activeDate.add("y", 1));
16955 update : function(date)
16957 var vd = this.activeDate;
16958 this.activeDate = date;
16959 // if(vd && this.el){
16960 // var t = date.getTime();
16961 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16962 // Roo.log('using add remove');
16964 // this.fireEvent('monthchange', this, date);
16966 // this.cells.removeClass("fc-state-highlight");
16967 // this.cells.each(function(c){
16968 // if(c.dateValue == t){
16969 // c.addClass("fc-state-highlight");
16970 // setTimeout(function(){
16971 // try{c.dom.firstChild.focus();}catch(e){}
16981 var days = date.getDaysInMonth();
16983 var firstOfMonth = date.getFirstDateOfMonth();
16984 var startingPos = firstOfMonth.getDay()-this.startDay;
16986 if(startingPos < this.startDay){
16990 var pm = date.add(Date.MONTH, -1);
16991 var prevStart = pm.getDaysInMonth()-startingPos;
16993 this.cells = this.el.select('.fc-day',true);
16994 this.textNodes = this.el.query('.fc-day-number');
16995 this.cells.addClassOnOver('fc-state-hover');
16997 var cells = this.cells.elements;
16998 var textEls = this.textNodes;
17000 Roo.each(cells, function(cell){
17001 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17004 days += startingPos;
17006 // convert everything to numbers so it's fast
17007 var day = 86400000;
17008 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17011 //Roo.log(prevStart);
17013 var today = new Date().clearTime().getTime();
17014 var sel = date.clearTime().getTime();
17015 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17016 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17017 var ddMatch = this.disabledDatesRE;
17018 var ddText = this.disabledDatesText;
17019 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17020 var ddaysText = this.disabledDaysText;
17021 var format = this.format;
17023 var setCellClass = function(cal, cell){
17027 //Roo.log('set Cell Class');
17029 var t = d.getTime();
17033 cell.dateValue = t;
17035 cell.className += " fc-today";
17036 cell.className += " fc-state-highlight";
17037 cell.title = cal.todayText;
17040 // disable highlight in other month..
17041 //cell.className += " fc-state-highlight";
17046 cell.className = " fc-state-disabled";
17047 cell.title = cal.minText;
17051 cell.className = " fc-state-disabled";
17052 cell.title = cal.maxText;
17056 if(ddays.indexOf(d.getDay()) != -1){
17057 cell.title = ddaysText;
17058 cell.className = " fc-state-disabled";
17061 if(ddMatch && format){
17062 var fvalue = d.dateFormat(format);
17063 if(ddMatch.test(fvalue)){
17064 cell.title = ddText.replace("%0", fvalue);
17065 cell.className = " fc-state-disabled";
17069 if (!cell.initialClassName) {
17070 cell.initialClassName = cell.dom.className;
17073 cell.dom.className = cell.initialClassName + ' ' + cell.className;
17078 for(; i < startingPos; i++) {
17079 textEls[i].innerHTML = (++prevStart);
17080 d.setDate(d.getDate()+1);
17082 cells[i].className = "fc-past fc-other-month";
17083 setCellClass(this, cells[i]);
17088 for(; i < days; i++){
17089 intDay = i - startingPos + 1;
17090 textEls[i].innerHTML = (intDay);
17091 d.setDate(d.getDate()+1);
17093 cells[i].className = ''; // "x-date-active";
17094 setCellClass(this, cells[i]);
17098 for(; i < 42; i++) {
17099 textEls[i].innerHTML = (++extraDays);
17100 d.setDate(d.getDate()+1);
17102 cells[i].className = "fc-future fc-other-month";
17103 setCellClass(this, cells[i]);
17106 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17108 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17110 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17111 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17113 if(totalRows != 6){
17114 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17115 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17118 this.fireEvent('monthchange', this, date);
17122 if(!this.internalRender){
17123 var main = this.el.dom.firstChild;
17124 var w = main.offsetWidth;
17125 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17126 Roo.fly(main).setWidth(w);
17127 this.internalRender = true;
17128 // opera does not respect the auto grow header center column
17129 // then, after it gets a width opera refuses to recalculate
17130 // without a second pass
17131 if(Roo.isOpera && !this.secondPass){
17132 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17133 this.secondPass = true;
17134 this.update.defer(10, this, [date]);
17141 findCell : function(dt) {
17142 dt = dt.clearTime().getTime();
17144 this.cells.each(function(c){
17145 //Roo.log("check " +c.dateValue + '?=' + dt);
17146 if(c.dateValue == dt){
17156 findCells : function(ev) {
17157 var s = ev.start.clone().clearTime().getTime();
17159 var e= ev.end.clone().clearTime().getTime();
17162 this.cells.each(function(c){
17163 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17165 if(c.dateValue > e){
17168 if(c.dateValue < s){
17177 // findBestRow: function(cells)
17181 // for (var i =0 ; i < cells.length;i++) {
17182 // ret = Math.max(cells[i].rows || 0,ret);
17189 addItem : function(ev)
17191 // look for vertical location slot in
17192 var cells = this.findCells(ev);
17194 // ev.row = this.findBestRow(cells);
17196 // work out the location.
17200 for(var i =0; i < cells.length; i++) {
17202 cells[i].row = cells[0].row;
17205 cells[i].row = cells[i].row + 1;
17215 if (crow.start.getY() == cells[i].getY()) {
17217 crow.end = cells[i];
17234 cells[0].events.push(ev);
17236 this.calevents.push(ev);
17239 clearEvents: function() {
17241 if(!this.calevents){
17245 Roo.each(this.cells.elements, function(c){
17251 Roo.each(this.calevents, function(e) {
17252 Roo.each(e.els, function(el) {
17253 el.un('mouseenter' ,this.onEventEnter, this);
17254 el.un('mouseleave' ,this.onEventLeave, this);
17259 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17265 renderEvents: function()
17269 this.cells.each(function(c) {
17278 if(c.row != c.events.length){
17279 r = 4 - (4 - (c.row - c.events.length));
17282 c.events = ev.slice(0, r);
17283 c.more = ev.slice(r);
17285 if(c.more.length && c.more.length == 1){
17286 c.events.push(c.more.pop());
17289 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17293 this.cells.each(function(c) {
17295 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17298 for (var e = 0; e < c.events.length; e++){
17299 var ev = c.events[e];
17300 var rows = ev.rows;
17302 for(var i = 0; i < rows.length; i++) {
17304 // how many rows should it span..
17307 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17308 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17310 unselectable : "on",
17313 cls: 'fc-event-inner',
17317 // cls: 'fc-event-time',
17318 // html : cells.length > 1 ? '' : ev.time
17322 cls: 'fc-event-title',
17323 html : String.format('{0}', ev.title)
17330 cls: 'ui-resizable-handle ui-resizable-e',
17331 html : '  '
17338 cfg.cls += ' fc-event-start';
17340 if ((i+1) == rows.length) {
17341 cfg.cls += ' fc-event-end';
17344 var ctr = _this.el.select('.fc-event-container',true).first();
17345 var cg = ctr.createChild(cfg);
17347 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17348 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17350 var r = (c.more.length) ? 1 : 0;
17351 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17352 cg.setWidth(ebox.right - sbox.x -2);
17354 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17355 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17356 cg.on('click', _this.onEventClick, _this, ev);
17367 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17368 style : 'position: absolute',
17369 unselectable : "on",
17372 cls: 'fc-event-inner',
17376 cls: 'fc-event-title',
17384 cls: 'ui-resizable-handle ui-resizable-e',
17385 html : '  '
17391 var ctr = _this.el.select('.fc-event-container',true).first();
17392 var cg = ctr.createChild(cfg);
17394 var sbox = c.select('.fc-day-content',true).first().getBox();
17395 var ebox = c.select('.fc-day-content',true).first().getBox();
17397 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17398 cg.setWidth(ebox.right - sbox.x -2);
17400 cg.on('click', _this.onMoreEventClick, _this, c.more);
17410 onEventEnter: function (e, el,event,d) {
17411 this.fireEvent('evententer', this, el, event);
17414 onEventLeave: function (e, el,event,d) {
17415 this.fireEvent('eventleave', this, el, event);
17418 onEventClick: function (e, el,event,d) {
17419 this.fireEvent('eventclick', this, el, event);
17422 onMonthChange: function () {
17426 onMoreEventClick: function(e, el, more)
17430 this.calpopover.placement = 'right';
17431 this.calpopover.setTitle('More');
17433 this.calpopover.setContent('');
17435 var ctr = this.calpopover.el.select('.popover-content', true).first();
17437 Roo.each(more, function(m){
17439 cls : 'fc-event-hori fc-event-draggable',
17442 var cg = ctr.createChild(cfg);
17444 cg.on('click', _this.onEventClick, _this, m);
17447 this.calpopover.show(el);
17452 onLoad: function ()
17454 this.calevents = [];
17457 if(this.store.getCount() > 0){
17458 this.store.data.each(function(d){
17461 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17462 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17463 time : d.data.start_time,
17464 title : d.data.title,
17465 description : d.data.description,
17466 venue : d.data.venue
17471 this.renderEvents();
17473 if(this.calevents.length && this.loadMask){
17474 this.maskEl.hide();
17478 onBeforeLoad: function()
17480 this.clearEvents();
17482 this.maskEl.show();
17496 * @class Roo.bootstrap.Popover
17497 * @extends Roo.bootstrap.Component
17498 * Bootstrap Popover class
17499 * @cfg {String} html contents of the popover (or false to use children..)
17500 * @cfg {String} title of popover (or false to hide)
17501 * @cfg {String} placement how it is placed
17502 * @cfg {String} trigger click || hover (or false to trigger manually)
17503 * @cfg {String} over what (parent or false to trigger manually.)
17504 * @cfg {Number} delay - delay before showing
17507 * Create a new Popover
17508 * @param {Object} config The config object
17511 Roo.bootstrap.Popover = function(config){
17512 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17518 * After the popover show
17520 * @param {Roo.bootstrap.Popover} this
17525 * After the popover hide
17527 * @param {Roo.bootstrap.Popover} this
17533 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17535 title: 'Fill in a title',
17538 placement : 'right',
17539 trigger : 'hover', // hover
17545 can_build_overlaid : false,
17547 getChildContainer : function()
17549 return this.el.select('.popover-content',true).first();
17552 getAutoCreate : function(){
17555 cls : 'popover roo-dynamic',
17556 style: 'display:block',
17562 cls : 'popover-inner',
17566 cls: 'popover-title',
17570 cls : 'popover-content',
17581 setTitle: function(str)
17584 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17586 setContent: function(str)
17589 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17591 // as it get's added to the bottom of the page.
17592 onRender : function(ct, position)
17594 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17596 var cfg = Roo.apply({}, this.getAutoCreate());
17600 cfg.cls += ' ' + this.cls;
17603 cfg.style = this.style;
17605 //Roo.log("adding to ");
17606 this.el = Roo.get(document.body).createChild(cfg, position);
17607 // Roo.log(this.el);
17612 initEvents : function()
17614 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17615 this.el.enableDisplayMode('block');
17617 if (this.over === false) {
17620 if (this.triggers === false) {
17623 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17624 var triggers = this.trigger ? this.trigger.split(' ') : [];
17625 Roo.each(triggers, function(trigger) {
17627 if (trigger == 'click') {
17628 on_el.on('click', this.toggle, this);
17629 } else if (trigger != 'manual') {
17630 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17631 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17633 on_el.on(eventIn ,this.enter, this);
17634 on_el.on(eventOut, this.leave, this);
17645 toggle : function () {
17646 this.hoverState == 'in' ? this.leave() : this.enter();
17649 enter : function () {
17651 clearTimeout(this.timeout);
17653 this.hoverState = 'in';
17655 if (!this.delay || !this.delay.show) {
17660 this.timeout = setTimeout(function () {
17661 if (_t.hoverState == 'in') {
17664 }, this.delay.show)
17667 leave : function() {
17668 clearTimeout(this.timeout);
17670 this.hoverState = 'out';
17672 if (!this.delay || !this.delay.hide) {
17677 this.timeout = setTimeout(function () {
17678 if (_t.hoverState == 'out') {
17681 }, this.delay.hide)
17684 show : function (on_el)
17687 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17691 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17692 if (this.html !== false) {
17693 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17695 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17696 if (!this.title.length) {
17697 this.el.select('.popover-title',true).hide();
17700 var placement = typeof this.placement == 'function' ?
17701 this.placement.call(this, this.el, on_el) :
17704 var autoToken = /\s?auto?\s?/i;
17705 var autoPlace = autoToken.test(placement);
17707 placement = placement.replace(autoToken, '') || 'top';
17711 //this.el.setXY([0,0]);
17713 this.el.dom.style.display='block';
17714 this.el.addClass(placement);
17716 //this.el.appendTo(on_el);
17718 var p = this.getPosition();
17719 var box = this.el.getBox();
17724 var align = Roo.bootstrap.Popover.alignment[placement];
17727 this.el.alignTo(on_el, align[0],align[1]);
17728 //var arrow = this.el.select('.arrow',true).first();
17729 //arrow.set(align[2],
17731 this.el.addClass('in');
17734 if (this.el.hasClass('fade')) {
17738 this.hoverState = 'in';
17740 this.fireEvent('show', this);
17745 this.el.setXY([0,0]);
17746 this.el.removeClass('in');
17748 this.hoverState = null;
17750 this.fireEvent('hide', this);
17755 Roo.bootstrap.Popover.alignment = {
17756 'left' : ['r-l', [-10,0], 'right'],
17757 'right' : ['l-r', [10,0], 'left'],
17758 'bottom' : ['t-b', [0,10], 'top'],
17759 'top' : [ 'b-t', [0,-10], 'bottom']
17770 * @class Roo.bootstrap.Progress
17771 * @extends Roo.bootstrap.Component
17772 * Bootstrap Progress class
17773 * @cfg {Boolean} striped striped of the progress bar
17774 * @cfg {Boolean} active animated of the progress bar
17778 * Create a new Progress
17779 * @param {Object} config The config object
17782 Roo.bootstrap.Progress = function(config){
17783 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17786 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17791 getAutoCreate : function(){
17799 cfg.cls += ' progress-striped';
17803 cfg.cls += ' active';
17822 * @class Roo.bootstrap.ProgressBar
17823 * @extends Roo.bootstrap.Component
17824 * Bootstrap ProgressBar class
17825 * @cfg {Number} aria_valuenow aria-value now
17826 * @cfg {Number} aria_valuemin aria-value min
17827 * @cfg {Number} aria_valuemax aria-value max
17828 * @cfg {String} label label for the progress bar
17829 * @cfg {String} panel (success | info | warning | danger )
17830 * @cfg {String} role role of the progress bar
17831 * @cfg {String} sr_only text
17835 * Create a new ProgressBar
17836 * @param {Object} config The config object
17839 Roo.bootstrap.ProgressBar = function(config){
17840 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17843 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17847 aria_valuemax : 100,
17853 getAutoCreate : function()
17858 cls: 'progress-bar',
17859 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17871 cfg.role = this.role;
17874 if(this.aria_valuenow){
17875 cfg['aria-valuenow'] = this.aria_valuenow;
17878 if(this.aria_valuemin){
17879 cfg['aria-valuemin'] = this.aria_valuemin;
17882 if(this.aria_valuemax){
17883 cfg['aria-valuemax'] = this.aria_valuemax;
17886 if(this.label && !this.sr_only){
17887 cfg.html = this.label;
17891 cfg.cls += ' progress-bar-' + this.panel;
17897 update : function(aria_valuenow)
17899 this.aria_valuenow = aria_valuenow;
17901 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17916 * @class Roo.bootstrap.TabGroup
17917 * @extends Roo.bootstrap.Column
17918 * Bootstrap Column class
17919 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17920 * @cfg {Boolean} carousel true to make the group behave like a carousel
17921 * @cfg {Boolean} bullets show bullets for the panels
17922 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17923 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17924 * @cfg {Boolean} showarrow (true|false) show arrow default true
17927 * Create a new TabGroup
17928 * @param {Object} config The config object
17931 Roo.bootstrap.TabGroup = function(config){
17932 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17934 this.navId = Roo.id();
17937 Roo.bootstrap.TabGroup.register(this);
17941 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17944 transition : false,
17949 slideOnTouch : false,
17952 getAutoCreate : function()
17954 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17956 cfg.cls += ' tab-content';
17958 if (this.carousel) {
17959 cfg.cls += ' carousel slide';
17962 cls : 'carousel-inner',
17966 if(this.bullets && !Roo.isTouch){
17969 cls : 'carousel-bullets',
17973 if(this.bullets_cls){
17974 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17981 cfg.cn[0].cn.push(bullets);
17984 if(this.showarrow){
17985 cfg.cn[0].cn.push({
17987 class : 'carousel-arrow',
17991 class : 'carousel-prev',
17995 class : 'fa fa-chevron-left'
18001 class : 'carousel-next',
18005 class : 'fa fa-chevron-right'
18018 initEvents: function()
18020 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18021 // this.el.on("touchstart", this.onTouchStart, this);
18024 if(this.autoslide){
18027 this.slideFn = window.setInterval(function() {
18028 _this.showPanelNext();
18032 if(this.showarrow){
18033 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18034 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18040 // onTouchStart : function(e, el, o)
18042 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18046 // this.showPanelNext();
18050 getChildContainer : function()
18052 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18056 * register a Navigation item
18057 * @param {Roo.bootstrap.NavItem} the navitem to add
18059 register : function(item)
18061 this.tabs.push( item);
18062 item.navId = this.navId; // not really needed..
18067 getActivePanel : function()
18070 Roo.each(this.tabs, function(t) {
18080 getPanelByName : function(n)
18083 Roo.each(this.tabs, function(t) {
18084 if (t.tabId == n) {
18092 indexOfPanel : function(p)
18095 Roo.each(this.tabs, function(t,i) {
18096 if (t.tabId == p.tabId) {
18105 * show a specific panel
18106 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18107 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18109 showPanel : function (pan)
18111 if(this.transition || typeof(pan) == 'undefined'){
18112 Roo.log("waiting for the transitionend");
18116 if (typeof(pan) == 'number') {
18117 pan = this.tabs[pan];
18120 if (typeof(pan) == 'string') {
18121 pan = this.getPanelByName(pan);
18124 var cur = this.getActivePanel();
18127 Roo.log('pan or acitve pan is undefined');
18131 if (pan.tabId == this.getActivePanel().tabId) {
18135 if (false === cur.fireEvent('beforedeactivate')) {
18139 if(this.bullets > 0 && !Roo.isTouch){
18140 this.setActiveBullet(this.indexOfPanel(pan));
18143 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18145 this.transition = true;
18146 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18147 var lr = dir == 'next' ? 'left' : 'right';
18148 pan.el.addClass(dir); // or prev
18149 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18150 cur.el.addClass(lr); // or right
18151 pan.el.addClass(lr);
18154 cur.el.on('transitionend', function() {
18155 Roo.log("trans end?");
18157 pan.el.removeClass([lr,dir]);
18158 pan.setActive(true);
18160 cur.el.removeClass([lr]);
18161 cur.setActive(false);
18163 _this.transition = false;
18165 }, this, { single: true } );
18170 cur.setActive(false);
18171 pan.setActive(true);
18176 showPanelNext : function()
18178 var i = this.indexOfPanel(this.getActivePanel());
18180 if (i >= this.tabs.length - 1 && !this.autoslide) {
18184 if (i >= this.tabs.length - 1 && this.autoslide) {
18188 this.showPanel(this.tabs[i+1]);
18191 showPanelPrev : function()
18193 var i = this.indexOfPanel(this.getActivePanel());
18195 if (i < 1 && !this.autoslide) {
18199 if (i < 1 && this.autoslide) {
18200 i = this.tabs.length;
18203 this.showPanel(this.tabs[i-1]);
18207 addBullet: function()
18209 if(!this.bullets || Roo.isTouch){
18212 var ctr = this.el.select('.carousel-bullets',true).first();
18213 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18214 var bullet = ctr.createChild({
18215 cls : 'bullet bullet-' + i
18216 },ctr.dom.lastChild);
18221 bullet.on('click', (function(e, el, o, ii, t){
18223 e.preventDefault();
18225 this.showPanel(ii);
18227 if(this.autoslide && this.slideFn){
18228 clearInterval(this.slideFn);
18229 this.slideFn = window.setInterval(function() {
18230 _this.showPanelNext();
18234 }).createDelegate(this, [i, bullet], true));
18239 setActiveBullet : function(i)
18245 Roo.each(this.el.select('.bullet', true).elements, function(el){
18246 el.removeClass('selected');
18249 var bullet = this.el.select('.bullet-' + i, true).first();
18255 bullet.addClass('selected');
18266 Roo.apply(Roo.bootstrap.TabGroup, {
18270 * register a Navigation Group
18271 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18273 register : function(navgrp)
18275 this.groups[navgrp.navId] = navgrp;
18279 * fetch a Navigation Group based on the navigation ID
18280 * if one does not exist , it will get created.
18281 * @param {string} the navgroup to add
18282 * @returns {Roo.bootstrap.NavGroup} the navgroup
18284 get: function(navId) {
18285 if (typeof(this.groups[navId]) == 'undefined') {
18286 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18288 return this.groups[navId] ;
18303 * @class Roo.bootstrap.TabPanel
18304 * @extends Roo.bootstrap.Component
18305 * Bootstrap TabPanel class
18306 * @cfg {Boolean} active panel active
18307 * @cfg {String} html panel content
18308 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18309 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18310 * @cfg {String} href click to link..
18314 * Create a new TabPanel
18315 * @param {Object} config The config object
18318 Roo.bootstrap.TabPanel = function(config){
18319 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18323 * Fires when the active status changes
18324 * @param {Roo.bootstrap.TabPanel} this
18325 * @param {Boolean} state the new state
18330 * @event beforedeactivate
18331 * Fires before a tab is de-activated - can be used to do validation on a form.
18332 * @param {Roo.bootstrap.TabPanel} this
18333 * @return {Boolean} false if there is an error
18336 'beforedeactivate': true
18339 this.tabId = this.tabId || Roo.id();
18343 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18351 getAutoCreate : function(){
18354 // item is needed for carousel - not sure if it has any effect otherwise
18355 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18356 html: this.html || ''
18360 cfg.cls += ' active';
18364 cfg.tabId = this.tabId;
18371 initEvents: function()
18373 var p = this.parent();
18375 this.navId = this.navId || p.navId;
18377 if (typeof(this.navId) != 'undefined') {
18378 // not really needed.. but just in case.. parent should be a NavGroup.
18379 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18383 var i = tg.tabs.length - 1;
18385 if(this.active && tg.bullets > 0 && i < tg.bullets){
18386 tg.setActiveBullet(i);
18390 this.el.on('click', this.onClick, this);
18393 this.el.on("touchstart", this.onTouchStart, this);
18394 this.el.on("touchmove", this.onTouchMove, this);
18395 this.el.on("touchend", this.onTouchEnd, this);
18400 onRender : function(ct, position)
18402 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18405 setActive : function(state)
18407 Roo.log("panel - set active " + this.tabId + "=" + state);
18409 this.active = state;
18411 this.el.removeClass('active');
18413 } else if (!this.el.hasClass('active')) {
18414 this.el.addClass('active');
18417 this.fireEvent('changed', this, state);
18420 onClick : function(e)
18422 e.preventDefault();
18424 if(!this.href.length){
18428 window.location.href = this.href;
18437 onTouchStart : function(e)
18439 this.swiping = false;
18441 this.startX = e.browserEvent.touches[0].clientX;
18442 this.startY = e.browserEvent.touches[0].clientY;
18445 onTouchMove : function(e)
18447 this.swiping = true;
18449 this.endX = e.browserEvent.touches[0].clientX;
18450 this.endY = e.browserEvent.touches[0].clientY;
18453 onTouchEnd : function(e)
18460 var tabGroup = this.parent();
18462 if(this.endX > this.startX){ // swiping right
18463 tabGroup.showPanelPrev();
18467 if(this.startX > this.endX){ // swiping left
18468 tabGroup.showPanelNext();
18487 * @class Roo.bootstrap.DateField
18488 * @extends Roo.bootstrap.Input
18489 * Bootstrap DateField class
18490 * @cfg {Number} weekStart default 0
18491 * @cfg {String} viewMode default empty, (months|years)
18492 * @cfg {String} minViewMode default empty, (months|years)
18493 * @cfg {Number} startDate default -Infinity
18494 * @cfg {Number} endDate default Infinity
18495 * @cfg {Boolean} todayHighlight default false
18496 * @cfg {Boolean} todayBtn default false
18497 * @cfg {Boolean} calendarWeeks default false
18498 * @cfg {Object} daysOfWeekDisabled default empty
18499 * @cfg {Boolean} singleMode default false (true | false)
18501 * @cfg {Boolean} keyboardNavigation default true
18502 * @cfg {String} language default en
18505 * Create a new DateField
18506 * @param {Object} config The config object
18509 Roo.bootstrap.DateField = function(config){
18510 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18514 * Fires when this field show.
18515 * @param {Roo.bootstrap.DateField} this
18516 * @param {Mixed} date The date value
18521 * Fires when this field hide.
18522 * @param {Roo.bootstrap.DateField} this
18523 * @param {Mixed} date The date value
18528 * Fires when select a date.
18529 * @param {Roo.bootstrap.DateField} this
18530 * @param {Mixed} date The date value
18534 * @event beforeselect
18535 * Fires when before select a date.
18536 * @param {Roo.bootstrap.DateField} this
18537 * @param {Mixed} date The date value
18539 beforeselect : true
18543 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18546 * @cfg {String} format
18547 * The default date format string which can be overriden for localization support. The format must be
18548 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18552 * @cfg {String} altFormats
18553 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18554 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18556 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18564 todayHighlight : false,
18570 keyboardNavigation: true,
18572 calendarWeeks: false,
18574 startDate: -Infinity,
18578 daysOfWeekDisabled: [],
18582 singleMode : false,
18584 UTCDate: function()
18586 return new Date(Date.UTC.apply(Date, arguments));
18589 UTCToday: function()
18591 var today = new Date();
18592 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18595 getDate: function() {
18596 var d = this.getUTCDate();
18597 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18600 getUTCDate: function() {
18604 setDate: function(d) {
18605 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18608 setUTCDate: function(d) {
18610 this.setValue(this.formatDate(this.date));
18613 onRender: function(ct, position)
18616 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18618 this.language = this.language || 'en';
18619 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18620 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18622 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18623 this.format = this.format || 'm/d/y';
18624 this.isInline = false;
18625 this.isInput = true;
18626 this.component = this.el.select('.add-on', true).first() || false;
18627 this.component = (this.component && this.component.length === 0) ? false : this.component;
18628 this.hasInput = this.component && this.inputEl().length;
18630 if (typeof(this.minViewMode === 'string')) {
18631 switch (this.minViewMode) {
18633 this.minViewMode = 1;
18636 this.minViewMode = 2;
18639 this.minViewMode = 0;
18644 if (typeof(this.viewMode === 'string')) {
18645 switch (this.viewMode) {
18658 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18660 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18662 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18664 this.picker().on('mousedown', this.onMousedown, this);
18665 this.picker().on('click', this.onClick, this);
18667 this.picker().addClass('datepicker-dropdown');
18669 this.startViewMode = this.viewMode;
18671 if(this.singleMode){
18672 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18673 v.setVisibilityMode(Roo.Element.DISPLAY);
18677 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18678 v.setStyle('width', '189px');
18682 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18683 if(!this.calendarWeeks){
18688 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18689 v.attr('colspan', function(i, val){
18690 return parseInt(val) + 1;
18695 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18697 this.setStartDate(this.startDate);
18698 this.setEndDate(this.endDate);
18700 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18707 if(this.isInline) {
18712 picker : function()
18714 return this.pickerEl;
18715 // return this.el.select('.datepicker', true).first();
18718 fillDow: function()
18720 var dowCnt = this.weekStart;
18729 if(this.calendarWeeks){
18737 while (dowCnt < this.weekStart + 7) {
18741 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18745 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18748 fillMonths: function()
18751 var months = this.picker().select('>.datepicker-months td', true).first();
18753 months.dom.innerHTML = '';
18759 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18762 months.createChild(month);
18769 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;
18771 if (this.date < this.startDate) {
18772 this.viewDate = new Date(this.startDate);
18773 } else if (this.date > this.endDate) {
18774 this.viewDate = new Date(this.endDate);
18776 this.viewDate = new Date(this.date);
18784 var d = new Date(this.viewDate),
18785 year = d.getUTCFullYear(),
18786 month = d.getUTCMonth(),
18787 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18788 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18789 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18790 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18791 currentDate = this.date && this.date.valueOf(),
18792 today = this.UTCToday();
18794 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18796 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18798 // this.picker.select('>tfoot th.today').
18799 // .text(dates[this.language].today)
18800 // .toggle(this.todayBtn !== false);
18802 this.updateNavArrows();
18805 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18807 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18809 prevMonth.setUTCDate(day);
18811 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18813 var nextMonth = new Date(prevMonth);
18815 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18817 nextMonth = nextMonth.valueOf();
18819 var fillMonths = false;
18821 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18823 while(prevMonth.valueOf() <= nextMonth) {
18826 if (prevMonth.getUTCDay() === this.weekStart) {
18828 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18836 if(this.calendarWeeks){
18837 // ISO 8601: First week contains first thursday.
18838 // ISO also states week starts on Monday, but we can be more abstract here.
18840 // Start of current week: based on weekstart/current date
18841 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18842 // Thursday of this week
18843 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18844 // First Thursday of year, year from thursday
18845 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18846 // Calendar week: ms between thursdays, div ms per day, div 7 days
18847 calWeek = (th - yth) / 864e5 / 7 + 1;
18849 fillMonths.cn.push({
18857 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18859 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18862 if (this.todayHighlight &&
18863 prevMonth.getUTCFullYear() == today.getFullYear() &&
18864 prevMonth.getUTCMonth() == today.getMonth() &&
18865 prevMonth.getUTCDate() == today.getDate()) {
18866 clsName += ' today';
18869 if (currentDate && prevMonth.valueOf() === currentDate) {
18870 clsName += ' active';
18873 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18874 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18875 clsName += ' disabled';
18878 fillMonths.cn.push({
18880 cls: 'day ' + clsName,
18881 html: prevMonth.getDate()
18884 prevMonth.setDate(prevMonth.getDate()+1);
18887 var currentYear = this.date && this.date.getUTCFullYear();
18888 var currentMonth = this.date && this.date.getUTCMonth();
18890 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18892 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18893 v.removeClass('active');
18895 if(currentYear === year && k === currentMonth){
18896 v.addClass('active');
18899 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18900 v.addClass('disabled');
18906 year = parseInt(year/10, 10) * 10;
18908 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18910 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18913 for (var i = -1; i < 11; i++) {
18914 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18916 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18924 showMode: function(dir)
18927 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18930 Roo.each(this.picker().select('>div',true).elements, function(v){
18931 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18934 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18939 if(this.isInline) {
18943 this.picker().removeClass(['bottom', 'top']);
18945 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18947 * place to the top of element!
18951 this.picker().addClass('top');
18952 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18957 this.picker().addClass('bottom');
18959 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18962 parseDate : function(value)
18964 if(!value || value instanceof Date){
18967 var v = Date.parseDate(value, this.format);
18968 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18969 v = Date.parseDate(value, 'Y-m-d');
18971 if(!v && this.altFormats){
18972 if(!this.altFormatsArray){
18973 this.altFormatsArray = this.altFormats.split("|");
18975 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18976 v = Date.parseDate(value, this.altFormatsArray[i]);
18982 formatDate : function(date, fmt)
18984 return (!date || !(date instanceof Date)) ?
18985 date : date.dateFormat(fmt || this.format);
18988 onFocus : function()
18990 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18994 onBlur : function()
18996 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18998 var d = this.inputEl().getValue();
19005 showPopup : function()
19007 this.picker().show();
19011 this.fireEvent('showpopup', this, this.date);
19014 hidePopup : function()
19016 if(this.isInline) {
19019 this.picker().hide();
19020 this.viewMode = this.startViewMode;
19023 this.fireEvent('hidepopup', this, this.date);
19027 onMousedown: function(e)
19029 e.stopPropagation();
19030 e.preventDefault();
19035 Roo.bootstrap.DateField.superclass.keyup.call(this);
19039 setValue: function(v)
19041 if(this.fireEvent('beforeselect', this, v) !== false){
19042 var d = new Date(this.parseDate(v) ).clearTime();
19044 if(isNaN(d.getTime())){
19045 this.date = this.viewDate = '';
19046 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19050 v = this.formatDate(d);
19052 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19054 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19058 this.fireEvent('select', this, this.date);
19062 getValue: function()
19064 return this.formatDate(this.date);
19067 fireKey: function(e)
19069 if (!this.picker().isVisible()){
19070 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19076 var dateChanged = false,
19078 newDate, newViewDate;
19083 e.preventDefault();
19087 if (!this.keyboardNavigation) {
19090 dir = e.keyCode == 37 ? -1 : 1;
19093 newDate = this.moveYear(this.date, dir);
19094 newViewDate = this.moveYear(this.viewDate, dir);
19095 } else if (e.shiftKey){
19096 newDate = this.moveMonth(this.date, dir);
19097 newViewDate = this.moveMonth(this.viewDate, dir);
19099 newDate = new Date(this.date);
19100 newDate.setUTCDate(this.date.getUTCDate() + dir);
19101 newViewDate = new Date(this.viewDate);
19102 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19104 if (this.dateWithinRange(newDate)){
19105 this.date = newDate;
19106 this.viewDate = newViewDate;
19107 this.setValue(this.formatDate(this.date));
19109 e.preventDefault();
19110 dateChanged = true;
19115 if (!this.keyboardNavigation) {
19118 dir = e.keyCode == 38 ? -1 : 1;
19120 newDate = this.moveYear(this.date, dir);
19121 newViewDate = this.moveYear(this.viewDate, dir);
19122 } else if (e.shiftKey){
19123 newDate = this.moveMonth(this.date, dir);
19124 newViewDate = this.moveMonth(this.viewDate, dir);
19126 newDate = new Date(this.date);
19127 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19128 newViewDate = new Date(this.viewDate);
19129 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19131 if (this.dateWithinRange(newDate)){
19132 this.date = newDate;
19133 this.viewDate = newViewDate;
19134 this.setValue(this.formatDate(this.date));
19136 e.preventDefault();
19137 dateChanged = true;
19141 this.setValue(this.formatDate(this.date));
19143 e.preventDefault();
19146 this.setValue(this.formatDate(this.date));
19160 onClick: function(e)
19162 e.stopPropagation();
19163 e.preventDefault();
19165 var target = e.getTarget();
19167 if(target.nodeName.toLowerCase() === 'i'){
19168 target = Roo.get(target).dom.parentNode;
19171 var nodeName = target.nodeName;
19172 var className = target.className;
19173 var html = target.innerHTML;
19174 //Roo.log(nodeName);
19176 switch(nodeName.toLowerCase()) {
19178 switch(className) {
19184 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19185 switch(this.viewMode){
19187 this.viewDate = this.moveMonth(this.viewDate, dir);
19191 this.viewDate = this.moveYear(this.viewDate, dir);
19197 var date = new Date();
19198 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19200 this.setValue(this.formatDate(this.date));
19207 if (className.indexOf('disabled') < 0) {
19208 this.viewDate.setUTCDate(1);
19209 if (className.indexOf('month') > -1) {
19210 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19212 var year = parseInt(html, 10) || 0;
19213 this.viewDate.setUTCFullYear(year);
19217 if(this.singleMode){
19218 this.setValue(this.formatDate(this.viewDate));
19229 //Roo.log(className);
19230 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19231 var day = parseInt(html, 10) || 1;
19232 var year = this.viewDate.getUTCFullYear(),
19233 month = this.viewDate.getUTCMonth();
19235 if (className.indexOf('old') > -1) {
19242 } else if (className.indexOf('new') > -1) {
19250 //Roo.log([year,month,day]);
19251 this.date = this.UTCDate(year, month, day,0,0,0,0);
19252 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19254 //Roo.log(this.formatDate(this.date));
19255 this.setValue(this.formatDate(this.date));
19262 setStartDate: function(startDate)
19264 this.startDate = startDate || -Infinity;
19265 if (this.startDate !== -Infinity) {
19266 this.startDate = this.parseDate(this.startDate);
19269 this.updateNavArrows();
19272 setEndDate: function(endDate)
19274 this.endDate = endDate || Infinity;
19275 if (this.endDate !== Infinity) {
19276 this.endDate = this.parseDate(this.endDate);
19279 this.updateNavArrows();
19282 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19284 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19285 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19286 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19288 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19289 return parseInt(d, 10);
19292 this.updateNavArrows();
19295 updateNavArrows: function()
19297 if(this.singleMode){
19301 var d = new Date(this.viewDate),
19302 year = d.getUTCFullYear(),
19303 month = d.getUTCMonth();
19305 Roo.each(this.picker().select('.prev', true).elements, function(v){
19307 switch (this.viewMode) {
19310 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19316 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19323 Roo.each(this.picker().select('.next', true).elements, function(v){
19325 switch (this.viewMode) {
19328 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19334 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19342 moveMonth: function(date, dir)
19347 var new_date = new Date(date.valueOf()),
19348 day = new_date.getUTCDate(),
19349 month = new_date.getUTCMonth(),
19350 mag = Math.abs(dir),
19352 dir = dir > 0 ? 1 : -1;
19355 // If going back one month, make sure month is not current month
19356 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19358 return new_date.getUTCMonth() == month;
19360 // If going forward one month, make sure month is as expected
19361 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19363 return new_date.getUTCMonth() != new_month;
19365 new_month = month + dir;
19366 new_date.setUTCMonth(new_month);
19367 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19368 if (new_month < 0 || new_month > 11) {
19369 new_month = (new_month + 12) % 12;
19372 // For magnitudes >1, move one month at a time...
19373 for (var i=0; i<mag; i++) {
19374 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19375 new_date = this.moveMonth(new_date, dir);
19377 // ...then reset the day, keeping it in the new month
19378 new_month = new_date.getUTCMonth();
19379 new_date.setUTCDate(day);
19381 return new_month != new_date.getUTCMonth();
19384 // Common date-resetting loop -- if date is beyond end of month, make it
19387 new_date.setUTCDate(--day);
19388 new_date.setUTCMonth(new_month);
19393 moveYear: function(date, dir)
19395 return this.moveMonth(date, dir*12);
19398 dateWithinRange: function(date)
19400 return date >= this.startDate && date <= this.endDate;
19406 this.picker().remove();
19409 validateValue : function(value)
19411 if(this.getVisibilityEl().hasClass('hidden')){
19415 if(value.length < 1) {
19416 if(this.allowBlank){
19422 if(value.length < this.minLength){
19425 if(value.length > this.maxLength){
19429 var vt = Roo.form.VTypes;
19430 if(!vt[this.vtype](value, this)){
19434 if(typeof this.validator == "function"){
19435 var msg = this.validator(value);
19441 if(this.regex && !this.regex.test(value)){
19445 if(typeof(this.parseDate(value)) == 'undefined'){
19449 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19453 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19463 this.date = this.viewDate = '';
19465 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19470 Roo.apply(Roo.bootstrap.DateField, {
19481 html: '<i class="fa fa-arrow-left"/>'
19491 html: '<i class="fa fa-arrow-right"/>'
19533 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19534 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19535 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19536 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19537 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19550 navFnc: 'FullYear',
19555 navFnc: 'FullYear',
19560 Roo.apply(Roo.bootstrap.DateField, {
19564 cls: 'datepicker dropdown-menu roo-dynamic',
19568 cls: 'datepicker-days',
19572 cls: 'table-condensed',
19574 Roo.bootstrap.DateField.head,
19578 Roo.bootstrap.DateField.footer
19585 cls: 'datepicker-months',
19589 cls: 'table-condensed',
19591 Roo.bootstrap.DateField.head,
19592 Roo.bootstrap.DateField.content,
19593 Roo.bootstrap.DateField.footer
19600 cls: 'datepicker-years',
19604 cls: 'table-condensed',
19606 Roo.bootstrap.DateField.head,
19607 Roo.bootstrap.DateField.content,
19608 Roo.bootstrap.DateField.footer
19627 * @class Roo.bootstrap.TimeField
19628 * @extends Roo.bootstrap.Input
19629 * Bootstrap DateField class
19633 * Create a new TimeField
19634 * @param {Object} config The config object
19637 Roo.bootstrap.TimeField = function(config){
19638 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19642 * Fires when this field show.
19643 * @param {Roo.bootstrap.DateField} thisthis
19644 * @param {Mixed} date The date value
19649 * Fires when this field hide.
19650 * @param {Roo.bootstrap.DateField} this
19651 * @param {Mixed} date The date value
19656 * Fires when select a date.
19657 * @param {Roo.bootstrap.DateField} this
19658 * @param {Mixed} date The date value
19664 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19667 * @cfg {String} format
19668 * The default time format string which can be overriden for localization support. The format must be
19669 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19673 onRender: function(ct, position)
19676 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19678 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19680 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19682 this.pop = this.picker().select('>.datepicker-time',true).first();
19683 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19685 this.picker().on('mousedown', this.onMousedown, this);
19686 this.picker().on('click', this.onClick, this);
19688 this.picker().addClass('datepicker-dropdown');
19693 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19694 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19695 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19696 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19697 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19698 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19702 fireKey: function(e){
19703 if (!this.picker().isVisible()){
19704 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19710 e.preventDefault();
19718 this.onTogglePeriod();
19721 this.onIncrementMinutes();
19724 this.onDecrementMinutes();
19733 onClick: function(e) {
19734 e.stopPropagation();
19735 e.preventDefault();
19738 picker : function()
19740 return this.el.select('.datepicker', true).first();
19743 fillTime: function()
19745 var time = this.pop.select('tbody', true).first();
19747 time.dom.innerHTML = '';
19762 cls: 'hours-up glyphicon glyphicon-chevron-up'
19782 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19803 cls: 'timepicker-hour',
19818 cls: 'timepicker-minute',
19833 cls: 'btn btn-primary period',
19855 cls: 'hours-down glyphicon glyphicon-chevron-down'
19875 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19893 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19900 var hours = this.time.getHours();
19901 var minutes = this.time.getMinutes();
19914 hours = hours - 12;
19918 hours = '0' + hours;
19922 minutes = '0' + minutes;
19925 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19926 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19927 this.pop.select('button', true).first().dom.innerHTML = period;
19933 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19935 var cls = ['bottom'];
19937 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19944 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19949 this.picker().addClass(cls.join('-'));
19953 Roo.each(cls, function(c){
19955 _this.picker().setTop(_this.inputEl().getHeight());
19959 _this.picker().setTop(0 - _this.picker().getHeight());
19964 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19968 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19975 onFocus : function()
19977 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19981 onBlur : function()
19983 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19989 this.picker().show();
19994 this.fireEvent('show', this, this.date);
19999 this.picker().hide();
20002 this.fireEvent('hide', this, this.date);
20005 setTime : function()
20008 this.setValue(this.time.format(this.format));
20010 this.fireEvent('select', this, this.date);
20015 onMousedown: function(e){
20016 e.stopPropagation();
20017 e.preventDefault();
20020 onIncrementHours: function()
20022 Roo.log('onIncrementHours');
20023 this.time = this.time.add(Date.HOUR, 1);
20028 onDecrementHours: function()
20030 Roo.log('onDecrementHours');
20031 this.time = this.time.add(Date.HOUR, -1);
20035 onIncrementMinutes: function()
20037 Roo.log('onIncrementMinutes');
20038 this.time = this.time.add(Date.MINUTE, 1);
20042 onDecrementMinutes: function()
20044 Roo.log('onDecrementMinutes');
20045 this.time = this.time.add(Date.MINUTE, -1);
20049 onTogglePeriod: function()
20051 Roo.log('onTogglePeriod');
20052 this.time = this.time.add(Date.HOUR, 12);
20059 Roo.apply(Roo.bootstrap.TimeField, {
20089 cls: 'btn btn-info ok',
20101 Roo.apply(Roo.bootstrap.TimeField, {
20105 cls: 'datepicker dropdown-menu',
20109 cls: 'datepicker-time',
20113 cls: 'table-condensed',
20115 Roo.bootstrap.TimeField.content,
20116 Roo.bootstrap.TimeField.footer
20135 * @class Roo.bootstrap.MonthField
20136 * @extends Roo.bootstrap.Input
20137 * Bootstrap MonthField class
20139 * @cfg {String} language default en
20142 * Create a new MonthField
20143 * @param {Object} config The config object
20146 Roo.bootstrap.MonthField = function(config){
20147 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20152 * Fires when this field show.
20153 * @param {Roo.bootstrap.MonthField} this
20154 * @param {Mixed} date The date value
20159 * Fires when this field hide.
20160 * @param {Roo.bootstrap.MonthField} this
20161 * @param {Mixed} date The date value
20166 * Fires when select a date.
20167 * @param {Roo.bootstrap.MonthField} this
20168 * @param {String} oldvalue The old value
20169 * @param {String} newvalue The new value
20175 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20177 onRender: function(ct, position)
20180 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20182 this.language = this.language || 'en';
20183 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20184 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20186 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20187 this.isInline = false;
20188 this.isInput = true;
20189 this.component = this.el.select('.add-on', true).first() || false;
20190 this.component = (this.component && this.component.length === 0) ? false : this.component;
20191 this.hasInput = this.component && this.inputEL().length;
20193 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20195 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20197 this.picker().on('mousedown', this.onMousedown, this);
20198 this.picker().on('click', this.onClick, this);
20200 this.picker().addClass('datepicker-dropdown');
20202 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20203 v.setStyle('width', '189px');
20210 if(this.isInline) {
20216 setValue: function(v, suppressEvent)
20218 var o = this.getValue();
20220 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20224 if(suppressEvent !== true){
20225 this.fireEvent('select', this, o, v);
20230 getValue: function()
20235 onClick: function(e)
20237 e.stopPropagation();
20238 e.preventDefault();
20240 var target = e.getTarget();
20242 if(target.nodeName.toLowerCase() === 'i'){
20243 target = Roo.get(target).dom.parentNode;
20246 var nodeName = target.nodeName;
20247 var className = target.className;
20248 var html = target.innerHTML;
20250 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20254 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20256 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20262 picker : function()
20264 return this.pickerEl;
20267 fillMonths: function()
20270 var months = this.picker().select('>.datepicker-months td', true).first();
20272 months.dom.innerHTML = '';
20278 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20281 months.createChild(month);
20290 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20291 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20294 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20295 e.removeClass('active');
20297 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20298 e.addClass('active');
20305 if(this.isInline) {
20309 this.picker().removeClass(['bottom', 'top']);
20311 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20313 * place to the top of element!
20317 this.picker().addClass('top');
20318 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20323 this.picker().addClass('bottom');
20325 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20328 onFocus : function()
20330 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20334 onBlur : function()
20336 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20338 var d = this.inputEl().getValue();
20347 this.picker().show();
20348 this.picker().select('>.datepicker-months', true).first().show();
20352 this.fireEvent('show', this, this.date);
20357 if(this.isInline) {
20360 this.picker().hide();
20361 this.fireEvent('hide', this, this.date);
20365 onMousedown: function(e)
20367 e.stopPropagation();
20368 e.preventDefault();
20373 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20377 fireKey: function(e)
20379 if (!this.picker().isVisible()){
20380 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20391 e.preventDefault();
20395 dir = e.keyCode == 37 ? -1 : 1;
20397 this.vIndex = this.vIndex + dir;
20399 if(this.vIndex < 0){
20403 if(this.vIndex > 11){
20407 if(isNaN(this.vIndex)){
20411 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20417 dir = e.keyCode == 38 ? -1 : 1;
20419 this.vIndex = this.vIndex + dir * 4;
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]);
20438 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20439 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20443 e.preventDefault();
20446 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20447 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20463 this.picker().remove();
20468 Roo.apply(Roo.bootstrap.MonthField, {
20487 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20488 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20493 Roo.apply(Roo.bootstrap.MonthField, {
20497 cls: 'datepicker dropdown-menu roo-dynamic',
20501 cls: 'datepicker-months',
20505 cls: 'table-condensed',
20507 Roo.bootstrap.DateField.content
20527 * @class Roo.bootstrap.CheckBox
20528 * @extends Roo.bootstrap.Input
20529 * Bootstrap CheckBox class
20531 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20532 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20533 * @cfg {String} boxLabel The text that appears beside the checkbox
20534 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20535 * @cfg {Boolean} checked initnal the element
20536 * @cfg {Boolean} inline inline the element (default false)
20537 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20538 * @cfg {String} tooltip label tooltip
20541 * Create a new CheckBox
20542 * @param {Object} config The config object
20545 Roo.bootstrap.CheckBox = function(config){
20546 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20551 * Fires when the element is checked or unchecked.
20552 * @param {Roo.bootstrap.CheckBox} this This input
20553 * @param {Boolean} checked The new checked value
20558 * Fires when the element is click.
20559 * @param {Roo.bootstrap.CheckBox} this This input
20566 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20568 inputType: 'checkbox',
20577 getAutoCreate : function()
20579 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20585 cfg.cls = 'form-group ' + this.inputType; //input-group
20588 cfg.cls += ' ' + this.inputType + '-inline';
20594 type : this.inputType,
20595 value : this.inputValue,
20596 cls : 'roo-' + this.inputType, //'form-box',
20597 placeholder : this.placeholder || ''
20601 if(this.inputType != 'radio'){
20605 cls : 'roo-hidden-value',
20606 value : this.checked ? this.inputValue : this.valueOff
20611 if (this.weight) { // Validity check?
20612 cfg.cls += " " + this.inputType + "-" + this.weight;
20615 if (this.disabled) {
20616 input.disabled=true;
20620 input.checked = this.checked;
20625 input.name = this.name;
20627 if(this.inputType != 'radio'){
20628 hidden.name = this.name;
20629 input.name = '_hidden_' + this.name;
20634 input.cls += ' input-' + this.size;
20639 ['xs','sm','md','lg'].map(function(size){
20640 if (settings[size]) {
20641 cfg.cls += ' col-' + size + '-' + settings[size];
20645 var inputblock = input;
20647 if (this.before || this.after) {
20650 cls : 'input-group',
20655 inputblock.cn.push({
20657 cls : 'input-group-addon',
20662 inputblock.cn.push(input);
20664 if(this.inputType != 'radio'){
20665 inputblock.cn.push(hidden);
20669 inputblock.cn.push({
20671 cls : 'input-group-addon',
20678 if (align ==='left' && this.fieldLabel.length) {
20679 // Roo.log("left and has label");
20684 cls : 'control-label',
20685 html : this.fieldLabel
20695 if(this.labelWidth > 12){
20696 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20699 if(this.labelWidth < 13 && this.labelmd == 0){
20700 this.labelmd = this.labelWidth;
20703 if(this.labellg > 0){
20704 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20705 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20708 if(this.labelmd > 0){
20709 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20710 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20713 if(this.labelsm > 0){
20714 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20715 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20718 if(this.labelxs > 0){
20719 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20720 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20723 } else if ( this.fieldLabel.length) {
20724 // Roo.log(" label");
20728 tag: this.boxLabel ? 'span' : 'label',
20730 cls: 'control-label box-input-label',
20731 //cls : 'input-group-addon',
20732 html : this.fieldLabel
20741 // Roo.log(" no label && no align");
20742 cfg.cn = [ inputblock ] ;
20748 var boxLabelCfg = {
20750 //'for': id, // box label is handled by onclick - so no for...
20752 html: this.boxLabel
20756 boxLabelCfg.tooltip = this.tooltip;
20759 cfg.cn.push(boxLabelCfg);
20762 if(this.inputType != 'radio'){
20763 cfg.cn.push(hidden);
20771 * return the real input element.
20773 inputEl: function ()
20775 return this.el.select('input.roo-' + this.inputType,true).first();
20777 hiddenEl: function ()
20779 return this.el.select('input.roo-hidden-value',true).first();
20782 labelEl: function()
20784 return this.el.select('label.control-label',true).first();
20786 /* depricated... */
20790 return this.labelEl();
20793 boxLabelEl: function()
20795 return this.el.select('label.box-label',true).first();
20798 initEvents : function()
20800 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20802 this.inputEl().on('click', this.onClick, this);
20804 if (this.boxLabel) {
20805 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20808 this.startValue = this.getValue();
20811 Roo.bootstrap.CheckBox.register(this);
20815 onClick : function(e)
20817 if(this.fireEvent('click', this, e) !== false){
20818 this.setChecked(!this.checked);
20823 setChecked : function(state,suppressEvent)
20825 this.startValue = this.getValue();
20827 if(this.inputType == 'radio'){
20829 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20830 e.dom.checked = false;
20833 this.inputEl().dom.checked = true;
20835 this.inputEl().dom.value = this.inputValue;
20837 if(suppressEvent !== true){
20838 this.fireEvent('check', this, true);
20846 this.checked = state;
20848 this.inputEl().dom.checked = state;
20851 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20853 if(suppressEvent !== true){
20854 this.fireEvent('check', this, state);
20860 getValue : function()
20862 if(this.inputType == 'radio'){
20863 return this.getGroupValue();
20866 return this.hiddenEl().dom.value;
20870 getGroupValue : function()
20872 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20876 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20879 setValue : function(v,suppressEvent)
20881 if(this.inputType == 'radio'){
20882 this.setGroupValue(v, suppressEvent);
20886 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20891 setGroupValue : function(v, suppressEvent)
20893 this.startValue = this.getValue();
20895 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20896 e.dom.checked = false;
20898 if(e.dom.value == v){
20899 e.dom.checked = true;
20903 if(suppressEvent !== true){
20904 this.fireEvent('check', this, true);
20912 validate : function()
20914 if(this.getVisibilityEl().hasClass('hidden')){
20920 (this.inputType == 'radio' && this.validateRadio()) ||
20921 (this.inputType == 'checkbox' && this.validateCheckbox())
20927 this.markInvalid();
20931 validateRadio : function()
20933 if(this.getVisibilityEl().hasClass('hidden')){
20937 if(this.allowBlank){
20943 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944 if(!e.dom.checked){
20956 validateCheckbox : function()
20959 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20960 //return (this.getValue() == this.inputValue) ? true : false;
20963 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20971 for(var i in group){
20972 if(group[i].el.isVisible(true)){
20980 for(var i in group){
20985 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20992 * Mark this field as valid
20994 markValid : function()
20998 this.fireEvent('valid', this);
21000 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21003 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21010 if(this.inputType == 'radio'){
21011 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21012 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21013 e.findParent('.form-group', false, true).addClass(_this.validClass);
21020 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21021 this.el.findParent('.form-group', false, true).addClass(this.validClass);
21025 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21031 for(var i in group){
21032 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21033 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21038 * Mark this field as invalid
21039 * @param {String} msg The validation message
21041 markInvalid : function(msg)
21043 if(this.allowBlank){
21049 this.fireEvent('invalid', this, msg);
21051 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21054 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21058 label.markInvalid();
21061 if(this.inputType == 'radio'){
21062 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21063 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21064 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21071 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21072 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21076 var group = Roo.bootstrap.CheckBox.get(this.groupId);
21082 for(var i in group){
21083 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21084 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21089 clearInvalid : function()
21091 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21093 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21095 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21097 if (label && label.iconEl) {
21098 label.iconEl.removeClass(label.validClass);
21099 label.iconEl.removeClass(label.invalidClass);
21103 disable : function()
21105 if(this.inputType != 'radio'){
21106 Roo.bootstrap.CheckBox.superclass.disable.call(this);
21113 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21114 _this.getActionEl().addClass(this.disabledClass);
21115 e.dom.disabled = true;
21119 this.disabled = true;
21120 this.fireEvent("disable", this);
21124 enable : function()
21126 if(this.inputType != 'radio'){
21127 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21134 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21135 _this.getActionEl().removeClass(this.disabledClass);
21136 e.dom.disabled = false;
21140 this.disabled = false;
21141 this.fireEvent("enable", this);
21145 setBoxLabel : function(v)
21150 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21156 Roo.apply(Roo.bootstrap.CheckBox, {
21161 * register a CheckBox Group
21162 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21164 register : function(checkbox)
21166 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21167 this.groups[checkbox.groupId] = {};
21170 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21174 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21178 * fetch a CheckBox Group based on the group ID
21179 * @param {string} the group ID
21180 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21182 get: function(groupId) {
21183 if (typeof(this.groups[groupId]) == 'undefined') {
21187 return this.groups[groupId] ;
21200 * @class Roo.bootstrap.Radio
21201 * @extends Roo.bootstrap.Component
21202 * Bootstrap Radio class
21203 * @cfg {String} boxLabel - the label associated
21204 * @cfg {String} value - the value of radio
21207 * Create a new Radio
21208 * @param {Object} config The config object
21210 Roo.bootstrap.Radio = function(config){
21211 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21215 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21221 getAutoCreate : function()
21225 cls : 'form-group radio',
21230 html : this.boxLabel
21238 initEvents : function()
21240 this.parent().register(this);
21242 this.el.on('click', this.onClick, this);
21246 onClick : function(e)
21248 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21249 this.setChecked(true);
21253 setChecked : function(state, suppressEvent)
21255 this.parent().setValue(this.value, suppressEvent);
21259 setBoxLabel : function(v)
21264 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21279 * @class Roo.bootstrap.SecurePass
21280 * @extends Roo.bootstrap.Input
21281 * Bootstrap SecurePass class
21285 * Create a new SecurePass
21286 * @param {Object} config The config object
21289 Roo.bootstrap.SecurePass = function (config) {
21290 // these go here, so the translation tool can replace them..
21292 PwdEmpty: "Please type a password, and then retype it to confirm.",
21293 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21294 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21295 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21296 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21297 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21298 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21299 TooWeak: "Your password is Too Weak."
21301 this.meterLabel = "Password strength:";
21302 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21303 this.meterClass = [
21304 "roo-password-meter-tooweak",
21305 "roo-password-meter-weak",
21306 "roo-password-meter-medium",
21307 "roo-password-meter-strong",
21308 "roo-password-meter-grey"
21313 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21316 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21318 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21320 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21321 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21322 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21323 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21324 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21325 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21326 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21336 * @cfg {String/Object} Label for the strength meter (defaults to
21337 * 'Password strength:')
21342 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21343 * ['Weak', 'Medium', 'Strong'])
21346 pwdStrengths: false,
21359 initEvents: function ()
21361 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21363 if (this.el.is('input[type=password]') && Roo.isSafari) {
21364 this.el.on('keydown', this.SafariOnKeyDown, this);
21367 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21370 onRender: function (ct, position)
21372 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21373 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21374 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21376 this.trigger.createChild({
21381 cls: 'roo-password-meter-grey col-xs-12',
21384 //width: this.meterWidth + 'px'
21388 cls: 'roo-password-meter-text'
21394 if (this.hideTrigger) {
21395 this.trigger.setDisplayed(false);
21397 this.setSize(this.width || '', this.height || '');
21400 onDestroy: function ()
21402 if (this.trigger) {
21403 this.trigger.removeAllListeners();
21404 this.trigger.remove();
21407 this.wrap.remove();
21409 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21412 checkStrength: function ()
21414 var pwd = this.inputEl().getValue();
21415 if (pwd == this._lastPwd) {
21420 if (this.ClientSideStrongPassword(pwd)) {
21422 } else if (this.ClientSideMediumPassword(pwd)) {
21424 } else if (this.ClientSideWeakPassword(pwd)) {
21430 Roo.log('strength1: ' + strength);
21432 //var pm = this.trigger.child('div/div/div').dom;
21433 var pm = this.trigger.child('div/div');
21434 pm.removeClass(this.meterClass);
21435 pm.addClass(this.meterClass[strength]);
21438 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21440 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21442 this._lastPwd = pwd;
21446 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21448 this._lastPwd = '';
21450 var pm = this.trigger.child('div/div');
21451 pm.removeClass(this.meterClass);
21452 pm.addClass('roo-password-meter-grey');
21455 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21458 this.inputEl().dom.type='password';
21461 validateValue: function (value)
21464 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21467 if (value.length == 0) {
21468 if (this.allowBlank) {
21469 this.clearInvalid();
21473 this.markInvalid(this.errors.PwdEmpty);
21474 this.errorMsg = this.errors.PwdEmpty;
21482 if ('[\x21-\x7e]*'.match(value)) {
21483 this.markInvalid(this.errors.PwdBadChar);
21484 this.errorMsg = this.errors.PwdBadChar;
21487 if (value.length < 6) {
21488 this.markInvalid(this.errors.PwdShort);
21489 this.errorMsg = this.errors.PwdShort;
21492 if (value.length > 16) {
21493 this.markInvalid(this.errors.PwdLong);
21494 this.errorMsg = this.errors.PwdLong;
21498 if (this.ClientSideStrongPassword(value)) {
21500 } else if (this.ClientSideMediumPassword(value)) {
21502 } else if (this.ClientSideWeakPassword(value)) {
21509 if (strength < 2) {
21510 //this.markInvalid(this.errors.TooWeak);
21511 this.errorMsg = this.errors.TooWeak;
21516 console.log('strength2: ' + strength);
21518 //var pm = this.trigger.child('div/div/div').dom;
21520 var pm = this.trigger.child('div/div');
21521 pm.removeClass(this.meterClass);
21522 pm.addClass(this.meterClass[strength]);
21524 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21526 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21528 this.errorMsg = '';
21532 CharacterSetChecks: function (type)
21535 this.fResult = false;
21538 isctype: function (character, type)
21541 case this.kCapitalLetter:
21542 if (character >= 'A' && character <= 'Z') {
21547 case this.kSmallLetter:
21548 if (character >= 'a' && character <= 'z') {
21554 if (character >= '0' && character <= '9') {
21559 case this.kPunctuation:
21560 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21571 IsLongEnough: function (pwd, size)
21573 return !(pwd == null || isNaN(size) || pwd.length < size);
21576 SpansEnoughCharacterSets: function (word, nb)
21578 if (!this.IsLongEnough(word, nb))
21583 var characterSetChecks = new Array(
21584 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21585 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21588 for (var index = 0; index < word.length; ++index) {
21589 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21590 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21591 characterSetChecks[nCharSet].fResult = true;
21598 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21599 if (characterSetChecks[nCharSet].fResult) {
21604 if (nCharSets < nb) {
21610 ClientSideStrongPassword: function (pwd)
21612 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21615 ClientSideMediumPassword: function (pwd)
21617 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21620 ClientSideWeakPassword: function (pwd)
21622 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21625 })//<script type="text/javascript">
21628 * Based Ext JS Library 1.1.1
21629 * Copyright(c) 2006-2007, Ext JS, LLC.
21635 * @class Roo.HtmlEditorCore
21636 * @extends Roo.Component
21637 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21639 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21642 Roo.HtmlEditorCore = function(config){
21645 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21650 * @event initialize
21651 * Fires when the editor is fully initialized (including the iframe)
21652 * @param {Roo.HtmlEditorCore} this
21657 * Fires when the editor is first receives the focus. Any insertion must wait
21658 * until after this event.
21659 * @param {Roo.HtmlEditorCore} this
21663 * @event beforesync
21664 * Fires before the textarea is updated with content from the editor iframe. Return false
21665 * to cancel the sync.
21666 * @param {Roo.HtmlEditorCore} this
21667 * @param {String} html
21671 * @event beforepush
21672 * Fires before the iframe editor is updated with content from the textarea. Return false
21673 * to cancel the push.
21674 * @param {Roo.HtmlEditorCore} this
21675 * @param {String} html
21680 * Fires when the textarea is updated with content from the editor iframe.
21681 * @param {Roo.HtmlEditorCore} this
21682 * @param {String} html
21687 * Fires when the iframe editor is updated with content from the textarea.
21688 * @param {Roo.HtmlEditorCore} this
21689 * @param {String} html
21694 * @event editorevent
21695 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21696 * @param {Roo.HtmlEditorCore} this
21702 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21704 // defaults : white / black...
21705 this.applyBlacklists();
21712 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21716 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21722 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21727 * @cfg {Number} height (in pixels)
21731 * @cfg {Number} width (in pixels)
21736 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21739 stylesheets: false,
21744 // private properties
21745 validationEvent : false,
21747 initialized : false,
21749 sourceEditMode : false,
21750 onFocus : Roo.emptyFn,
21752 hideMode:'offsets',
21756 // blacklist + whitelisted elements..
21763 * Protected method that will not generally be called directly. It
21764 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21765 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21767 getDocMarkup : function(){
21771 // inherit styels from page...??
21772 if (this.stylesheets === false) {
21774 Roo.get(document.head).select('style').each(function(node) {
21775 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21778 Roo.get(document.head).select('link').each(function(node) {
21779 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21782 } else if (!this.stylesheets.length) {
21784 st = '<style type="text/css">' +
21785 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21788 st = '<style type="text/css">' +
21793 st += '<style type="text/css">' +
21794 'IMG { cursor: pointer } ' +
21797 var cls = 'roo-htmleditor-body';
21799 if(this.bodyCls.length){
21800 cls += ' ' + this.bodyCls;
21803 return '<html><head>' + st +
21804 //<style type="text/css">' +
21805 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21807 ' </head><body class="' + cls + '"></body></html>';
21811 onRender : function(ct, position)
21814 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21815 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21818 this.el.dom.style.border = '0 none';
21819 this.el.dom.setAttribute('tabIndex', -1);
21820 this.el.addClass('x-hidden hide');
21824 if(Roo.isIE){ // fix IE 1px bogus margin
21825 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21829 this.frameId = Roo.id();
21833 var iframe = this.owner.wrap.createChild({
21835 cls: 'form-control', // bootstrap..
21837 name: this.frameId,
21838 frameBorder : 'no',
21839 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21844 this.iframe = iframe.dom;
21846 this.assignDocWin();
21848 this.doc.designMode = 'on';
21851 this.doc.write(this.getDocMarkup());
21855 var task = { // must defer to wait for browser to be ready
21857 //console.log("run task?" + this.doc.readyState);
21858 this.assignDocWin();
21859 if(this.doc.body || this.doc.readyState == 'complete'){
21861 this.doc.designMode="on";
21865 Roo.TaskMgr.stop(task);
21866 this.initEditor.defer(10, this);
21873 Roo.TaskMgr.start(task);
21878 onResize : function(w, h)
21880 Roo.log('resize: ' +w + ',' + h );
21881 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21885 if(typeof w == 'number'){
21887 this.iframe.style.width = w + 'px';
21889 if(typeof h == 'number'){
21891 this.iframe.style.height = h + 'px';
21893 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21900 * Toggles the editor between standard and source edit mode.
21901 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21903 toggleSourceEdit : function(sourceEditMode){
21905 this.sourceEditMode = sourceEditMode === true;
21907 if(this.sourceEditMode){
21909 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21912 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21913 //this.iframe.className = '';
21916 //this.setSize(this.owner.wrap.getSize());
21917 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21924 * Protected method that will not generally be called directly. If you need/want
21925 * custom HTML cleanup, this is the method you should override.
21926 * @param {String} html The HTML to be cleaned
21927 * return {String} The cleaned HTML
21929 cleanHtml : function(html){
21930 html = String(html);
21931 if(html.length > 5){
21932 if(Roo.isSafari){ // strip safari nonsense
21933 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21936 if(html == ' '){
21943 * HTML Editor -> Textarea
21944 * Protected method that will not generally be called directly. Syncs the contents
21945 * of the editor iframe with the textarea.
21947 syncValue : function(){
21948 if(this.initialized){
21949 var bd = (this.doc.body || this.doc.documentElement);
21950 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21951 var html = bd.innerHTML;
21953 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21954 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21956 html = '<div style="'+m[0]+'">' + html + '</div>';
21959 html = this.cleanHtml(html);
21960 // fix up the special chars.. normaly like back quotes in word...
21961 // however we do not want to do this with chinese..
21962 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21963 var cc = b.charCodeAt();
21965 (cc >= 0x4E00 && cc < 0xA000 ) ||
21966 (cc >= 0x3400 && cc < 0x4E00 ) ||
21967 (cc >= 0xf900 && cc < 0xfb00 )
21973 if(this.owner.fireEvent('beforesync', this, html) !== false){
21974 this.el.dom.value = html;
21975 this.owner.fireEvent('sync', this, html);
21981 * Protected method that will not generally be called directly. Pushes the value of the textarea
21982 * into the iframe editor.
21984 pushValue : function(){
21985 if(this.initialized){
21986 var v = this.el.dom.value.trim();
21988 // if(v.length < 1){
21992 if(this.owner.fireEvent('beforepush', this, v) !== false){
21993 var d = (this.doc.body || this.doc.documentElement);
21995 this.cleanUpPaste();
21996 this.el.dom.value = d.innerHTML;
21997 this.owner.fireEvent('push', this, v);
22003 deferFocus : function(){
22004 this.focus.defer(10, this);
22008 focus : function(){
22009 if(this.win && !this.sourceEditMode){
22016 assignDocWin: function()
22018 var iframe = this.iframe;
22021 this.doc = iframe.contentWindow.document;
22022 this.win = iframe.contentWindow;
22024 // if (!Roo.get(this.frameId)) {
22027 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22028 // this.win = Roo.get(this.frameId).dom.contentWindow;
22030 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22034 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22035 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22040 initEditor : function(){
22041 //console.log("INIT EDITOR");
22042 this.assignDocWin();
22046 this.doc.designMode="on";
22048 this.doc.write(this.getDocMarkup());
22051 var dbody = (this.doc.body || this.doc.documentElement);
22052 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22053 // this copies styles from the containing element into thsi one..
22054 // not sure why we need all of this..
22055 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22057 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22058 //ss['background-attachment'] = 'fixed'; // w3c
22059 dbody.bgProperties = 'fixed'; // ie
22060 //Roo.DomHelper.applyStyles(dbody, ss);
22061 Roo.EventManager.on(this.doc, {
22062 //'mousedown': this.onEditorEvent,
22063 'mouseup': this.onEditorEvent,
22064 'dblclick': this.onEditorEvent,
22065 'click': this.onEditorEvent,
22066 'keyup': this.onEditorEvent,
22071 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22073 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22074 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22076 this.initialized = true;
22078 this.owner.fireEvent('initialize', this);
22083 onDestroy : function(){
22089 //for (var i =0; i < this.toolbars.length;i++) {
22090 // // fixme - ask toolbars for heights?
22091 // this.toolbars[i].onDestroy();
22094 //this.wrap.dom.innerHTML = '';
22095 //this.wrap.remove();
22100 onFirstFocus : function(){
22102 this.assignDocWin();
22105 this.activated = true;
22108 if(Roo.isGecko){ // prevent silly gecko errors
22110 var s = this.win.getSelection();
22111 if(!s.focusNode || s.focusNode.nodeType != 3){
22112 var r = s.getRangeAt(0);
22113 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22118 this.execCmd('useCSS', true);
22119 this.execCmd('styleWithCSS', false);
22122 this.owner.fireEvent('activate', this);
22126 adjustFont: function(btn){
22127 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22128 //if(Roo.isSafari){ // safari
22131 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22132 if(Roo.isSafari){ // safari
22133 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22134 v = (v < 10) ? 10 : v;
22135 v = (v > 48) ? 48 : v;
22136 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22141 v = Math.max(1, v+adjust);
22143 this.execCmd('FontSize', v );
22146 onEditorEvent : function(e)
22148 this.owner.fireEvent('editorevent', this, e);
22149 // this.updateToolbar();
22150 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22153 insertTag : function(tg)
22155 // could be a bit smarter... -> wrap the current selected tRoo..
22156 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22158 range = this.createRange(this.getSelection());
22159 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22160 wrappingNode.appendChild(range.extractContents());
22161 range.insertNode(wrappingNode);
22168 this.execCmd("formatblock", tg);
22172 insertText : function(txt)
22176 var range = this.createRange();
22177 range.deleteContents();
22178 //alert(Sender.getAttribute('label'));
22180 range.insertNode(this.doc.createTextNode(txt));
22186 * Executes a Midas editor command on the editor document and performs necessary focus and
22187 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22188 * @param {String} cmd The Midas command
22189 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22191 relayCmd : function(cmd, value){
22193 this.execCmd(cmd, value);
22194 this.owner.fireEvent('editorevent', this);
22195 //this.updateToolbar();
22196 this.owner.deferFocus();
22200 * Executes a Midas editor command directly on the editor document.
22201 * For visual commands, you should use {@link #relayCmd} instead.
22202 * <b>This should only be called after the editor is initialized.</b>
22203 * @param {String} cmd The Midas command
22204 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22206 execCmd : function(cmd, value){
22207 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22214 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22216 * @param {String} text | dom node..
22218 insertAtCursor : function(text)
22221 if(!this.activated){
22227 var r = this.doc.selection.createRange();
22238 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22242 // from jquery ui (MIT licenced)
22244 var win = this.win;
22246 if (win.getSelection && win.getSelection().getRangeAt) {
22247 range = win.getSelection().getRangeAt(0);
22248 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22249 range.insertNode(node);
22250 } else if (win.document.selection && win.document.selection.createRange) {
22251 // no firefox support
22252 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22253 win.document.selection.createRange().pasteHTML(txt);
22255 // no firefox support
22256 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22257 this.execCmd('InsertHTML', txt);
22266 mozKeyPress : function(e){
22268 var c = e.getCharCode(), cmd;
22271 c = String.fromCharCode(c).toLowerCase();
22285 this.cleanUpPaste.defer(100, this);
22293 e.preventDefault();
22301 fixKeys : function(){ // load time branching for fastest keydown performance
22303 return function(e){
22304 var k = e.getKey(), r;
22307 r = this.doc.selection.createRange();
22310 r.pasteHTML('    ');
22317 r = this.doc.selection.createRange();
22319 var target = r.parentElement();
22320 if(!target || target.tagName.toLowerCase() != 'li'){
22322 r.pasteHTML('<br />');
22328 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22329 this.cleanUpPaste.defer(100, this);
22335 }else if(Roo.isOpera){
22336 return function(e){
22337 var k = e.getKey();
22341 this.execCmd('InsertHTML','    ');
22344 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22345 this.cleanUpPaste.defer(100, this);
22350 }else if(Roo.isSafari){
22351 return function(e){
22352 var k = e.getKey();
22356 this.execCmd('InsertText','\t');
22360 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22361 this.cleanUpPaste.defer(100, this);
22369 getAllAncestors: function()
22371 var p = this.getSelectedNode();
22374 a.push(p); // push blank onto stack..
22375 p = this.getParentElement();
22379 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22383 a.push(this.doc.body);
22387 lastSelNode : false,
22390 getSelection : function()
22392 this.assignDocWin();
22393 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22396 getSelectedNode: function()
22398 // this may only work on Gecko!!!
22400 // should we cache this!!!!
22405 var range = this.createRange(this.getSelection()).cloneRange();
22408 var parent = range.parentElement();
22410 var testRange = range.duplicate();
22411 testRange.moveToElementText(parent);
22412 if (testRange.inRange(range)) {
22415 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22418 parent = parent.parentElement;
22423 // is ancestor a text element.
22424 var ac = range.commonAncestorContainer;
22425 if (ac.nodeType == 3) {
22426 ac = ac.parentNode;
22429 var ar = ac.childNodes;
22432 var other_nodes = [];
22433 var has_other_nodes = false;
22434 for (var i=0;i<ar.length;i++) {
22435 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22438 // fullly contained node.
22440 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22445 // probably selected..
22446 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22447 other_nodes.push(ar[i]);
22451 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22456 has_other_nodes = true;
22458 if (!nodes.length && other_nodes.length) {
22459 nodes= other_nodes;
22461 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22467 createRange: function(sel)
22469 // this has strange effects when using with
22470 // top toolbar - not sure if it's a great idea.
22471 //this.editor.contentWindow.focus();
22472 if (typeof sel != "undefined") {
22474 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22476 return this.doc.createRange();
22479 return this.doc.createRange();
22482 getParentElement: function()
22485 this.assignDocWin();
22486 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22488 var range = this.createRange(sel);
22491 var p = range.commonAncestorContainer;
22492 while (p.nodeType == 3) { // text node
22503 * Range intersection.. the hard stuff...
22507 * [ -- selected range --- ]
22511 * if end is before start or hits it. fail.
22512 * if start is after end or hits it fail.
22514 * if either hits (but other is outside. - then it's not
22520 // @see http://www.thismuchiknow.co.uk/?p=64.
22521 rangeIntersectsNode : function(range, node)
22523 var nodeRange = node.ownerDocument.createRange();
22525 nodeRange.selectNode(node);
22527 nodeRange.selectNodeContents(node);
22530 var rangeStartRange = range.cloneRange();
22531 rangeStartRange.collapse(true);
22533 var rangeEndRange = range.cloneRange();
22534 rangeEndRange.collapse(false);
22536 var nodeStartRange = nodeRange.cloneRange();
22537 nodeStartRange.collapse(true);
22539 var nodeEndRange = nodeRange.cloneRange();
22540 nodeEndRange.collapse(false);
22542 return rangeStartRange.compareBoundaryPoints(
22543 Range.START_TO_START, nodeEndRange) == -1 &&
22544 rangeEndRange.compareBoundaryPoints(
22545 Range.START_TO_START, nodeStartRange) == 1;
22549 rangeCompareNode : function(range, node)
22551 var nodeRange = node.ownerDocument.createRange();
22553 nodeRange.selectNode(node);
22555 nodeRange.selectNodeContents(node);
22559 range.collapse(true);
22561 nodeRange.collapse(true);
22563 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22564 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22566 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22568 var nodeIsBefore = ss == 1;
22569 var nodeIsAfter = ee == -1;
22571 if (nodeIsBefore && nodeIsAfter) {
22574 if (!nodeIsBefore && nodeIsAfter) {
22575 return 1; //right trailed.
22578 if (nodeIsBefore && !nodeIsAfter) {
22579 return 2; // left trailed.
22585 // private? - in a new class?
22586 cleanUpPaste : function()
22588 // cleans up the whole document..
22589 Roo.log('cleanuppaste');
22591 this.cleanUpChildren(this.doc.body);
22592 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22593 if (clean != this.doc.body.innerHTML) {
22594 this.doc.body.innerHTML = clean;
22599 cleanWordChars : function(input) {// change the chars to hex code
22600 var he = Roo.HtmlEditorCore;
22602 var output = input;
22603 Roo.each(he.swapCodes, function(sw) {
22604 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22606 output = output.replace(swapper, sw[1]);
22613 cleanUpChildren : function (n)
22615 if (!n.childNodes.length) {
22618 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22619 this.cleanUpChild(n.childNodes[i]);
22626 cleanUpChild : function (node)
22629 //console.log(node);
22630 if (node.nodeName == "#text") {
22631 // clean up silly Windows -- stuff?
22634 if (node.nodeName == "#comment") {
22635 node.parentNode.removeChild(node);
22636 // clean up silly Windows -- stuff?
22639 var lcname = node.tagName.toLowerCase();
22640 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22641 // whitelist of tags..
22643 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22645 node.parentNode.removeChild(node);
22650 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22652 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22653 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22655 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22656 // remove_keep_children = true;
22659 if (remove_keep_children) {
22660 this.cleanUpChildren(node);
22661 // inserts everything just before this node...
22662 while (node.childNodes.length) {
22663 var cn = node.childNodes[0];
22664 node.removeChild(cn);
22665 node.parentNode.insertBefore(cn, node);
22667 node.parentNode.removeChild(node);
22671 if (!node.attributes || !node.attributes.length) {
22672 this.cleanUpChildren(node);
22676 function cleanAttr(n,v)
22679 if (v.match(/^\./) || v.match(/^\//)) {
22682 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22685 if (v.match(/^#/)) {
22688 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22689 node.removeAttribute(n);
22693 var cwhite = this.cwhite;
22694 var cblack = this.cblack;
22696 function cleanStyle(n,v)
22698 if (v.match(/expression/)) { //XSS?? should we even bother..
22699 node.removeAttribute(n);
22703 var parts = v.split(/;/);
22706 Roo.each(parts, function(p) {
22707 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22711 var l = p.split(':').shift().replace(/\s+/g,'');
22712 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22714 if ( cwhite.length && cblack.indexOf(l) > -1) {
22715 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22716 //node.removeAttribute(n);
22720 // only allow 'c whitelisted system attributes'
22721 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22722 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22723 //node.removeAttribute(n);
22733 if (clean.length) {
22734 node.setAttribute(n, clean.join(';'));
22736 node.removeAttribute(n);
22742 for (var i = node.attributes.length-1; i > -1 ; i--) {
22743 var a = node.attributes[i];
22746 if (a.name.toLowerCase().substr(0,2)=='on') {
22747 node.removeAttribute(a.name);
22750 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22751 node.removeAttribute(a.name);
22754 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22755 cleanAttr(a.name,a.value); // fixme..
22758 if (a.name == 'style') {
22759 cleanStyle(a.name,a.value);
22762 /// clean up MS crap..
22763 // tecnically this should be a list of valid class'es..
22766 if (a.name == 'class') {
22767 if (a.value.match(/^Mso/)) {
22768 node.className = '';
22771 if (a.value.match(/^body$/)) {
22772 node.className = '';
22783 this.cleanUpChildren(node);
22789 * Clean up MS wordisms...
22791 cleanWord : function(node)
22796 this.cleanWord(this.doc.body);
22799 if (node.nodeName == "#text") {
22800 // clean up silly Windows -- stuff?
22803 if (node.nodeName == "#comment") {
22804 node.parentNode.removeChild(node);
22805 // clean up silly Windows -- stuff?
22809 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22810 node.parentNode.removeChild(node);
22814 // remove - but keep children..
22815 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22816 while (node.childNodes.length) {
22817 var cn = node.childNodes[0];
22818 node.removeChild(cn);
22819 node.parentNode.insertBefore(cn, node);
22821 node.parentNode.removeChild(node);
22822 this.iterateChildren(node, this.cleanWord);
22826 if (node.className.length) {
22828 var cn = node.className.split(/\W+/);
22830 Roo.each(cn, function(cls) {
22831 if (cls.match(/Mso[a-zA-Z]+/)) {
22836 node.className = cna.length ? cna.join(' ') : '';
22838 node.removeAttribute("class");
22842 if (node.hasAttribute("lang")) {
22843 node.removeAttribute("lang");
22846 if (node.hasAttribute("style")) {
22848 var styles = node.getAttribute("style").split(";");
22850 Roo.each(styles, function(s) {
22851 if (!s.match(/:/)) {
22854 var kv = s.split(":");
22855 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22858 // what ever is left... we allow.
22861 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22862 if (!nstyle.length) {
22863 node.removeAttribute('style');
22866 this.iterateChildren(node, this.cleanWord);
22872 * iterateChildren of a Node, calling fn each time, using this as the scole..
22873 * @param {DomNode} node node to iterate children of.
22874 * @param {Function} fn method of this class to call on each item.
22876 iterateChildren : function(node, fn)
22878 if (!node.childNodes.length) {
22881 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22882 fn.call(this, node.childNodes[i])
22888 * cleanTableWidths.
22890 * Quite often pasting from word etc.. results in tables with column and widths.
22891 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22894 cleanTableWidths : function(node)
22899 this.cleanTableWidths(this.doc.body);
22904 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22907 Roo.log(node.tagName);
22908 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22909 this.iterateChildren(node, this.cleanTableWidths);
22912 if (node.hasAttribute('width')) {
22913 node.removeAttribute('width');
22917 if (node.hasAttribute("style")) {
22920 var styles = node.getAttribute("style").split(";");
22922 Roo.each(styles, function(s) {
22923 if (!s.match(/:/)) {
22926 var kv = s.split(":");
22927 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22930 // what ever is left... we allow.
22933 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22934 if (!nstyle.length) {
22935 node.removeAttribute('style');
22939 this.iterateChildren(node, this.cleanTableWidths);
22947 domToHTML : function(currentElement, depth, nopadtext) {
22949 depth = depth || 0;
22950 nopadtext = nopadtext || false;
22952 if (!currentElement) {
22953 return this.domToHTML(this.doc.body);
22956 //Roo.log(currentElement);
22958 var allText = false;
22959 var nodeName = currentElement.nodeName;
22960 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22962 if (nodeName == '#text') {
22964 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22969 if (nodeName != 'BODY') {
22972 // Prints the node tagName, such as <A>, <IMG>, etc
22975 for(i = 0; i < currentElement.attributes.length;i++) {
22977 var aname = currentElement.attributes.item(i).name;
22978 if (!currentElement.attributes.item(i).value.length) {
22981 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22984 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22993 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22996 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23001 // Traverse the tree
23003 var currentElementChild = currentElement.childNodes.item(i);
23004 var allText = true;
23005 var innerHTML = '';
23007 while (currentElementChild) {
23008 // Formatting code (indent the tree so it looks nice on the screen)
23009 var nopad = nopadtext;
23010 if (lastnode == 'SPAN') {
23014 if (currentElementChild.nodeName == '#text') {
23015 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23016 toadd = nopadtext ? toadd : toadd.trim();
23017 if (!nopad && toadd.length > 80) {
23018 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
23020 innerHTML += toadd;
23023 currentElementChild = currentElement.childNodes.item(i);
23029 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
23031 // Recursively traverse the tree structure of the child node
23032 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
23033 lastnode = currentElementChild.nodeName;
23035 currentElementChild=currentElement.childNodes.item(i);
23041 // The remaining code is mostly for formatting the tree
23042 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
23047 ret+= "</"+tagName+">";
23053 applyBlacklists : function()
23055 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
23056 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
23060 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23061 if (b.indexOf(tag) > -1) {
23064 this.white.push(tag);
23068 Roo.each(w, function(tag) {
23069 if (b.indexOf(tag) > -1) {
23072 if (this.white.indexOf(tag) > -1) {
23075 this.white.push(tag);
23080 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23081 if (w.indexOf(tag) > -1) {
23084 this.black.push(tag);
23088 Roo.each(b, function(tag) {
23089 if (w.indexOf(tag) > -1) {
23092 if (this.black.indexOf(tag) > -1) {
23095 this.black.push(tag);
23100 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
23101 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
23105 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23106 if (b.indexOf(tag) > -1) {
23109 this.cwhite.push(tag);
23113 Roo.each(w, function(tag) {
23114 if (b.indexOf(tag) > -1) {
23117 if (this.cwhite.indexOf(tag) > -1) {
23120 this.cwhite.push(tag);
23125 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23126 if (w.indexOf(tag) > -1) {
23129 this.cblack.push(tag);
23133 Roo.each(b, function(tag) {
23134 if (w.indexOf(tag) > -1) {
23137 if (this.cblack.indexOf(tag) > -1) {
23140 this.cblack.push(tag);
23145 setStylesheets : function(stylesheets)
23147 if(typeof(stylesheets) == 'string'){
23148 Roo.get(this.iframe.contentDocument.head).createChild({
23150 rel : 'stylesheet',
23159 Roo.each(stylesheets, function(s) {
23164 Roo.get(_this.iframe.contentDocument.head).createChild({
23166 rel : 'stylesheet',
23175 removeStylesheets : function()
23179 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23184 setStyle : function(style)
23186 Roo.get(this.iframe.contentDocument.head).createChild({
23195 // hide stuff that is not compatible
23209 * @event specialkey
23213 * @cfg {String} fieldClass @hide
23216 * @cfg {String} focusClass @hide
23219 * @cfg {String} autoCreate @hide
23222 * @cfg {String} inputType @hide
23225 * @cfg {String} invalidClass @hide
23228 * @cfg {String} invalidText @hide
23231 * @cfg {String} msgFx @hide
23234 * @cfg {String} validateOnBlur @hide
23238 Roo.HtmlEditorCore.white = [
23239 'area', 'br', 'img', 'input', 'hr', 'wbr',
23241 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23242 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23243 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23244 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23245 'table', 'ul', 'xmp',
23247 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23250 'dir', 'menu', 'ol', 'ul', 'dl',
23256 Roo.HtmlEditorCore.black = [
23257 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23259 'base', 'basefont', 'bgsound', 'blink', 'body',
23260 'frame', 'frameset', 'head', 'html', 'ilayer',
23261 'iframe', 'layer', 'link', 'meta', 'object',
23262 'script', 'style' ,'title', 'xml' // clean later..
23264 Roo.HtmlEditorCore.clean = [
23265 'script', 'style', 'title', 'xml'
23267 Roo.HtmlEditorCore.remove = [
23272 Roo.HtmlEditorCore.ablack = [
23276 Roo.HtmlEditorCore.aclean = [
23277 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23281 Roo.HtmlEditorCore.pwhite= [
23282 'http', 'https', 'mailto'
23285 // white listed style attributes.
23286 Roo.HtmlEditorCore.cwhite= [
23287 // 'text-align', /// default is to allow most things..
23293 // black listed style attributes.
23294 Roo.HtmlEditorCore.cblack= [
23295 // 'font-size' -- this can be set by the project
23299 Roo.HtmlEditorCore.swapCodes =[
23318 * @class Roo.bootstrap.HtmlEditor
23319 * @extends Roo.bootstrap.TextArea
23320 * Bootstrap HtmlEditor class
23323 * Create a new HtmlEditor
23324 * @param {Object} config The config object
23327 Roo.bootstrap.HtmlEditor = function(config){
23328 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23329 if (!this.toolbars) {
23330 this.toolbars = [];
23333 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23336 * @event initialize
23337 * Fires when the editor is fully initialized (including the iframe)
23338 * @param {HtmlEditor} this
23343 * Fires when the editor is first receives the focus. Any insertion must wait
23344 * until after this event.
23345 * @param {HtmlEditor} this
23349 * @event beforesync
23350 * Fires before the textarea is updated with content from the editor iframe. Return false
23351 * to cancel the sync.
23352 * @param {HtmlEditor} this
23353 * @param {String} html
23357 * @event beforepush
23358 * Fires before the iframe editor is updated with content from the textarea. Return false
23359 * to cancel the push.
23360 * @param {HtmlEditor} this
23361 * @param {String} html
23366 * Fires when the textarea is updated with content from the editor iframe.
23367 * @param {HtmlEditor} this
23368 * @param {String} html
23373 * Fires when the iframe editor is updated with content from the textarea.
23374 * @param {HtmlEditor} this
23375 * @param {String} html
23379 * @event editmodechange
23380 * Fires when the editor switches edit modes
23381 * @param {HtmlEditor} this
23382 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23384 editmodechange: true,
23386 * @event editorevent
23387 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23388 * @param {HtmlEditor} this
23392 * @event firstfocus
23393 * Fires when on first focus - needed by toolbars..
23394 * @param {HtmlEditor} this
23399 * Auto save the htmlEditor value as a file into Events
23400 * @param {HtmlEditor} this
23404 * @event savedpreview
23405 * preview the saved version of htmlEditor
23406 * @param {HtmlEditor} this
23413 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23417 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23422 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23427 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23432 * @cfg {Number} height (in pixels)
23436 * @cfg {Number} width (in pixels)
23441 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23444 stylesheets: false,
23449 // private properties
23450 validationEvent : false,
23452 initialized : false,
23455 onFocus : Roo.emptyFn,
23457 hideMode:'offsets',
23459 tbContainer : false,
23463 toolbarContainer :function() {
23464 return this.wrap.select('.x-html-editor-tb',true).first();
23468 * Protected method that will not generally be called directly. It
23469 * is called when the editor creates its toolbar. Override this method if you need to
23470 * add custom toolbar buttons.
23471 * @param {HtmlEditor} editor
23473 createToolbar : function(){
23474 Roo.log('renewing');
23475 Roo.log("create toolbars");
23477 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23478 this.toolbars[0].render(this.toolbarContainer());
23482 // if (!editor.toolbars || !editor.toolbars.length) {
23483 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23486 // for (var i =0 ; i < editor.toolbars.length;i++) {
23487 // editor.toolbars[i] = Roo.factory(
23488 // typeof(editor.toolbars[i]) == 'string' ?
23489 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23490 // Roo.bootstrap.HtmlEditor);
23491 // editor.toolbars[i].init(editor);
23497 onRender : function(ct, position)
23499 // Roo.log("Call onRender: " + this.xtype);
23501 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23503 this.wrap = this.inputEl().wrap({
23504 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23507 this.editorcore.onRender(ct, position);
23509 if (this.resizable) {
23510 this.resizeEl = new Roo.Resizable(this.wrap, {
23514 minHeight : this.height,
23515 height: this.height,
23516 handles : this.resizable,
23519 resize : function(r, w, h) {
23520 _t.onResize(w,h); // -something
23526 this.createToolbar(this);
23529 if(!this.width && this.resizable){
23530 this.setSize(this.wrap.getSize());
23532 if (this.resizeEl) {
23533 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23534 // should trigger onReize..
23540 onResize : function(w, h)
23542 Roo.log('resize: ' +w + ',' + h );
23543 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23547 if(this.inputEl() ){
23548 if(typeof w == 'number'){
23549 var aw = w - this.wrap.getFrameWidth('lr');
23550 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23553 if(typeof h == 'number'){
23554 var tbh = -11; // fixme it needs to tool bar size!
23555 for (var i =0; i < this.toolbars.length;i++) {
23556 // fixme - ask toolbars for heights?
23557 tbh += this.toolbars[i].el.getHeight();
23558 //if (this.toolbars[i].footer) {
23559 // tbh += this.toolbars[i].footer.el.getHeight();
23567 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23568 ah -= 5; // knock a few pixes off for look..
23569 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23573 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23574 this.editorcore.onResize(ew,eh);
23579 * Toggles the editor between standard and source edit mode.
23580 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23582 toggleSourceEdit : function(sourceEditMode)
23584 this.editorcore.toggleSourceEdit(sourceEditMode);
23586 if(this.editorcore.sourceEditMode){
23587 Roo.log('editor - showing textarea');
23590 // Roo.log(this.syncValue());
23592 this.inputEl().removeClass(['hide', 'x-hidden']);
23593 this.inputEl().dom.removeAttribute('tabIndex');
23594 this.inputEl().focus();
23596 Roo.log('editor - hiding textarea');
23598 // Roo.log(this.pushValue());
23601 this.inputEl().addClass(['hide', 'x-hidden']);
23602 this.inputEl().dom.setAttribute('tabIndex', -1);
23603 //this.deferFocus();
23606 if(this.resizable){
23607 this.setSize(this.wrap.getSize());
23610 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23613 // private (for BoxComponent)
23614 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23616 // private (for BoxComponent)
23617 getResizeEl : function(){
23621 // private (for BoxComponent)
23622 getPositionEl : function(){
23627 initEvents : function(){
23628 this.originalValue = this.getValue();
23632 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23635 // markInvalid : Roo.emptyFn,
23637 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23640 // clearInvalid : Roo.emptyFn,
23642 setValue : function(v){
23643 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23644 this.editorcore.pushValue();
23649 deferFocus : function(){
23650 this.focus.defer(10, this);
23654 focus : function(){
23655 this.editorcore.focus();
23661 onDestroy : function(){
23667 for (var i =0; i < this.toolbars.length;i++) {
23668 // fixme - ask toolbars for heights?
23669 this.toolbars[i].onDestroy();
23672 this.wrap.dom.innerHTML = '';
23673 this.wrap.remove();
23678 onFirstFocus : function(){
23679 //Roo.log("onFirstFocus");
23680 this.editorcore.onFirstFocus();
23681 for (var i =0; i < this.toolbars.length;i++) {
23682 this.toolbars[i].onFirstFocus();
23688 syncValue : function()
23690 this.editorcore.syncValue();
23693 pushValue : function()
23695 this.editorcore.pushValue();
23699 // hide stuff that is not compatible
23713 * @event specialkey
23717 * @cfg {String} fieldClass @hide
23720 * @cfg {String} focusClass @hide
23723 * @cfg {String} autoCreate @hide
23726 * @cfg {String} inputType @hide
23729 * @cfg {String} invalidClass @hide
23732 * @cfg {String} invalidText @hide
23735 * @cfg {String} msgFx @hide
23738 * @cfg {String} validateOnBlur @hide
23747 Roo.namespace('Roo.bootstrap.htmleditor');
23749 * @class Roo.bootstrap.HtmlEditorToolbar1
23754 new Roo.bootstrap.HtmlEditor({
23757 new Roo.bootstrap.HtmlEditorToolbar1({
23758 disable : { fonts: 1 , format: 1, ..., ... , ...],
23764 * @cfg {Object} disable List of elements to disable..
23765 * @cfg {Array} btns List of additional buttons.
23769 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23772 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23775 Roo.apply(this, config);
23777 // default disabled, based on 'good practice'..
23778 this.disable = this.disable || {};
23779 Roo.applyIf(this.disable, {
23782 specialElements : true
23784 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23786 this.editor = config.editor;
23787 this.editorcore = config.editor.editorcore;
23789 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23791 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23792 // dont call parent... till later.
23794 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23799 editorcore : false,
23804 "h1","h2","h3","h4","h5","h6",
23806 "abbr", "acronym", "address", "cite", "samp", "var",
23810 onRender : function(ct, position)
23812 // Roo.log("Call onRender: " + this.xtype);
23814 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23816 this.el.dom.style.marginBottom = '0';
23818 var editorcore = this.editorcore;
23819 var editor= this.editor;
23822 var btn = function(id,cmd , toggle, handler, html){
23824 var event = toggle ? 'toggle' : 'click';
23829 xns: Roo.bootstrap,
23832 enableToggle:toggle !== false,
23834 pressed : toggle ? false : null,
23837 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23838 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23844 // var cb_box = function...
23849 xns: Roo.bootstrap,
23850 glyphicon : 'font',
23854 xns: Roo.bootstrap,
23858 Roo.each(this.formats, function(f) {
23859 style.menu.items.push({
23861 xns: Roo.bootstrap,
23862 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23867 editorcore.insertTag(this.tagname);
23874 children.push(style);
23876 btn('bold',false,true);
23877 btn('italic',false,true);
23878 btn('align-left', 'justifyleft',true);
23879 btn('align-center', 'justifycenter',true);
23880 btn('align-right' , 'justifyright',true);
23881 btn('link', false, false, function(btn) {
23882 //Roo.log("create link?");
23883 var url = prompt(this.createLinkText, this.defaultLinkValue);
23884 if(url && url != 'http:/'+'/'){
23885 this.editorcore.relayCmd('createlink', url);
23888 btn('list','insertunorderedlist',true);
23889 btn('pencil', false,true, function(btn){
23891 this.toggleSourceEdit(btn.pressed);
23894 if (this.editor.btns.length > 0) {
23895 for (var i = 0; i<this.editor.btns.length; i++) {
23896 children.push(this.editor.btns[i]);
23904 xns: Roo.bootstrap,
23909 xns: Roo.bootstrap,
23914 cog.menu.items.push({
23916 xns: Roo.bootstrap,
23917 html : Clean styles,
23922 editorcore.insertTag(this.tagname);
23931 this.xtype = 'NavSimplebar';
23933 for(var i=0;i< children.length;i++) {
23935 this.buttons.add(this.addxtypeChild(children[i]));
23939 editor.on('editorevent', this.updateToolbar, this);
23941 onBtnClick : function(id)
23943 this.editorcore.relayCmd(id);
23944 this.editorcore.focus();
23948 * Protected method that will not generally be called directly. It triggers
23949 * a toolbar update by reading the markup state of the current selection in the editor.
23951 updateToolbar: function(){
23953 if(!this.editorcore.activated){
23954 this.editor.onFirstFocus(); // is this neeed?
23958 var btns = this.buttons;
23959 var doc = this.editorcore.doc;
23960 btns.get('bold').setActive(doc.queryCommandState('bold'));
23961 btns.get('italic').setActive(doc.queryCommandState('italic'));
23962 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23964 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23965 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23966 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23968 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23969 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23972 var ans = this.editorcore.getAllAncestors();
23973 if (this.formatCombo) {
23976 var store = this.formatCombo.store;
23977 this.formatCombo.setValue("");
23978 for (var i =0; i < ans.length;i++) {
23979 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23981 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23989 // hides menus... - so this cant be on a menu...
23990 Roo.bootstrap.MenuMgr.hideAll();
23992 Roo.bootstrap.MenuMgr.hideAll();
23993 //this.editorsyncValue();
23995 onFirstFocus: function() {
23996 this.buttons.each(function(item){
24000 toggleSourceEdit : function(sourceEditMode){
24003 if(sourceEditMode){
24004 Roo.log("disabling buttons");
24005 this.buttons.each( function(item){
24006 if(item.cmd != 'pencil'){
24012 Roo.log("enabling buttons");
24013 if(this.editorcore.initialized){
24014 this.buttons.each( function(item){
24020 Roo.log("calling toggole on editor");
24021 // tell the editor that it's been pressed..
24022 this.editor.toggleSourceEdit(sourceEditMode);
24032 * @class Roo.bootstrap.Table.AbstractSelectionModel
24033 * @extends Roo.util.Observable
24034 * Abstract base class for grid SelectionModels. It provides the interface that should be
24035 * implemented by descendant classes. This class should not be directly instantiated.
24038 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24039 this.locked = false;
24040 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24044 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
24045 /** @ignore Called by the grid automatically. Do not call directly. */
24046 init : function(grid){
24052 * Locks the selections.
24055 this.locked = true;
24059 * Unlocks the selections.
24061 unlock : function(){
24062 this.locked = false;
24066 * Returns true if the selections are locked.
24067 * @return {Boolean}
24069 isLocked : function(){
24070 return this.locked;
24074 * @extends Roo.bootstrap.Table.AbstractSelectionModel
24075 * @class Roo.bootstrap.Table.RowSelectionModel
24076 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24077 * It supports multiple selections and keyboard selection/navigation.
24079 * @param {Object} config
24082 Roo.bootstrap.Table.RowSelectionModel = function(config){
24083 Roo.apply(this, config);
24084 this.selections = new Roo.util.MixedCollection(false, function(o){
24089 this.lastActive = false;
24093 * @event selectionchange
24094 * Fires when the selection changes
24095 * @param {SelectionModel} this
24097 "selectionchange" : true,
24099 * @event afterselectionchange
24100 * Fires after the selection changes (eg. by key press or clicking)
24101 * @param {SelectionModel} this
24103 "afterselectionchange" : true,
24105 * @event beforerowselect
24106 * Fires when a row is selected being selected, return false to cancel.
24107 * @param {SelectionModel} this
24108 * @param {Number} rowIndex The selected index
24109 * @param {Boolean} keepExisting False if other selections will be cleared
24111 "beforerowselect" : true,
24114 * Fires when a row is selected.
24115 * @param {SelectionModel} this
24116 * @param {Number} rowIndex The selected index
24117 * @param {Roo.data.Record} r The record
24119 "rowselect" : true,
24121 * @event rowdeselect
24122 * Fires when a row is deselected.
24123 * @param {SelectionModel} this
24124 * @param {Number} rowIndex The selected index
24126 "rowdeselect" : true
24128 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24129 this.locked = false;
24132 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24134 * @cfg {Boolean} singleSelect
24135 * True to allow selection of only one row at a time (defaults to false)
24137 singleSelect : false,
24140 initEvents : function()
24143 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24144 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24145 //}else{ // allow click to work like normal
24146 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24148 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24149 this.grid.on("rowclick", this.handleMouseDown, this);
24151 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24152 "up" : function(e){
24154 this.selectPrevious(e.shiftKey);
24155 }else if(this.last !== false && this.lastActive !== false){
24156 var last = this.last;
24157 this.selectRange(this.last, this.lastActive-1);
24158 this.grid.getView().focusRow(this.lastActive);
24159 if(last !== false){
24163 this.selectFirstRow();
24165 this.fireEvent("afterselectionchange", this);
24167 "down" : function(e){
24169 this.selectNext(e.shiftKey);
24170 }else if(this.last !== false && this.lastActive !== false){
24171 var last = this.last;
24172 this.selectRange(this.last, this.lastActive+1);
24173 this.grid.getView().focusRow(this.lastActive);
24174 if(last !== false){
24178 this.selectFirstRow();
24180 this.fireEvent("afterselectionchange", this);
24184 this.grid.store.on('load', function(){
24185 this.selections.clear();
24188 var view = this.grid.view;
24189 view.on("refresh", this.onRefresh, this);
24190 view.on("rowupdated", this.onRowUpdated, this);
24191 view.on("rowremoved", this.onRemove, this);
24196 onRefresh : function()
24198 var ds = this.grid.store, i, v = this.grid.view;
24199 var s = this.selections;
24200 s.each(function(r){
24201 if((i = ds.indexOfId(r.id)) != -1){
24210 onRemove : function(v, index, r){
24211 this.selections.remove(r);
24215 onRowUpdated : function(v, index, r){
24216 if(this.isSelected(r)){
24217 v.onRowSelect(index);
24223 * @param {Array} records The records to select
24224 * @param {Boolean} keepExisting (optional) True to keep existing selections
24226 selectRecords : function(records, keepExisting)
24229 this.clearSelections();
24231 var ds = this.grid.store;
24232 for(var i = 0, len = records.length; i < len; i++){
24233 this.selectRow(ds.indexOf(records[i]), true);
24238 * Gets the number of selected rows.
24241 getCount : function(){
24242 return this.selections.length;
24246 * Selects the first row in the grid.
24248 selectFirstRow : function(){
24253 * Select the last row.
24254 * @param {Boolean} keepExisting (optional) True to keep existing selections
24256 selectLastRow : function(keepExisting){
24257 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24258 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24262 * Selects the row immediately following the last selected row.
24263 * @param {Boolean} keepExisting (optional) True to keep existing selections
24265 selectNext : function(keepExisting)
24267 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24268 this.selectRow(this.last+1, keepExisting);
24269 this.grid.getView().focusRow(this.last);
24274 * Selects the row that precedes the last selected row.
24275 * @param {Boolean} keepExisting (optional) True to keep existing selections
24277 selectPrevious : function(keepExisting){
24279 this.selectRow(this.last-1, keepExisting);
24280 this.grid.getView().focusRow(this.last);
24285 * Returns the selected records
24286 * @return {Array} Array of selected records
24288 getSelections : function(){
24289 return [].concat(this.selections.items);
24293 * Returns the first selected record.
24296 getSelected : function(){
24297 return this.selections.itemAt(0);
24302 * Clears all selections.
24304 clearSelections : function(fast)
24310 var ds = this.grid.store;
24311 var s = this.selections;
24312 s.each(function(r){
24313 this.deselectRow(ds.indexOfId(r.id));
24317 this.selections.clear();
24324 * Selects all rows.
24326 selectAll : function(){
24330 this.selections.clear();
24331 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24332 this.selectRow(i, true);
24337 * Returns True if there is a selection.
24338 * @return {Boolean}
24340 hasSelection : function(){
24341 return this.selections.length > 0;
24345 * Returns True if the specified row is selected.
24346 * @param {Number/Record} record The record or index of the record to check
24347 * @return {Boolean}
24349 isSelected : function(index){
24350 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24351 return (r && this.selections.key(r.id) ? true : false);
24355 * Returns True if the specified record id is selected.
24356 * @param {String} id The id of record to check
24357 * @return {Boolean}
24359 isIdSelected : function(id){
24360 return (this.selections.key(id) ? true : false);
24365 handleMouseDBClick : function(e, t){
24369 handleMouseDown : function(e, t)
24371 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24372 if(this.isLocked() || rowIndex < 0 ){
24375 if(e.shiftKey && this.last !== false){
24376 var last = this.last;
24377 this.selectRange(last, rowIndex, e.ctrlKey);
24378 this.last = last; // reset the last
24382 var isSelected = this.isSelected(rowIndex);
24383 //Roo.log("select row:" + rowIndex);
24385 this.deselectRow(rowIndex);
24387 this.selectRow(rowIndex, true);
24391 if(e.button !== 0 && isSelected){
24392 alert('rowIndex 2: ' + rowIndex);
24393 view.focusRow(rowIndex);
24394 }else if(e.ctrlKey && isSelected){
24395 this.deselectRow(rowIndex);
24396 }else if(!isSelected){
24397 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24398 view.focusRow(rowIndex);
24402 this.fireEvent("afterselectionchange", this);
24405 handleDragableRowClick : function(grid, rowIndex, e)
24407 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24408 this.selectRow(rowIndex, false);
24409 grid.view.focusRow(rowIndex);
24410 this.fireEvent("afterselectionchange", this);
24415 * Selects multiple rows.
24416 * @param {Array} rows Array of the indexes of the row to select
24417 * @param {Boolean} keepExisting (optional) True to keep existing selections
24419 selectRows : function(rows, keepExisting){
24421 this.clearSelections();
24423 for(var i = 0, len = rows.length; i < len; i++){
24424 this.selectRow(rows[i], true);
24429 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24430 * @param {Number} startRow The index of the first row in the range
24431 * @param {Number} endRow The index of the last row in the range
24432 * @param {Boolean} keepExisting (optional) True to retain existing selections
24434 selectRange : function(startRow, endRow, keepExisting){
24439 this.clearSelections();
24441 if(startRow <= endRow){
24442 for(var i = startRow; i <= endRow; i++){
24443 this.selectRow(i, true);
24446 for(var i = startRow; i >= endRow; i--){
24447 this.selectRow(i, true);
24453 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24454 * @param {Number} startRow The index of the first row in the range
24455 * @param {Number} endRow The index of the last row in the range
24457 deselectRange : function(startRow, endRow, preventViewNotify){
24461 for(var i = startRow; i <= endRow; i++){
24462 this.deselectRow(i, preventViewNotify);
24468 * @param {Number} row The index of the row to select
24469 * @param {Boolean} keepExisting (optional) True to keep existing selections
24471 selectRow : function(index, keepExisting, preventViewNotify)
24473 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24476 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24477 if(!keepExisting || this.singleSelect){
24478 this.clearSelections();
24481 var r = this.grid.store.getAt(index);
24482 //console.log('selectRow - record id :' + r.id);
24484 this.selections.add(r);
24485 this.last = this.lastActive = index;
24486 if(!preventViewNotify){
24487 var proxy = new Roo.Element(
24488 this.grid.getRowDom(index)
24490 proxy.addClass('bg-info info');
24492 this.fireEvent("rowselect", this, index, r);
24493 this.fireEvent("selectionchange", this);
24499 * @param {Number} row The index of the row to deselect
24501 deselectRow : function(index, preventViewNotify)
24506 if(this.last == index){
24509 if(this.lastActive == index){
24510 this.lastActive = false;
24513 var r = this.grid.store.getAt(index);
24518 this.selections.remove(r);
24519 //.console.log('deselectRow - record id :' + r.id);
24520 if(!preventViewNotify){
24522 var proxy = new Roo.Element(
24523 this.grid.getRowDom(index)
24525 proxy.removeClass('bg-info info');
24527 this.fireEvent("rowdeselect", this, index);
24528 this.fireEvent("selectionchange", this);
24532 restoreLast : function(){
24534 this.last = this._last;
24539 acceptsNav : function(row, col, cm){
24540 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24544 onEditorKey : function(field, e){
24545 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24550 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24552 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24554 }else if(k == e.ENTER && !e.ctrlKey){
24558 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24560 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24562 }else if(k == e.ESC){
24566 g.startEditing(newCell[0], newCell[1]);
24572 * Ext JS Library 1.1.1
24573 * Copyright(c) 2006-2007, Ext JS, LLC.
24575 * Originally Released Under LGPL - original licence link has changed is not relivant.
24578 * <script type="text/javascript">
24582 * @class Roo.bootstrap.PagingToolbar
24583 * @extends Roo.bootstrap.NavSimplebar
24584 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24586 * Create a new PagingToolbar
24587 * @param {Object} config The config object
24588 * @param {Roo.data.Store} store
24590 Roo.bootstrap.PagingToolbar = function(config)
24592 // old args format still supported... - xtype is prefered..
24593 // created from xtype...
24595 this.ds = config.dataSource;
24597 if (config.store && !this.ds) {
24598 this.store= Roo.factory(config.store, Roo.data);
24599 this.ds = this.store;
24600 this.ds.xmodule = this.xmodule || false;
24603 this.toolbarItems = [];
24604 if (config.items) {
24605 this.toolbarItems = config.items;
24608 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24613 this.bind(this.ds);
24616 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24620 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24622 * @cfg {Roo.data.Store} dataSource
24623 * The underlying data store providing the paged data
24626 * @cfg {String/HTMLElement/Element} container
24627 * container The id or element that will contain the toolbar
24630 * @cfg {Boolean} displayInfo
24631 * True to display the displayMsg (defaults to false)
24634 * @cfg {Number} pageSize
24635 * The number of records to display per page (defaults to 20)
24639 * @cfg {String} displayMsg
24640 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24642 displayMsg : 'Displaying {0} - {1} of {2}',
24644 * @cfg {String} emptyMsg
24645 * The message to display when no records are found (defaults to "No data to display")
24647 emptyMsg : 'No data to display',
24649 * Customizable piece of the default paging text (defaults to "Page")
24652 beforePageText : "Page",
24654 * Customizable piece of the default paging text (defaults to "of %0")
24657 afterPageText : "of {0}",
24659 * Customizable piece of the default paging text (defaults to "First Page")
24662 firstText : "First Page",
24664 * Customizable piece of the default paging text (defaults to "Previous Page")
24667 prevText : "Previous Page",
24669 * Customizable piece of the default paging text (defaults to "Next Page")
24672 nextText : "Next Page",
24674 * Customizable piece of the default paging text (defaults to "Last Page")
24677 lastText : "Last Page",
24679 * Customizable piece of the default paging text (defaults to "Refresh")
24682 refreshText : "Refresh",
24686 onRender : function(ct, position)
24688 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24689 this.navgroup.parentId = this.id;
24690 this.navgroup.onRender(this.el, null);
24691 // add the buttons to the navgroup
24693 if(this.displayInfo){
24694 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24695 this.displayEl = this.el.select('.x-paging-info', true).first();
24696 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24697 // this.displayEl = navel.el.select('span',true).first();
24703 Roo.each(_this.buttons, function(e){ // this might need to use render????
24704 Roo.factory(e).render(_this.el);
24708 Roo.each(_this.toolbarItems, function(e) {
24709 _this.navgroup.addItem(e);
24713 this.first = this.navgroup.addItem({
24714 tooltip: this.firstText,
24716 icon : 'fa fa-backward',
24718 preventDefault: true,
24719 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24722 this.prev = this.navgroup.addItem({
24723 tooltip: this.prevText,
24725 icon : 'fa fa-step-backward',
24727 preventDefault: true,
24728 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24730 //this.addSeparator();
24733 var field = this.navgroup.addItem( {
24735 cls : 'x-paging-position',
24737 html : this.beforePageText +
24738 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24739 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24742 this.field = field.el.select('input', true).first();
24743 this.field.on("keydown", this.onPagingKeydown, this);
24744 this.field.on("focus", function(){this.dom.select();});
24747 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24748 //this.field.setHeight(18);
24749 //this.addSeparator();
24750 this.next = this.navgroup.addItem({
24751 tooltip: this.nextText,
24753 html : ' <i class="fa fa-step-forward">',
24755 preventDefault: true,
24756 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24758 this.last = this.navgroup.addItem({
24759 tooltip: this.lastText,
24760 icon : 'fa fa-forward',
24763 preventDefault: true,
24764 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24766 //this.addSeparator();
24767 this.loading = this.navgroup.addItem({
24768 tooltip: this.refreshText,
24769 icon: 'fa fa-refresh',
24770 preventDefault: true,
24771 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24777 updateInfo : function(){
24778 if(this.displayEl){
24779 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24780 var msg = count == 0 ?
24784 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24786 this.displayEl.update(msg);
24791 onLoad : function(ds, r, o)
24793 this.cursor = o.params.start ? o.params.start : 0;
24795 var d = this.getPageData(),
24800 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24801 this.field.dom.value = ap;
24802 this.first.setDisabled(ap == 1);
24803 this.prev.setDisabled(ap == 1);
24804 this.next.setDisabled(ap == ps);
24805 this.last.setDisabled(ap == ps);
24806 this.loading.enable();
24811 getPageData : function(){
24812 var total = this.ds.getTotalCount();
24815 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24816 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24821 onLoadError : function(){
24822 this.loading.enable();
24826 onPagingKeydown : function(e){
24827 var k = e.getKey();
24828 var d = this.getPageData();
24830 var v = this.field.dom.value, pageNum;
24831 if(!v || isNaN(pageNum = parseInt(v, 10))){
24832 this.field.dom.value = d.activePage;
24835 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24836 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24839 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))
24841 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24842 this.field.dom.value = pageNum;
24843 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24846 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24848 var v = this.field.dom.value, pageNum;
24849 var increment = (e.shiftKey) ? 10 : 1;
24850 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24853 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24854 this.field.dom.value = d.activePage;
24857 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24859 this.field.dom.value = parseInt(v, 10) + increment;
24860 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24861 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24868 beforeLoad : function(){
24870 this.loading.disable();
24875 onClick : function(which){
24884 ds.load({params:{start: 0, limit: this.pageSize}});
24887 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24890 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24893 var total = ds.getTotalCount();
24894 var extra = total % this.pageSize;
24895 var lastStart = extra ? (total - extra) : total-this.pageSize;
24896 ds.load({params:{start: lastStart, limit: this.pageSize}});
24899 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24905 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24906 * @param {Roo.data.Store} store The data store to unbind
24908 unbind : function(ds){
24909 ds.un("beforeload", this.beforeLoad, this);
24910 ds.un("load", this.onLoad, this);
24911 ds.un("loadexception", this.onLoadError, this);
24912 ds.un("remove", this.updateInfo, this);
24913 ds.un("add", this.updateInfo, this);
24914 this.ds = undefined;
24918 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24919 * @param {Roo.data.Store} store The data store to bind
24921 bind : function(ds){
24922 ds.on("beforeload", this.beforeLoad, this);
24923 ds.on("load", this.onLoad, this);
24924 ds.on("loadexception", this.onLoadError, this);
24925 ds.on("remove", this.updateInfo, this);
24926 ds.on("add", this.updateInfo, this);
24937 * @class Roo.bootstrap.MessageBar
24938 * @extends Roo.bootstrap.Component
24939 * Bootstrap MessageBar class
24940 * @cfg {String} html contents of the MessageBar
24941 * @cfg {String} weight (info | success | warning | danger) default info
24942 * @cfg {String} beforeClass insert the bar before the given class
24943 * @cfg {Boolean} closable (true | false) default false
24944 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24947 * Create a new Element
24948 * @param {Object} config The config object
24951 Roo.bootstrap.MessageBar = function(config){
24952 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24955 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24961 beforeClass: 'bootstrap-sticky-wrap',
24963 getAutoCreate : function(){
24967 cls: 'alert alert-dismissable alert-' + this.weight,
24972 html: this.html || ''
24978 cfg.cls += ' alert-messages-fixed';
24992 onRender : function(ct, position)
24994 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24997 var cfg = Roo.apply({}, this.getAutoCreate());
25001 cfg.cls += ' ' + this.cls;
25004 cfg.style = this.style;
25006 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25008 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25011 this.el.select('>button.close').on('click', this.hide, this);
25017 if (!this.rendered) {
25023 this.fireEvent('show', this);
25029 if (!this.rendered) {
25035 this.fireEvent('hide', this);
25038 update : function()
25040 // var e = this.el.dom.firstChild;
25042 // if(this.closable){
25043 // e = e.nextSibling;
25046 // e.data = this.html || '';
25048 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25064 * @class Roo.bootstrap.Graph
25065 * @extends Roo.bootstrap.Component
25066 * Bootstrap Graph class
25070 @cfg {String} graphtype bar | vbar | pie
25071 @cfg {number} g_x coodinator | centre x (pie)
25072 @cfg {number} g_y coodinator | centre y (pie)
25073 @cfg {number} g_r radius (pie)
25074 @cfg {number} g_height height of the chart (respected by all elements in the set)
25075 @cfg {number} g_width width of the chart (respected by all elements in the set)
25076 @cfg {Object} title The title of the chart
25079 -opts (object) options for the chart
25081 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25082 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25084 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.
25085 o stacked (boolean) whether or not to tread values as in a stacked bar chart
25087 o stretch (boolean)
25089 -opts (object) options for the pie
25092 o startAngle (number)
25093 o endAngle (number)
25097 * Create a new Input
25098 * @param {Object} config The config object
25101 Roo.bootstrap.Graph = function(config){
25102 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25108 * The img click event for the img.
25109 * @param {Roo.EventObject} e
25115 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25126 //g_colors: this.colors,
25133 getAutoCreate : function(){
25144 onRender : function(ct,position){
25147 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25149 if (typeof(Raphael) == 'undefined') {
25150 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25154 this.raphael = Raphael(this.el.dom);
25156 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25157 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25158 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25159 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25161 r.text(160, 10, "Single Series Chart").attr(txtattr);
25162 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25163 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25164 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25166 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25167 r.barchart(330, 10, 300, 220, data1);
25168 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25169 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25172 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25173 // r.barchart(30, 30, 560, 250, xdata, {
25174 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25175 // axis : "0 0 1 1",
25176 // axisxlabels : xdata
25177 // //yvalues : cols,
25180 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25182 // this.load(null,xdata,{
25183 // axis : "0 0 1 1",
25184 // axisxlabels : xdata
25189 load : function(graphtype,xdata,opts)
25191 this.raphael.clear();
25193 graphtype = this.graphtype;
25198 var r = this.raphael,
25199 fin = function () {
25200 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25202 fout = function () {
25203 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25205 pfin = function() {
25206 this.sector.stop();
25207 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25210 this.label[0].stop();
25211 this.label[0].attr({ r: 7.5 });
25212 this.label[1].attr({ "font-weight": 800 });
25215 pfout = function() {
25216 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25219 this.label[0].animate({ r: 5 }, 500, "bounce");
25220 this.label[1].attr({ "font-weight": 400 });
25226 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25229 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25232 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25233 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25235 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25242 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25247 setTitle: function(o)
25252 initEvents: function() {
25255 this.el.on('click', this.onClick, this);
25259 onClick : function(e)
25261 Roo.log('img onclick');
25262 this.fireEvent('click', this, e);
25274 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25277 * @class Roo.bootstrap.dash.NumberBox
25278 * @extends Roo.bootstrap.Component
25279 * Bootstrap NumberBox class
25280 * @cfg {String} headline Box headline
25281 * @cfg {String} content Box content
25282 * @cfg {String} icon Box icon
25283 * @cfg {String} footer Footer text
25284 * @cfg {String} fhref Footer href
25287 * Create a new NumberBox
25288 * @param {Object} config The config object
25292 Roo.bootstrap.dash.NumberBox = function(config){
25293 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25297 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25306 getAutoCreate : function(){
25310 cls : 'small-box ',
25318 cls : 'roo-headline',
25319 html : this.headline
25323 cls : 'roo-content',
25324 html : this.content
25338 cls : 'ion ' + this.icon
25347 cls : 'small-box-footer',
25348 href : this.fhref || '#',
25352 cfg.cn.push(footer);
25359 onRender : function(ct,position){
25360 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25367 setHeadline: function (value)
25369 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25372 setFooter: function (value, href)
25374 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25377 this.el.select('a.small-box-footer',true).first().attr('href', href);
25382 setContent: function (value)
25384 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25387 initEvents: function()
25401 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25404 * @class Roo.bootstrap.dash.TabBox
25405 * @extends Roo.bootstrap.Component
25406 * Bootstrap TabBox class
25407 * @cfg {String} title Title of the TabBox
25408 * @cfg {String} icon Icon of the TabBox
25409 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25410 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25413 * Create a new TabBox
25414 * @param {Object} config The config object
25418 Roo.bootstrap.dash.TabBox = function(config){
25419 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25424 * When a pane is added
25425 * @param {Roo.bootstrap.dash.TabPane} pane
25429 * @event activatepane
25430 * When a pane is activated
25431 * @param {Roo.bootstrap.dash.TabPane} pane
25433 "activatepane" : true
25441 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25446 tabScrollable : false,
25448 getChildContainer : function()
25450 return this.el.select('.tab-content', true).first();
25453 getAutoCreate : function(){
25457 cls: 'pull-left header',
25465 cls: 'fa ' + this.icon
25471 cls: 'nav nav-tabs pull-right',
25477 if(this.tabScrollable){
25484 cls: 'nav nav-tabs pull-right',
25495 cls: 'nav-tabs-custom',
25500 cls: 'tab-content no-padding',
25508 initEvents : function()
25510 //Roo.log('add add pane handler');
25511 this.on('addpane', this.onAddPane, this);
25514 * Updates the box title
25515 * @param {String} html to set the title to.
25517 setTitle : function(value)
25519 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25521 onAddPane : function(pane)
25523 this.panes.push(pane);
25524 //Roo.log('addpane');
25526 // tabs are rendere left to right..
25527 if(!this.showtabs){
25531 var ctr = this.el.select('.nav-tabs', true).first();
25534 var existing = ctr.select('.nav-tab',true);
25535 var qty = existing.getCount();;
25538 var tab = ctr.createChild({
25540 cls : 'nav-tab' + (qty ? '' : ' active'),
25548 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25551 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25553 pane.el.addClass('active');
25558 onTabClick : function(ev,un,ob,pane)
25560 //Roo.log('tab - prev default');
25561 ev.preventDefault();
25564 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25565 pane.tab.addClass('active');
25566 //Roo.log(pane.title);
25567 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25568 // technically we should have a deactivate event.. but maybe add later.
25569 // and it should not de-activate the selected tab...
25570 this.fireEvent('activatepane', pane);
25571 pane.el.addClass('active');
25572 pane.fireEvent('activate');
25577 getActivePane : function()
25580 Roo.each(this.panes, function(p) {
25581 if(p.el.hasClass('active')){
25602 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25604 * @class Roo.bootstrap.TabPane
25605 * @extends Roo.bootstrap.Component
25606 * Bootstrap TabPane class
25607 * @cfg {Boolean} active (false | true) Default false
25608 * @cfg {String} title title of panel
25612 * Create a new TabPane
25613 * @param {Object} config The config object
25616 Roo.bootstrap.dash.TabPane = function(config){
25617 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25623 * When a pane is activated
25624 * @param {Roo.bootstrap.dash.TabPane} pane
25631 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25636 // the tabBox that this is attached to.
25639 getAutoCreate : function()
25647 cfg.cls += ' active';
25652 initEvents : function()
25654 //Roo.log('trigger add pane handler');
25655 this.parent().fireEvent('addpane', this)
25659 * Updates the tab title
25660 * @param {String} html to set the title to.
25662 setTitle: function(str)
25668 this.tab.select('a', true).first().dom.innerHTML = str;
25685 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25688 * @class Roo.bootstrap.menu.Menu
25689 * @extends Roo.bootstrap.Component
25690 * Bootstrap Menu class - container for Menu
25691 * @cfg {String} html Text of the menu
25692 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25693 * @cfg {String} icon Font awesome icon
25694 * @cfg {String} pos Menu align to (top | bottom) default bottom
25698 * Create a new Menu
25699 * @param {Object} config The config object
25703 Roo.bootstrap.menu.Menu = function(config){
25704 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25708 * @event beforeshow
25709 * Fires before this menu is displayed
25710 * @param {Roo.bootstrap.menu.Menu} this
25714 * @event beforehide
25715 * Fires before this menu is hidden
25716 * @param {Roo.bootstrap.menu.Menu} this
25721 * Fires after this menu is displayed
25722 * @param {Roo.bootstrap.menu.Menu} this
25727 * Fires after this menu is hidden
25728 * @param {Roo.bootstrap.menu.Menu} this
25733 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25734 * @param {Roo.bootstrap.menu.Menu} this
25735 * @param {Roo.EventObject} e
25742 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25746 weight : 'default',
25751 getChildContainer : function() {
25752 if(this.isSubMenu){
25756 return this.el.select('ul.dropdown-menu', true).first();
25759 getAutoCreate : function()
25764 cls : 'roo-menu-text',
25772 cls : 'fa ' + this.icon
25783 cls : 'dropdown-button btn btn-' + this.weight,
25788 cls : 'dropdown-toggle btn btn-' + this.weight,
25798 cls : 'dropdown-menu'
25804 if(this.pos == 'top'){
25805 cfg.cls += ' dropup';
25808 if(this.isSubMenu){
25811 cls : 'dropdown-menu'
25818 onRender : function(ct, position)
25820 this.isSubMenu = ct.hasClass('dropdown-submenu');
25822 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25825 initEvents : function()
25827 if(this.isSubMenu){
25831 this.hidden = true;
25833 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25834 this.triggerEl.on('click', this.onTriggerPress, this);
25836 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25837 this.buttonEl.on('click', this.onClick, this);
25843 if(this.isSubMenu){
25847 return this.el.select('ul.dropdown-menu', true).first();
25850 onClick : function(e)
25852 this.fireEvent("click", this, e);
25855 onTriggerPress : function(e)
25857 if (this.isVisible()) {
25864 isVisible : function(){
25865 return !this.hidden;
25870 this.fireEvent("beforeshow", this);
25872 this.hidden = false;
25873 this.el.addClass('open');
25875 Roo.get(document).on("mouseup", this.onMouseUp, this);
25877 this.fireEvent("show", this);
25884 this.fireEvent("beforehide", this);
25886 this.hidden = true;
25887 this.el.removeClass('open');
25889 Roo.get(document).un("mouseup", this.onMouseUp);
25891 this.fireEvent("hide", this);
25894 onMouseUp : function()
25908 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25911 * @class Roo.bootstrap.menu.Item
25912 * @extends Roo.bootstrap.Component
25913 * Bootstrap MenuItem class
25914 * @cfg {Boolean} submenu (true | false) default false
25915 * @cfg {String} html text of the item
25916 * @cfg {String} href the link
25917 * @cfg {Boolean} disable (true | false) default false
25918 * @cfg {Boolean} preventDefault (true | false) default true
25919 * @cfg {String} icon Font awesome icon
25920 * @cfg {String} pos Submenu align to (left | right) default right
25924 * Create a new Item
25925 * @param {Object} config The config object
25929 Roo.bootstrap.menu.Item = function(config){
25930 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25934 * Fires when the mouse is hovering over this menu
25935 * @param {Roo.bootstrap.menu.Item} this
25936 * @param {Roo.EventObject} e
25941 * Fires when the mouse exits this menu
25942 * @param {Roo.bootstrap.menu.Item} this
25943 * @param {Roo.EventObject} e
25949 * The raw click event for the entire grid.
25950 * @param {Roo.EventObject} e
25956 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25961 preventDefault: true,
25966 getAutoCreate : function()
25971 cls : 'roo-menu-item-text',
25979 cls : 'fa ' + this.icon
25988 href : this.href || '#',
25995 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25999 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26001 if(this.pos == 'left'){
26002 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26009 initEvents : function()
26011 this.el.on('mouseover', this.onMouseOver, this);
26012 this.el.on('mouseout', this.onMouseOut, this);
26014 this.el.select('a', true).first().on('click', this.onClick, this);
26018 onClick : function(e)
26020 if(this.preventDefault){
26021 e.preventDefault();
26024 this.fireEvent("click", this, e);
26027 onMouseOver : function(e)
26029 if(this.submenu && this.pos == 'left'){
26030 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26033 this.fireEvent("mouseover", this, e);
26036 onMouseOut : function(e)
26038 this.fireEvent("mouseout", this, e);
26050 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26053 * @class Roo.bootstrap.menu.Separator
26054 * @extends Roo.bootstrap.Component
26055 * Bootstrap Separator class
26058 * Create a new Separator
26059 * @param {Object} config The config object
26063 Roo.bootstrap.menu.Separator = function(config){
26064 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26067 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
26069 getAutoCreate : function(){
26090 * @class Roo.bootstrap.Tooltip
26091 * Bootstrap Tooltip class
26092 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26093 * to determine which dom element triggers the tooltip.
26095 * It needs to add support for additional attributes like tooltip-position
26098 * Create a new Toolti
26099 * @param {Object} config The config object
26102 Roo.bootstrap.Tooltip = function(config){
26103 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26105 this.alignment = Roo.bootstrap.Tooltip.alignment;
26107 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26108 this.alignment = config.alignment;
26113 Roo.apply(Roo.bootstrap.Tooltip, {
26115 * @function init initialize tooltip monitoring.
26119 currentTip : false,
26120 currentRegion : false,
26126 Roo.get(document).on('mouseover', this.enter ,this);
26127 Roo.get(document).on('mouseout', this.leave, this);
26130 this.currentTip = new Roo.bootstrap.Tooltip();
26133 enter : function(ev)
26135 var dom = ev.getTarget();
26137 //Roo.log(['enter',dom]);
26138 var el = Roo.fly(dom);
26139 if (this.currentEl) {
26141 //Roo.log(this.currentEl);
26142 //Roo.log(this.currentEl.contains(dom));
26143 if (this.currentEl == el) {
26146 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26152 if (this.currentTip.el) {
26153 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26157 if(!el || el.dom == document){
26163 // you can not look for children, as if el is the body.. then everythign is the child..
26164 if (!el.attr('tooltip')) { //
26165 if (!el.select("[tooltip]").elements.length) {
26168 // is the mouse over this child...?
26169 bindEl = el.select("[tooltip]").first();
26170 var xy = ev.getXY();
26171 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26172 //Roo.log("not in region.");
26175 //Roo.log("child element over..");
26178 this.currentEl = bindEl;
26179 this.currentTip.bind(bindEl);
26180 this.currentRegion = Roo.lib.Region.getRegion(dom);
26181 this.currentTip.enter();
26184 leave : function(ev)
26186 var dom = ev.getTarget();
26187 //Roo.log(['leave',dom]);
26188 if (!this.currentEl) {
26193 if (dom != this.currentEl.dom) {
26196 var xy = ev.getXY();
26197 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26200 // only activate leave if mouse cursor is outside... bounding box..
26205 if (this.currentTip) {
26206 this.currentTip.leave();
26208 //Roo.log('clear currentEl');
26209 this.currentEl = false;
26214 'left' : ['r-l', [-2,0], 'right'],
26215 'right' : ['l-r', [2,0], 'left'],
26216 'bottom' : ['t-b', [0,2], 'top'],
26217 'top' : [ 'b-t', [0,-2], 'bottom']
26223 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26228 delay : null, // can be { show : 300 , hide: 500}
26232 hoverState : null, //???
26234 placement : 'bottom',
26238 getAutoCreate : function(){
26245 cls : 'tooltip-arrow'
26248 cls : 'tooltip-inner'
26255 bind : function(el)
26261 enter : function () {
26263 if (this.timeout != null) {
26264 clearTimeout(this.timeout);
26267 this.hoverState = 'in';
26268 //Roo.log("enter - show");
26269 if (!this.delay || !this.delay.show) {
26274 this.timeout = setTimeout(function () {
26275 if (_t.hoverState == 'in') {
26278 }, this.delay.show);
26282 clearTimeout(this.timeout);
26284 this.hoverState = 'out';
26285 if (!this.delay || !this.delay.hide) {
26291 this.timeout = setTimeout(function () {
26292 //Roo.log("leave - timeout");
26294 if (_t.hoverState == 'out') {
26296 Roo.bootstrap.Tooltip.currentEl = false;
26301 show : function (msg)
26304 this.render(document.body);
26307 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26309 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26311 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26313 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26315 var placement = typeof this.placement == 'function' ?
26316 this.placement.call(this, this.el, on_el) :
26319 var autoToken = /\s?auto?\s?/i;
26320 var autoPlace = autoToken.test(placement);
26322 placement = placement.replace(autoToken, '') || 'top';
26326 //this.el.setXY([0,0]);
26328 //this.el.dom.style.display='block';
26330 //this.el.appendTo(on_el);
26332 var p = this.getPosition();
26333 var box = this.el.getBox();
26339 var align = this.alignment[placement];
26341 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26343 if(placement == 'top' || placement == 'bottom'){
26345 placement = 'right';
26348 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26349 placement = 'left';
26352 var scroll = Roo.select('body', true).first().getScroll();
26354 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26358 align = this.alignment[placement];
26361 this.el.alignTo(this.bindEl, align[0],align[1]);
26362 //var arrow = this.el.select('.arrow',true).first();
26363 //arrow.set(align[2],
26365 this.el.addClass(placement);
26367 this.el.addClass('in fade');
26369 this.hoverState = null;
26371 if (this.el.hasClass('fade')) {
26382 //this.el.setXY([0,0]);
26383 this.el.removeClass('in');
26399 * @class Roo.bootstrap.LocationPicker
26400 * @extends Roo.bootstrap.Component
26401 * Bootstrap LocationPicker class
26402 * @cfg {Number} latitude Position when init default 0
26403 * @cfg {Number} longitude Position when init default 0
26404 * @cfg {Number} zoom default 15
26405 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26406 * @cfg {Boolean} mapTypeControl default false
26407 * @cfg {Boolean} disableDoubleClickZoom default false
26408 * @cfg {Boolean} scrollwheel default true
26409 * @cfg {Boolean} streetViewControl default false
26410 * @cfg {Number} radius default 0
26411 * @cfg {String} locationName
26412 * @cfg {Boolean} draggable default true
26413 * @cfg {Boolean} enableAutocomplete default false
26414 * @cfg {Boolean} enableReverseGeocode default true
26415 * @cfg {String} markerTitle
26418 * Create a new LocationPicker
26419 * @param {Object} config The config object
26423 Roo.bootstrap.LocationPicker = function(config){
26425 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26430 * Fires when the picker initialized.
26431 * @param {Roo.bootstrap.LocationPicker} this
26432 * @param {Google Location} location
26436 * @event positionchanged
26437 * Fires when the picker position changed.
26438 * @param {Roo.bootstrap.LocationPicker} this
26439 * @param {Google Location} location
26441 positionchanged : true,
26444 * Fires when the map resize.
26445 * @param {Roo.bootstrap.LocationPicker} this
26450 * Fires when the map show.
26451 * @param {Roo.bootstrap.LocationPicker} this
26456 * Fires when the map hide.
26457 * @param {Roo.bootstrap.LocationPicker} this
26462 * Fires when click the map.
26463 * @param {Roo.bootstrap.LocationPicker} this
26464 * @param {Map event} e
26468 * @event mapRightClick
26469 * Fires when right click the map.
26470 * @param {Roo.bootstrap.LocationPicker} this
26471 * @param {Map event} e
26473 mapRightClick : true,
26475 * @event markerClick
26476 * Fires when click the marker.
26477 * @param {Roo.bootstrap.LocationPicker} this
26478 * @param {Map event} e
26480 markerClick : true,
26482 * @event markerRightClick
26483 * Fires when right click the marker.
26484 * @param {Roo.bootstrap.LocationPicker} this
26485 * @param {Map event} e
26487 markerRightClick : true,
26489 * @event OverlayViewDraw
26490 * Fires when OverlayView Draw
26491 * @param {Roo.bootstrap.LocationPicker} this
26493 OverlayViewDraw : true,
26495 * @event OverlayViewOnAdd
26496 * Fires when OverlayView Draw
26497 * @param {Roo.bootstrap.LocationPicker} this
26499 OverlayViewOnAdd : true,
26501 * @event OverlayViewOnRemove
26502 * Fires when OverlayView Draw
26503 * @param {Roo.bootstrap.LocationPicker} this
26505 OverlayViewOnRemove : true,
26507 * @event OverlayViewShow
26508 * Fires when OverlayView Draw
26509 * @param {Roo.bootstrap.LocationPicker} this
26510 * @param {Pixel} cpx
26512 OverlayViewShow : true,
26514 * @event OverlayViewHide
26515 * Fires when OverlayView Draw
26516 * @param {Roo.bootstrap.LocationPicker} this
26518 OverlayViewHide : true,
26520 * @event loadexception
26521 * Fires when load google lib failed.
26522 * @param {Roo.bootstrap.LocationPicker} this
26524 loadexception : true
26529 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26531 gMapContext: false,
26537 mapTypeControl: false,
26538 disableDoubleClickZoom: false,
26540 streetViewControl: false,
26544 enableAutocomplete: false,
26545 enableReverseGeocode: true,
26548 getAutoCreate: function()
26553 cls: 'roo-location-picker'
26559 initEvents: function(ct, position)
26561 if(!this.el.getWidth() || this.isApplied()){
26565 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26570 initial: function()
26572 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26573 this.fireEvent('loadexception', this);
26577 if(!this.mapTypeId){
26578 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26581 this.gMapContext = this.GMapContext();
26583 this.initOverlayView();
26585 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26589 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26590 _this.setPosition(_this.gMapContext.marker.position);
26593 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26594 _this.fireEvent('mapClick', this, event);
26598 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26599 _this.fireEvent('mapRightClick', this, event);
26603 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26604 _this.fireEvent('markerClick', this, event);
26608 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26609 _this.fireEvent('markerRightClick', this, event);
26613 this.setPosition(this.gMapContext.location);
26615 this.fireEvent('initial', this, this.gMapContext.location);
26618 initOverlayView: function()
26622 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26626 _this.fireEvent('OverlayViewDraw', _this);
26631 _this.fireEvent('OverlayViewOnAdd', _this);
26634 onRemove: function()
26636 _this.fireEvent('OverlayViewOnRemove', _this);
26639 show: function(cpx)
26641 _this.fireEvent('OverlayViewShow', _this, cpx);
26646 _this.fireEvent('OverlayViewHide', _this);
26652 fromLatLngToContainerPixel: function(event)
26654 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26657 isApplied: function()
26659 return this.getGmapContext() == false ? false : true;
26662 getGmapContext: function()
26664 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26667 GMapContext: function()
26669 var position = new google.maps.LatLng(this.latitude, this.longitude);
26671 var _map = new google.maps.Map(this.el.dom, {
26674 mapTypeId: this.mapTypeId,
26675 mapTypeControl: this.mapTypeControl,
26676 disableDoubleClickZoom: this.disableDoubleClickZoom,
26677 scrollwheel: this.scrollwheel,
26678 streetViewControl: this.streetViewControl,
26679 locationName: this.locationName,
26680 draggable: this.draggable,
26681 enableAutocomplete: this.enableAutocomplete,
26682 enableReverseGeocode: this.enableReverseGeocode
26685 var _marker = new google.maps.Marker({
26686 position: position,
26688 title: this.markerTitle,
26689 draggable: this.draggable
26696 location: position,
26697 radius: this.radius,
26698 locationName: this.locationName,
26699 addressComponents: {
26700 formatted_address: null,
26701 addressLine1: null,
26702 addressLine2: null,
26704 streetNumber: null,
26708 stateOrProvince: null
26711 domContainer: this.el.dom,
26712 geodecoder: new google.maps.Geocoder()
26716 drawCircle: function(center, radius, options)
26718 if (this.gMapContext.circle != null) {
26719 this.gMapContext.circle.setMap(null);
26723 options = Roo.apply({}, options, {
26724 strokeColor: "#0000FF",
26725 strokeOpacity: .35,
26727 fillColor: "#0000FF",
26731 options.map = this.gMapContext.map;
26732 options.radius = radius;
26733 options.center = center;
26734 this.gMapContext.circle = new google.maps.Circle(options);
26735 return this.gMapContext.circle;
26741 setPosition: function(location)
26743 this.gMapContext.location = location;
26744 this.gMapContext.marker.setPosition(location);
26745 this.gMapContext.map.panTo(location);
26746 this.drawCircle(location, this.gMapContext.radius, {});
26750 if (this.gMapContext.settings.enableReverseGeocode) {
26751 this.gMapContext.geodecoder.geocode({
26752 latLng: this.gMapContext.location
26753 }, function(results, status) {
26755 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26756 _this.gMapContext.locationName = results[0].formatted_address;
26757 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26759 _this.fireEvent('positionchanged', this, location);
26766 this.fireEvent('positionchanged', this, location);
26771 google.maps.event.trigger(this.gMapContext.map, "resize");
26773 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26775 this.fireEvent('resize', this);
26778 setPositionByLatLng: function(latitude, longitude)
26780 this.setPosition(new google.maps.LatLng(latitude, longitude));
26783 getCurrentPosition: function()
26786 latitude: this.gMapContext.location.lat(),
26787 longitude: this.gMapContext.location.lng()
26791 getAddressName: function()
26793 return this.gMapContext.locationName;
26796 getAddressComponents: function()
26798 return this.gMapContext.addressComponents;
26801 address_component_from_google_geocode: function(address_components)
26805 for (var i = 0; i < address_components.length; i++) {
26806 var component = address_components[i];
26807 if (component.types.indexOf("postal_code") >= 0) {
26808 result.postalCode = component.short_name;
26809 } else if (component.types.indexOf("street_number") >= 0) {
26810 result.streetNumber = component.short_name;
26811 } else if (component.types.indexOf("route") >= 0) {
26812 result.streetName = component.short_name;
26813 } else if (component.types.indexOf("neighborhood") >= 0) {
26814 result.city = component.short_name;
26815 } else if (component.types.indexOf("locality") >= 0) {
26816 result.city = component.short_name;
26817 } else if (component.types.indexOf("sublocality") >= 0) {
26818 result.district = component.short_name;
26819 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26820 result.stateOrProvince = component.short_name;
26821 } else if (component.types.indexOf("country") >= 0) {
26822 result.country = component.short_name;
26826 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26827 result.addressLine2 = "";
26831 setZoomLevel: function(zoom)
26833 this.gMapContext.map.setZoom(zoom);
26846 this.fireEvent('show', this);
26857 this.fireEvent('hide', this);
26862 Roo.apply(Roo.bootstrap.LocationPicker, {
26864 OverlayView : function(map, options)
26866 options = options || {};
26880 * @class Roo.bootstrap.Alert
26881 * @extends Roo.bootstrap.Component
26882 * Bootstrap Alert class
26883 * @cfg {String} title The title of alert
26884 * @cfg {String} html The content of alert
26885 * @cfg {String} weight ( success | info | warning | danger )
26886 * @cfg {String} faicon font-awesomeicon
26889 * Create a new alert
26890 * @param {Object} config The config object
26894 Roo.bootstrap.Alert = function(config){
26895 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26899 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26906 getAutoCreate : function()
26915 cls : 'roo-alert-icon'
26920 cls : 'roo-alert-title',
26925 cls : 'roo-alert-text',
26932 cfg.cn[0].cls += ' fa ' + this.faicon;
26936 cfg.cls += ' alert-' + this.weight;
26942 initEvents: function()
26944 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26947 setTitle : function(str)
26949 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26952 setText : function(str)
26954 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26957 setWeight : function(weight)
26960 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26963 this.weight = weight;
26965 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26968 setIcon : function(icon)
26971 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26974 this.faicon = icon;
26976 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26997 * @class Roo.bootstrap.UploadCropbox
26998 * @extends Roo.bootstrap.Component
26999 * Bootstrap UploadCropbox class
27000 * @cfg {String} emptyText show when image has been loaded
27001 * @cfg {String} rotateNotify show when image too small to rotate
27002 * @cfg {Number} errorTimeout default 3000
27003 * @cfg {Number} minWidth default 300
27004 * @cfg {Number} minHeight default 300
27005 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27006 * @cfg {Boolean} isDocument (true|false) default false
27007 * @cfg {String} url action url
27008 * @cfg {String} paramName default 'imageUpload'
27009 * @cfg {String} method default POST
27010 * @cfg {Boolean} loadMask (true|false) default true
27011 * @cfg {Boolean} loadingText default 'Loading...'
27014 * Create a new UploadCropbox
27015 * @param {Object} config The config object
27018 Roo.bootstrap.UploadCropbox = function(config){
27019 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27023 * @event beforeselectfile
27024 * Fire before select file
27025 * @param {Roo.bootstrap.UploadCropbox} this
27027 "beforeselectfile" : true,
27030 * Fire after initEvent
27031 * @param {Roo.bootstrap.UploadCropbox} this
27036 * Fire after initEvent
27037 * @param {Roo.bootstrap.UploadCropbox} this
27038 * @param {String} data
27043 * Fire when preparing the file data
27044 * @param {Roo.bootstrap.UploadCropbox} this
27045 * @param {Object} file
27050 * Fire when get exception
27051 * @param {Roo.bootstrap.UploadCropbox} this
27052 * @param {XMLHttpRequest} xhr
27054 "exception" : true,
27056 * @event beforeloadcanvas
27057 * Fire before load the canvas
27058 * @param {Roo.bootstrap.UploadCropbox} this
27059 * @param {String} src
27061 "beforeloadcanvas" : true,
27064 * Fire when trash image
27065 * @param {Roo.bootstrap.UploadCropbox} this
27070 * Fire when download the image
27071 * @param {Roo.bootstrap.UploadCropbox} this
27075 * @event footerbuttonclick
27076 * Fire when footerbuttonclick
27077 * @param {Roo.bootstrap.UploadCropbox} this
27078 * @param {String} type
27080 "footerbuttonclick" : true,
27084 * @param {Roo.bootstrap.UploadCropbox} this
27089 * Fire when rotate the image
27090 * @param {Roo.bootstrap.UploadCropbox} this
27091 * @param {String} pos
27096 * Fire when inspect the file
27097 * @param {Roo.bootstrap.UploadCropbox} this
27098 * @param {Object} file
27103 * Fire when xhr upload the file
27104 * @param {Roo.bootstrap.UploadCropbox} this
27105 * @param {Object} data
27110 * Fire when arrange the file data
27111 * @param {Roo.bootstrap.UploadCropbox} this
27112 * @param {Object} formData
27117 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27120 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27122 emptyText : 'Click to upload image',
27123 rotateNotify : 'Image is too small to rotate',
27124 errorTimeout : 3000,
27138 cropType : 'image/jpeg',
27140 canvasLoaded : false,
27141 isDocument : false,
27143 paramName : 'imageUpload',
27145 loadingText : 'Loading...',
27148 getAutoCreate : function()
27152 cls : 'roo-upload-cropbox',
27156 cls : 'roo-upload-cropbox-selector',
27161 cls : 'roo-upload-cropbox-body',
27162 style : 'cursor:pointer',
27166 cls : 'roo-upload-cropbox-preview'
27170 cls : 'roo-upload-cropbox-thumb'
27174 cls : 'roo-upload-cropbox-empty-notify',
27175 html : this.emptyText
27179 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27180 html : this.rotateNotify
27186 cls : 'roo-upload-cropbox-footer',
27189 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27199 onRender : function(ct, position)
27201 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27203 if (this.buttons.length) {
27205 Roo.each(this.buttons, function(bb) {
27207 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27209 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27215 this.maskEl = this.el;
27219 initEvents : function()
27221 this.urlAPI = (window.createObjectURL && window) ||
27222 (window.URL && URL.revokeObjectURL && URL) ||
27223 (window.webkitURL && webkitURL);
27225 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27226 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27228 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27229 this.selectorEl.hide();
27231 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27232 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27234 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27235 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27236 this.thumbEl.hide();
27238 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27239 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27241 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27242 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27243 this.errorEl.hide();
27245 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27246 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27247 this.footerEl.hide();
27249 this.setThumbBoxSize();
27255 this.fireEvent('initial', this);
27262 window.addEventListener("resize", function() { _this.resize(); } );
27264 this.bodyEl.on('click', this.beforeSelectFile, this);
27267 this.bodyEl.on('touchstart', this.onTouchStart, this);
27268 this.bodyEl.on('touchmove', this.onTouchMove, this);
27269 this.bodyEl.on('touchend', this.onTouchEnd, this);
27273 this.bodyEl.on('mousedown', this.onMouseDown, this);
27274 this.bodyEl.on('mousemove', this.onMouseMove, this);
27275 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27276 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27277 Roo.get(document).on('mouseup', this.onMouseUp, this);
27280 this.selectorEl.on('change', this.onFileSelected, this);
27286 this.baseScale = 1;
27288 this.baseRotate = 1;
27289 this.dragable = false;
27290 this.pinching = false;
27293 this.cropData = false;
27294 this.notifyEl.dom.innerHTML = this.emptyText;
27296 this.selectorEl.dom.value = '';
27300 resize : function()
27302 if(this.fireEvent('resize', this) != false){
27303 this.setThumbBoxPosition();
27304 this.setCanvasPosition();
27308 onFooterButtonClick : function(e, el, o, type)
27311 case 'rotate-left' :
27312 this.onRotateLeft(e);
27314 case 'rotate-right' :
27315 this.onRotateRight(e);
27318 this.beforeSelectFile(e);
27333 this.fireEvent('footerbuttonclick', this, type);
27336 beforeSelectFile : function(e)
27338 e.preventDefault();
27340 if(this.fireEvent('beforeselectfile', this) != false){
27341 this.selectorEl.dom.click();
27345 onFileSelected : function(e)
27347 e.preventDefault();
27349 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27353 var file = this.selectorEl.dom.files[0];
27355 if(this.fireEvent('inspect', this, file) != false){
27356 this.prepare(file);
27361 trash : function(e)
27363 this.fireEvent('trash', this);
27366 download : function(e)
27368 this.fireEvent('download', this);
27371 loadCanvas : function(src)
27373 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27377 this.imageEl = document.createElement('img');
27381 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27383 this.imageEl.src = src;
27387 onLoadCanvas : function()
27389 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27390 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27392 this.bodyEl.un('click', this.beforeSelectFile, this);
27394 this.notifyEl.hide();
27395 this.thumbEl.show();
27396 this.footerEl.show();
27398 this.baseRotateLevel();
27400 if(this.isDocument){
27401 this.setThumbBoxSize();
27404 this.setThumbBoxPosition();
27406 this.baseScaleLevel();
27412 this.canvasLoaded = true;
27415 this.maskEl.unmask();
27420 setCanvasPosition : function()
27422 if(!this.canvasEl){
27426 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27427 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27429 this.previewEl.setLeft(pw);
27430 this.previewEl.setTop(ph);
27434 onMouseDown : function(e)
27438 this.dragable = true;
27439 this.pinching = false;
27441 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27442 this.dragable = false;
27446 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27447 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27451 onMouseMove : function(e)
27455 if(!this.canvasLoaded){
27459 if (!this.dragable){
27463 var minX = Math.ceil(this.thumbEl.getLeft(true));
27464 var minY = Math.ceil(this.thumbEl.getTop(true));
27466 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27467 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27469 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27470 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27472 x = x - this.mouseX;
27473 y = y - this.mouseY;
27475 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27476 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27478 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27479 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27481 this.previewEl.setLeft(bgX);
27482 this.previewEl.setTop(bgY);
27484 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27485 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27488 onMouseUp : function(e)
27492 this.dragable = false;
27495 onMouseWheel : function(e)
27499 this.startScale = this.scale;
27501 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27503 if(!this.zoomable()){
27504 this.scale = this.startScale;
27513 zoomable : function()
27515 var minScale = this.thumbEl.getWidth() / this.minWidth;
27517 if(this.minWidth < this.minHeight){
27518 minScale = this.thumbEl.getHeight() / this.minHeight;
27521 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27522 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27526 (this.rotate == 0 || this.rotate == 180) &&
27528 width > this.imageEl.OriginWidth ||
27529 height > this.imageEl.OriginHeight ||
27530 (width < this.minWidth && height < this.minHeight)
27538 (this.rotate == 90 || this.rotate == 270) &&
27540 width > this.imageEl.OriginWidth ||
27541 height > this.imageEl.OriginHeight ||
27542 (width < this.minHeight && height < this.minWidth)
27549 !this.isDocument &&
27550 (this.rotate == 0 || this.rotate == 180) &&
27552 width < this.minWidth ||
27553 width > this.imageEl.OriginWidth ||
27554 height < this.minHeight ||
27555 height > this.imageEl.OriginHeight
27562 !this.isDocument &&
27563 (this.rotate == 90 || this.rotate == 270) &&
27565 width < this.minHeight ||
27566 width > this.imageEl.OriginWidth ||
27567 height < this.minWidth ||
27568 height > this.imageEl.OriginHeight
27578 onRotateLeft : function(e)
27580 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27582 var minScale = this.thumbEl.getWidth() / this.minWidth;
27584 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27585 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27587 this.startScale = this.scale;
27589 while (this.getScaleLevel() < minScale){
27591 this.scale = this.scale + 1;
27593 if(!this.zoomable()){
27598 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27599 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27604 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27611 this.scale = this.startScale;
27613 this.onRotateFail();
27618 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27620 if(this.isDocument){
27621 this.setThumbBoxSize();
27622 this.setThumbBoxPosition();
27623 this.setCanvasPosition();
27628 this.fireEvent('rotate', this, 'left');
27632 onRotateRight : function(e)
27634 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27636 var minScale = this.thumbEl.getWidth() / this.minWidth;
27638 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27639 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27641 this.startScale = this.scale;
27643 while (this.getScaleLevel() < minScale){
27645 this.scale = this.scale + 1;
27647 if(!this.zoomable()){
27652 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27653 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27658 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27665 this.scale = this.startScale;
27667 this.onRotateFail();
27672 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27674 if(this.isDocument){
27675 this.setThumbBoxSize();
27676 this.setThumbBoxPosition();
27677 this.setCanvasPosition();
27682 this.fireEvent('rotate', this, 'right');
27685 onRotateFail : function()
27687 this.errorEl.show(true);
27691 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27696 this.previewEl.dom.innerHTML = '';
27698 var canvasEl = document.createElement("canvas");
27700 var contextEl = canvasEl.getContext("2d");
27702 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27703 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27704 var center = this.imageEl.OriginWidth / 2;
27706 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27707 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27708 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27709 center = this.imageEl.OriginHeight / 2;
27712 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27714 contextEl.translate(center, center);
27715 contextEl.rotate(this.rotate * Math.PI / 180);
27717 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27719 this.canvasEl = document.createElement("canvas");
27721 this.contextEl = this.canvasEl.getContext("2d");
27723 switch (this.rotate) {
27726 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27727 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27729 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27734 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27735 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27737 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27738 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);
27742 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27747 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27748 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27750 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27751 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);
27755 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);
27760 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27761 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27763 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27764 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27768 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);
27775 this.previewEl.appendChild(this.canvasEl);
27777 this.setCanvasPosition();
27782 if(!this.canvasLoaded){
27786 var imageCanvas = document.createElement("canvas");
27788 var imageContext = imageCanvas.getContext("2d");
27790 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27791 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27793 var center = imageCanvas.width / 2;
27795 imageContext.translate(center, center);
27797 imageContext.rotate(this.rotate * Math.PI / 180);
27799 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27801 var canvas = document.createElement("canvas");
27803 var context = canvas.getContext("2d");
27805 canvas.width = this.minWidth;
27806 canvas.height = this.minHeight;
27808 switch (this.rotate) {
27811 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27812 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27814 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27815 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27817 var targetWidth = this.minWidth - 2 * x;
27818 var targetHeight = this.minHeight - 2 * y;
27822 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27823 scale = targetWidth / width;
27826 if(x > 0 && y == 0){
27827 scale = targetHeight / height;
27830 if(x > 0 && y > 0){
27831 scale = targetWidth / width;
27833 if(width < height){
27834 scale = targetHeight / height;
27838 context.scale(scale, scale);
27840 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27841 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27843 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27844 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27846 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27851 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27852 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27854 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27855 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27857 var targetWidth = this.minWidth - 2 * x;
27858 var targetHeight = this.minHeight - 2 * y;
27862 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27863 scale = targetWidth / width;
27866 if(x > 0 && y == 0){
27867 scale = targetHeight / height;
27870 if(x > 0 && y > 0){
27871 scale = targetWidth / width;
27873 if(width < height){
27874 scale = targetHeight / height;
27878 context.scale(scale, scale);
27880 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27881 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27883 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27884 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27886 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27888 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27893 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27894 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27896 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27897 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27899 var targetWidth = this.minWidth - 2 * x;
27900 var targetHeight = this.minHeight - 2 * y;
27904 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27905 scale = targetWidth / width;
27908 if(x > 0 && y == 0){
27909 scale = targetHeight / height;
27912 if(x > 0 && y > 0){
27913 scale = targetWidth / width;
27915 if(width < height){
27916 scale = targetHeight / height;
27920 context.scale(scale, scale);
27922 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27923 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27925 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27926 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27928 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27929 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27931 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27936 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27937 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27939 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27940 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27942 var targetWidth = this.minWidth - 2 * x;
27943 var targetHeight = this.minHeight - 2 * y;
27947 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27948 scale = targetWidth / width;
27951 if(x > 0 && y == 0){
27952 scale = targetHeight / height;
27955 if(x > 0 && y > 0){
27956 scale = targetWidth / width;
27958 if(width < height){
27959 scale = targetHeight / height;
27963 context.scale(scale, scale);
27965 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27966 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27968 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27969 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27971 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27973 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27980 this.cropData = canvas.toDataURL(this.cropType);
27982 if(this.fireEvent('crop', this, this.cropData) !== false){
27983 this.process(this.file, this.cropData);
27990 setThumbBoxSize : function()
27994 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27995 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27996 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27998 this.minWidth = width;
27999 this.minHeight = height;
28001 if(this.rotate == 90 || this.rotate == 270){
28002 this.minWidth = height;
28003 this.minHeight = width;
28008 width = Math.ceil(this.minWidth * height / this.minHeight);
28010 if(this.minWidth > this.minHeight){
28012 height = Math.ceil(this.minHeight * width / this.minWidth);
28015 this.thumbEl.setStyle({
28016 width : width + 'px',
28017 height : height + 'px'
28024 setThumbBoxPosition : function()
28026 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28027 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28029 this.thumbEl.setLeft(x);
28030 this.thumbEl.setTop(y);
28034 baseRotateLevel : function()
28036 this.baseRotate = 1;
28039 typeof(this.exif) != 'undefined' &&
28040 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28041 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28043 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28046 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28050 baseScaleLevel : function()
28054 if(this.isDocument){
28056 if(this.baseRotate == 6 || this.baseRotate == 8){
28058 height = this.thumbEl.getHeight();
28059 this.baseScale = height / this.imageEl.OriginWidth;
28061 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28062 width = this.thumbEl.getWidth();
28063 this.baseScale = width / this.imageEl.OriginHeight;
28069 height = this.thumbEl.getHeight();
28070 this.baseScale = height / this.imageEl.OriginHeight;
28072 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28073 width = this.thumbEl.getWidth();
28074 this.baseScale = width / this.imageEl.OriginWidth;
28080 if(this.baseRotate == 6 || this.baseRotate == 8){
28082 width = this.thumbEl.getHeight();
28083 this.baseScale = width / this.imageEl.OriginHeight;
28085 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28086 height = this.thumbEl.getWidth();
28087 this.baseScale = height / this.imageEl.OriginHeight;
28090 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28091 height = this.thumbEl.getWidth();
28092 this.baseScale = height / this.imageEl.OriginHeight;
28094 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28095 width = this.thumbEl.getHeight();
28096 this.baseScale = width / this.imageEl.OriginWidth;
28103 width = this.thumbEl.getWidth();
28104 this.baseScale = width / this.imageEl.OriginWidth;
28106 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28107 height = this.thumbEl.getHeight();
28108 this.baseScale = height / this.imageEl.OriginHeight;
28111 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28113 height = this.thumbEl.getHeight();
28114 this.baseScale = height / this.imageEl.OriginHeight;
28116 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28117 width = this.thumbEl.getWidth();
28118 this.baseScale = width / this.imageEl.OriginWidth;
28126 getScaleLevel : function()
28128 return this.baseScale * Math.pow(1.1, this.scale);
28131 onTouchStart : function(e)
28133 if(!this.canvasLoaded){
28134 this.beforeSelectFile(e);
28138 var touches = e.browserEvent.touches;
28144 if(touches.length == 1){
28145 this.onMouseDown(e);
28149 if(touches.length != 2){
28155 for(var i = 0, finger; finger = touches[i]; i++){
28156 coords.push(finger.pageX, finger.pageY);
28159 var x = Math.pow(coords[0] - coords[2], 2);
28160 var y = Math.pow(coords[1] - coords[3], 2);
28162 this.startDistance = Math.sqrt(x + y);
28164 this.startScale = this.scale;
28166 this.pinching = true;
28167 this.dragable = false;
28171 onTouchMove : function(e)
28173 if(!this.pinching && !this.dragable){
28177 var touches = e.browserEvent.touches;
28184 this.onMouseMove(e);
28190 for(var i = 0, finger; finger = touches[i]; i++){
28191 coords.push(finger.pageX, finger.pageY);
28194 var x = Math.pow(coords[0] - coords[2], 2);
28195 var y = Math.pow(coords[1] - coords[3], 2);
28197 this.endDistance = Math.sqrt(x + y);
28199 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28201 if(!this.zoomable()){
28202 this.scale = this.startScale;
28210 onTouchEnd : function(e)
28212 this.pinching = false;
28213 this.dragable = false;
28217 process : function(file, crop)
28220 this.maskEl.mask(this.loadingText);
28223 this.xhr = new XMLHttpRequest();
28225 file.xhr = this.xhr;
28227 this.xhr.open(this.method, this.url, true);
28230 "Accept": "application/json",
28231 "Cache-Control": "no-cache",
28232 "X-Requested-With": "XMLHttpRequest"
28235 for (var headerName in headers) {
28236 var headerValue = headers[headerName];
28238 this.xhr.setRequestHeader(headerName, headerValue);
28244 this.xhr.onload = function()
28246 _this.xhrOnLoad(_this.xhr);
28249 this.xhr.onerror = function()
28251 _this.xhrOnError(_this.xhr);
28254 var formData = new FormData();
28256 formData.append('returnHTML', 'NO');
28259 formData.append('crop', crop);
28262 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28263 formData.append(this.paramName, file, file.name);
28266 if(typeof(file.filename) != 'undefined'){
28267 formData.append('filename', file.filename);
28270 if(typeof(file.mimetype) != 'undefined'){
28271 formData.append('mimetype', file.mimetype);
28274 if(this.fireEvent('arrange', this, formData) != false){
28275 this.xhr.send(formData);
28279 xhrOnLoad : function(xhr)
28282 this.maskEl.unmask();
28285 if (xhr.readyState !== 4) {
28286 this.fireEvent('exception', this, xhr);
28290 var response = Roo.decode(xhr.responseText);
28292 if(!response.success){
28293 this.fireEvent('exception', this, xhr);
28297 var response = Roo.decode(xhr.responseText);
28299 this.fireEvent('upload', this, response);
28303 xhrOnError : function()
28306 this.maskEl.unmask();
28309 Roo.log('xhr on error');
28311 var response = Roo.decode(xhr.responseText);
28317 prepare : function(file)
28320 this.maskEl.mask(this.loadingText);
28326 if(typeof(file) === 'string'){
28327 this.loadCanvas(file);
28331 if(!file || !this.urlAPI){
28336 this.cropType = file.type;
28340 if(this.fireEvent('prepare', this, this.file) != false){
28342 var reader = new FileReader();
28344 reader.onload = function (e) {
28345 if (e.target.error) {
28346 Roo.log(e.target.error);
28350 var buffer = e.target.result,
28351 dataView = new DataView(buffer),
28353 maxOffset = dataView.byteLength - 4,
28357 if (dataView.getUint16(0) === 0xffd8) {
28358 while (offset < maxOffset) {
28359 markerBytes = dataView.getUint16(offset);
28361 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28362 markerLength = dataView.getUint16(offset + 2) + 2;
28363 if (offset + markerLength > dataView.byteLength) {
28364 Roo.log('Invalid meta data: Invalid segment size.');
28368 if(markerBytes == 0xffe1){
28369 _this.parseExifData(
28376 offset += markerLength;
28386 var url = _this.urlAPI.createObjectURL(_this.file);
28388 _this.loadCanvas(url);
28393 reader.readAsArrayBuffer(this.file);
28399 parseExifData : function(dataView, offset, length)
28401 var tiffOffset = offset + 10,
28405 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28406 // No Exif data, might be XMP data instead
28410 // Check for the ASCII code for "Exif" (0x45786966):
28411 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28412 // No Exif data, might be XMP data instead
28415 if (tiffOffset + 8 > dataView.byteLength) {
28416 Roo.log('Invalid Exif data: Invalid segment size.');
28419 // Check for the two null bytes:
28420 if (dataView.getUint16(offset + 8) !== 0x0000) {
28421 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28424 // Check the byte alignment:
28425 switch (dataView.getUint16(tiffOffset)) {
28427 littleEndian = true;
28430 littleEndian = false;
28433 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28436 // Check for the TIFF tag marker (0x002A):
28437 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28438 Roo.log('Invalid Exif data: Missing TIFF marker.');
28441 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28442 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28444 this.parseExifTags(
28447 tiffOffset + dirOffset,
28452 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28457 if (dirOffset + 6 > dataView.byteLength) {
28458 Roo.log('Invalid Exif data: Invalid directory offset.');
28461 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28462 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28463 if (dirEndOffset + 4 > dataView.byteLength) {
28464 Roo.log('Invalid Exif data: Invalid directory size.');
28467 for (i = 0; i < tagsNumber; i += 1) {
28471 dirOffset + 2 + 12 * i, // tag offset
28475 // Return the offset to the next directory:
28476 return dataView.getUint32(dirEndOffset, littleEndian);
28479 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28481 var tag = dataView.getUint16(offset, littleEndian);
28483 this.exif[tag] = this.getExifValue(
28487 dataView.getUint16(offset + 2, littleEndian), // tag type
28488 dataView.getUint32(offset + 4, littleEndian), // tag length
28493 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28495 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28504 Roo.log('Invalid Exif data: Invalid tag type.');
28508 tagSize = tagType.size * length;
28509 // Determine if the value is contained in the dataOffset bytes,
28510 // or if the value at the dataOffset is a pointer to the actual data:
28511 dataOffset = tagSize > 4 ?
28512 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28513 if (dataOffset + tagSize > dataView.byteLength) {
28514 Roo.log('Invalid Exif data: Invalid data offset.');
28517 if (length === 1) {
28518 return tagType.getValue(dataView, dataOffset, littleEndian);
28521 for (i = 0; i < length; i += 1) {
28522 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28525 if (tagType.ascii) {
28527 // Concatenate the chars:
28528 for (i = 0; i < values.length; i += 1) {
28530 // Ignore the terminating NULL byte(s):
28531 if (c === '\u0000') {
28543 Roo.apply(Roo.bootstrap.UploadCropbox, {
28545 'Orientation': 0x0112
28549 1: 0, //'top-left',
28551 3: 180, //'bottom-right',
28552 // 4: 'bottom-left',
28554 6: 90, //'right-top',
28555 // 7: 'right-bottom',
28556 8: 270 //'left-bottom'
28560 // byte, 8-bit unsigned int:
28562 getValue: function (dataView, dataOffset) {
28563 return dataView.getUint8(dataOffset);
28567 // ascii, 8-bit byte:
28569 getValue: function (dataView, dataOffset) {
28570 return String.fromCharCode(dataView.getUint8(dataOffset));
28575 // short, 16 bit int:
28577 getValue: function (dataView, dataOffset, littleEndian) {
28578 return dataView.getUint16(dataOffset, littleEndian);
28582 // long, 32 bit int:
28584 getValue: function (dataView, dataOffset, littleEndian) {
28585 return dataView.getUint32(dataOffset, littleEndian);
28589 // rational = two long values, first is numerator, second is denominator:
28591 getValue: function (dataView, dataOffset, littleEndian) {
28592 return dataView.getUint32(dataOffset, littleEndian) /
28593 dataView.getUint32(dataOffset + 4, littleEndian);
28597 // slong, 32 bit signed int:
28599 getValue: function (dataView, dataOffset, littleEndian) {
28600 return dataView.getInt32(dataOffset, littleEndian);
28604 // srational, two slongs, first is numerator, second is denominator:
28606 getValue: function (dataView, dataOffset, littleEndian) {
28607 return dataView.getInt32(dataOffset, littleEndian) /
28608 dataView.getInt32(dataOffset + 4, littleEndian);
28618 cls : 'btn-group roo-upload-cropbox-rotate-left',
28619 action : 'rotate-left',
28623 cls : 'btn btn-default',
28624 html : '<i class="fa fa-undo"></i>'
28630 cls : 'btn-group roo-upload-cropbox-picture',
28631 action : 'picture',
28635 cls : 'btn btn-default',
28636 html : '<i class="fa fa-picture-o"></i>'
28642 cls : 'btn-group roo-upload-cropbox-rotate-right',
28643 action : 'rotate-right',
28647 cls : 'btn btn-default',
28648 html : '<i class="fa fa-repeat"></i>'
28656 cls : 'btn-group roo-upload-cropbox-rotate-left',
28657 action : 'rotate-left',
28661 cls : 'btn btn-default',
28662 html : '<i class="fa fa-undo"></i>'
28668 cls : 'btn-group roo-upload-cropbox-download',
28669 action : 'download',
28673 cls : 'btn btn-default',
28674 html : '<i class="fa fa-download"></i>'
28680 cls : 'btn-group roo-upload-cropbox-crop',
28685 cls : 'btn btn-default',
28686 html : '<i class="fa fa-crop"></i>'
28692 cls : 'btn-group roo-upload-cropbox-trash',
28697 cls : 'btn btn-default',
28698 html : '<i class="fa fa-trash"></i>'
28704 cls : 'btn-group roo-upload-cropbox-rotate-right',
28705 action : 'rotate-right',
28709 cls : 'btn btn-default',
28710 html : '<i class="fa fa-repeat"></i>'
28718 cls : 'btn-group roo-upload-cropbox-rotate-left',
28719 action : 'rotate-left',
28723 cls : 'btn btn-default',
28724 html : '<i class="fa fa-undo"></i>'
28730 cls : 'btn-group roo-upload-cropbox-rotate-right',
28731 action : 'rotate-right',
28735 cls : 'btn btn-default',
28736 html : '<i class="fa fa-repeat"></i>'
28749 * @class Roo.bootstrap.DocumentManager
28750 * @extends Roo.bootstrap.Component
28751 * Bootstrap DocumentManager class
28752 * @cfg {String} paramName default 'imageUpload'
28753 * @cfg {String} toolTipName default 'filename'
28754 * @cfg {String} method default POST
28755 * @cfg {String} url action url
28756 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28757 * @cfg {Boolean} multiple multiple upload default true
28758 * @cfg {Number} thumbSize default 300
28759 * @cfg {String} fieldLabel
28760 * @cfg {Number} labelWidth default 4
28761 * @cfg {String} labelAlign (left|top) default left
28762 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28763 * @cfg {Number} labellg set the width of label (1-12)
28764 * @cfg {Number} labelmd set the width of label (1-12)
28765 * @cfg {Number} labelsm set the width of label (1-12)
28766 * @cfg {Number} labelxs set the width of label (1-12)
28769 * Create a new DocumentManager
28770 * @param {Object} config The config object
28773 Roo.bootstrap.DocumentManager = function(config){
28774 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28777 this.delegates = [];
28782 * Fire when initial the DocumentManager
28783 * @param {Roo.bootstrap.DocumentManager} this
28788 * inspect selected file
28789 * @param {Roo.bootstrap.DocumentManager} this
28790 * @param {File} file
28795 * Fire when xhr load exception
28796 * @param {Roo.bootstrap.DocumentManager} this
28797 * @param {XMLHttpRequest} xhr
28799 "exception" : true,
28801 * @event afterupload
28802 * Fire when xhr load exception
28803 * @param {Roo.bootstrap.DocumentManager} this
28804 * @param {XMLHttpRequest} xhr
28806 "afterupload" : true,
28809 * prepare the form data
28810 * @param {Roo.bootstrap.DocumentManager} this
28811 * @param {Object} formData
28816 * Fire when remove the file
28817 * @param {Roo.bootstrap.DocumentManager} this
28818 * @param {Object} file
28823 * Fire after refresh the file
28824 * @param {Roo.bootstrap.DocumentManager} this
28829 * Fire after click the image
28830 * @param {Roo.bootstrap.DocumentManager} this
28831 * @param {Object} file
28836 * Fire when upload a image and editable set to true
28837 * @param {Roo.bootstrap.DocumentManager} this
28838 * @param {Object} file
28842 * @event beforeselectfile
28843 * Fire before select file
28844 * @param {Roo.bootstrap.DocumentManager} this
28846 "beforeselectfile" : true,
28849 * Fire before process file
28850 * @param {Roo.bootstrap.DocumentManager} this
28851 * @param {Object} file
28855 * @event previewrendered
28856 * Fire when preview rendered
28857 * @param {Roo.bootstrap.DocumentManager} this
28858 * @param {Object} file
28860 "previewrendered" : true,
28863 "previewResize" : true
28868 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28877 paramName : 'imageUpload',
28878 toolTipName : 'filename',
28881 labelAlign : 'left',
28891 getAutoCreate : function()
28893 var managerWidget = {
28895 cls : 'roo-document-manager',
28899 cls : 'roo-document-manager-selector',
28904 cls : 'roo-document-manager-uploader',
28908 cls : 'roo-document-manager-upload-btn',
28909 html : '<i class="fa fa-plus"></i>'
28920 cls : 'column col-md-12',
28925 if(this.fieldLabel.length){
28930 cls : 'column col-md-12',
28931 html : this.fieldLabel
28935 cls : 'column col-md-12',
28940 if(this.labelAlign == 'left'){
28945 html : this.fieldLabel
28954 if(this.labelWidth > 12){
28955 content[0].style = "width: " + this.labelWidth + 'px';
28958 if(this.labelWidth < 13 && this.labelmd == 0){
28959 this.labelmd = this.labelWidth;
28962 if(this.labellg > 0){
28963 content[0].cls += ' col-lg-' + this.labellg;
28964 content[1].cls += ' col-lg-' + (12 - this.labellg);
28967 if(this.labelmd > 0){
28968 content[0].cls += ' col-md-' + this.labelmd;
28969 content[1].cls += ' col-md-' + (12 - this.labelmd);
28972 if(this.labelsm > 0){
28973 content[0].cls += ' col-sm-' + this.labelsm;
28974 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28977 if(this.labelxs > 0){
28978 content[0].cls += ' col-xs-' + this.labelxs;
28979 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28987 cls : 'row clearfix',
28995 initEvents : function()
28997 this.managerEl = this.el.select('.roo-document-manager', true).first();
28998 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29000 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29001 this.selectorEl.hide();
29004 this.selectorEl.attr('multiple', 'multiple');
29007 this.selectorEl.on('change', this.onFileSelected, this);
29009 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29010 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29012 this.uploader.on('click', this.onUploaderClick, this);
29014 this.renderProgressDialog();
29018 window.addEventListener("resize", function() { _this.refresh(); } );
29020 this.fireEvent('initial', this);
29023 renderProgressDialog : function()
29027 this.progressDialog = new Roo.bootstrap.Modal({
29028 cls : 'roo-document-manager-progress-dialog',
29029 allow_close : false,
29039 btnclick : function() {
29040 _this.uploadCancel();
29046 this.progressDialog.render(Roo.get(document.body));
29048 this.progress = new Roo.bootstrap.Progress({
29049 cls : 'roo-document-manager-progress',
29054 this.progress.render(this.progressDialog.getChildContainer());
29056 this.progressBar = new Roo.bootstrap.ProgressBar({
29057 cls : 'roo-document-manager-progress-bar',
29060 aria_valuemax : 12,
29064 this.progressBar.render(this.progress.getChildContainer());
29067 onUploaderClick : function(e)
29069 e.preventDefault();
29071 if(this.fireEvent('beforeselectfile', this) != false){
29072 this.selectorEl.dom.click();
29077 onFileSelected : function(e)
29079 e.preventDefault();
29081 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29085 Roo.each(this.selectorEl.dom.files, function(file){
29086 if(this.fireEvent('inspect', this, file) != false){
29087 this.files.push(file);
29097 this.selectorEl.dom.value = '';
29099 if(!this.files || !this.files.length){
29103 if(this.boxes > 0 && this.files.length > this.boxes){
29104 this.files = this.files.slice(0, this.boxes);
29107 this.uploader.show();
29109 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29110 this.uploader.hide();
29119 Roo.each(this.files, function(file){
29121 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29122 var f = this.renderPreview(file);
29127 if(file.type.indexOf('image') != -1){
29128 this.delegates.push(
29130 _this.process(file);
29131 }).createDelegate(this)
29139 _this.process(file);
29140 }).createDelegate(this)
29145 this.files = files;
29147 this.delegates = this.delegates.concat(docs);
29149 if(!this.delegates.length){
29154 this.progressBar.aria_valuemax = this.delegates.length;
29161 arrange : function()
29163 if(!this.delegates.length){
29164 this.progressDialog.hide();
29169 var delegate = this.delegates.shift();
29171 this.progressDialog.show();
29173 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29175 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29180 refresh : function()
29182 this.uploader.show();
29184 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29185 this.uploader.hide();
29188 Roo.isTouch ? this.closable(false) : this.closable(true);
29190 this.fireEvent('refresh', this);
29193 onRemove : function(e, el, o)
29195 e.preventDefault();
29197 this.fireEvent('remove', this, o);
29201 remove : function(o)
29205 Roo.each(this.files, function(file){
29206 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29215 this.files = files;
29222 Roo.each(this.files, function(file){
29227 file.target.remove();
29236 onClick : function(e, el, o)
29238 e.preventDefault();
29240 this.fireEvent('click', this, o);
29244 closable : function(closable)
29246 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29248 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29260 xhrOnLoad : function(xhr)
29262 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29266 if (xhr.readyState !== 4) {
29268 this.fireEvent('exception', this, xhr);
29272 var response = Roo.decode(xhr.responseText);
29274 if(!response.success){
29276 this.fireEvent('exception', this, xhr);
29280 var file = this.renderPreview(response.data);
29282 this.files.push(file);
29286 this.fireEvent('afterupload', this, xhr);
29290 xhrOnError : function(xhr)
29292 Roo.log('xhr on error');
29294 var response = Roo.decode(xhr.responseText);
29301 process : function(file)
29303 if(this.fireEvent('process', this, file) !== false){
29304 if(this.editable && file.type.indexOf('image') != -1){
29305 this.fireEvent('edit', this, file);
29309 this.uploadStart(file, false);
29316 uploadStart : function(file, crop)
29318 this.xhr = new XMLHttpRequest();
29320 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29325 file.xhr = this.xhr;
29327 this.managerEl.createChild({
29329 cls : 'roo-document-manager-loading',
29333 tooltip : file.name,
29334 cls : 'roo-document-manager-thumb',
29335 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29341 this.xhr.open(this.method, this.url, true);
29344 "Accept": "application/json",
29345 "Cache-Control": "no-cache",
29346 "X-Requested-With": "XMLHttpRequest"
29349 for (var headerName in headers) {
29350 var headerValue = headers[headerName];
29352 this.xhr.setRequestHeader(headerName, headerValue);
29358 this.xhr.onload = function()
29360 _this.xhrOnLoad(_this.xhr);
29363 this.xhr.onerror = function()
29365 _this.xhrOnError(_this.xhr);
29368 var formData = new FormData();
29370 formData.append('returnHTML', 'NO');
29373 formData.append('crop', crop);
29376 formData.append(this.paramName, file, file.name);
29383 if(this.fireEvent('prepare', this, formData, options) != false){
29385 if(options.manually){
29389 this.xhr.send(formData);
29393 this.uploadCancel();
29396 uploadCancel : function()
29402 this.delegates = [];
29404 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29411 renderPreview : function(file)
29413 if(typeof(file.target) != 'undefined' && file.target){
29417 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29419 var previewEl = this.managerEl.createChild({
29421 cls : 'roo-document-manager-preview',
29425 tooltip : file[this.toolTipName],
29426 cls : 'roo-document-manager-thumb',
29427 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29432 html : '<i class="fa fa-times-circle"></i>'
29437 var close = previewEl.select('button.close', true).first();
29439 close.on('click', this.onRemove, this, file);
29441 file.target = previewEl;
29443 var image = previewEl.select('img', true).first();
29447 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29449 image.on('click', this.onClick, this, file);
29451 this.fireEvent('previewrendered', this, file);
29457 onPreviewLoad : function(file, image)
29459 if(typeof(file.target) == 'undefined' || !file.target){
29463 var width = image.dom.naturalWidth || image.dom.width;
29464 var height = image.dom.naturalHeight || image.dom.height;
29466 if(!this.previewResize) {
29470 if(width > height){
29471 file.target.addClass('wide');
29475 file.target.addClass('tall');
29480 uploadFromSource : function(file, crop)
29482 this.xhr = new XMLHttpRequest();
29484 this.managerEl.createChild({
29486 cls : 'roo-document-manager-loading',
29490 tooltip : file.name,
29491 cls : 'roo-document-manager-thumb',
29492 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29498 this.xhr.open(this.method, this.url, true);
29501 "Accept": "application/json",
29502 "Cache-Control": "no-cache",
29503 "X-Requested-With": "XMLHttpRequest"
29506 for (var headerName in headers) {
29507 var headerValue = headers[headerName];
29509 this.xhr.setRequestHeader(headerName, headerValue);
29515 this.xhr.onload = function()
29517 _this.xhrOnLoad(_this.xhr);
29520 this.xhr.onerror = function()
29522 _this.xhrOnError(_this.xhr);
29525 var formData = new FormData();
29527 formData.append('returnHTML', 'NO');
29529 formData.append('crop', crop);
29531 if(typeof(file.filename) != 'undefined'){
29532 formData.append('filename', file.filename);
29535 if(typeof(file.mimetype) != 'undefined'){
29536 formData.append('mimetype', file.mimetype);
29541 if(this.fireEvent('prepare', this, formData) != false){
29542 this.xhr.send(formData);
29552 * @class Roo.bootstrap.DocumentViewer
29553 * @extends Roo.bootstrap.Component
29554 * Bootstrap DocumentViewer class
29555 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29556 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29559 * Create a new DocumentViewer
29560 * @param {Object} config The config object
29563 Roo.bootstrap.DocumentViewer = function(config){
29564 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29569 * Fire after initEvent
29570 * @param {Roo.bootstrap.DocumentViewer} this
29576 * @param {Roo.bootstrap.DocumentViewer} this
29581 * Fire after download button
29582 * @param {Roo.bootstrap.DocumentViewer} this
29587 * Fire after trash button
29588 * @param {Roo.bootstrap.DocumentViewer} this
29595 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29597 showDownload : true,
29601 getAutoCreate : function()
29605 cls : 'roo-document-viewer',
29609 cls : 'roo-document-viewer-body',
29613 cls : 'roo-document-viewer-thumb',
29617 cls : 'roo-document-viewer-image'
29625 cls : 'roo-document-viewer-footer',
29628 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29632 cls : 'btn-group roo-document-viewer-download',
29636 cls : 'btn btn-default',
29637 html : '<i class="fa fa-download"></i>'
29643 cls : 'btn-group roo-document-viewer-trash',
29647 cls : 'btn btn-default',
29648 html : '<i class="fa fa-trash"></i>'
29661 initEvents : function()
29663 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29664 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29666 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29667 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29669 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29670 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29672 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29673 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29675 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29676 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29678 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29679 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29681 this.bodyEl.on('click', this.onClick, this);
29682 this.downloadBtn.on('click', this.onDownload, this);
29683 this.trashBtn.on('click', this.onTrash, this);
29685 this.downloadBtn.hide();
29686 this.trashBtn.hide();
29688 if(this.showDownload){
29689 this.downloadBtn.show();
29692 if(this.showTrash){
29693 this.trashBtn.show();
29696 if(!this.showDownload && !this.showTrash) {
29697 this.footerEl.hide();
29702 initial : function()
29704 this.fireEvent('initial', this);
29708 onClick : function(e)
29710 e.preventDefault();
29712 this.fireEvent('click', this);
29715 onDownload : function(e)
29717 e.preventDefault();
29719 this.fireEvent('download', this);
29722 onTrash : function(e)
29724 e.preventDefault();
29726 this.fireEvent('trash', this);
29738 * @class Roo.bootstrap.NavProgressBar
29739 * @extends Roo.bootstrap.Component
29740 * Bootstrap NavProgressBar class
29743 * Create a new nav progress bar
29744 * @param {Object} config The config object
29747 Roo.bootstrap.NavProgressBar = function(config){
29748 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29750 this.bullets = this.bullets || [];
29752 // Roo.bootstrap.NavProgressBar.register(this);
29756 * Fires when the active item changes
29757 * @param {Roo.bootstrap.NavProgressBar} this
29758 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29759 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29766 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29771 getAutoCreate : function()
29773 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29777 cls : 'roo-navigation-bar-group',
29781 cls : 'roo-navigation-top-bar'
29785 cls : 'roo-navigation-bullets-bar',
29789 cls : 'roo-navigation-bar'
29796 cls : 'roo-navigation-bottom-bar'
29806 initEvents: function()
29811 onRender : function(ct, position)
29813 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29815 if(this.bullets.length){
29816 Roo.each(this.bullets, function(b){
29825 addItem : function(cfg)
29827 var item = new Roo.bootstrap.NavProgressItem(cfg);
29829 item.parentId = this.id;
29830 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29833 var top = new Roo.bootstrap.Element({
29835 cls : 'roo-navigation-bar-text'
29838 var bottom = new Roo.bootstrap.Element({
29840 cls : 'roo-navigation-bar-text'
29843 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29844 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29846 var topText = new Roo.bootstrap.Element({
29848 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29851 var bottomText = new Roo.bootstrap.Element({
29853 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29856 topText.onRender(top.el, null);
29857 bottomText.onRender(bottom.el, null);
29860 item.bottomEl = bottom;
29863 this.barItems.push(item);
29868 getActive : function()
29870 var active = false;
29872 Roo.each(this.barItems, function(v){
29874 if (!v.isActive()) {
29886 setActiveItem : function(item)
29890 Roo.each(this.barItems, function(v){
29891 if (v.rid == item.rid) {
29895 if (v.isActive()) {
29896 v.setActive(false);
29901 item.setActive(true);
29903 this.fireEvent('changed', this, item, prev);
29906 getBarItem: function(rid)
29910 Roo.each(this.barItems, function(e) {
29911 if (e.rid != rid) {
29922 indexOfItem : function(item)
29926 Roo.each(this.barItems, function(v, i){
29928 if (v.rid != item.rid) {
29939 setActiveNext : function()
29941 var i = this.indexOfItem(this.getActive());
29943 if (i > this.barItems.length) {
29947 this.setActiveItem(this.barItems[i+1]);
29950 setActivePrev : function()
29952 var i = this.indexOfItem(this.getActive());
29958 this.setActiveItem(this.barItems[i-1]);
29961 format : function()
29963 if(!this.barItems.length){
29967 var width = 100 / this.barItems.length;
29969 Roo.each(this.barItems, function(i){
29970 i.el.setStyle('width', width + '%');
29971 i.topEl.el.setStyle('width', width + '%');
29972 i.bottomEl.el.setStyle('width', width + '%');
29981 * Nav Progress Item
29986 * @class Roo.bootstrap.NavProgressItem
29987 * @extends Roo.bootstrap.Component
29988 * Bootstrap NavProgressItem class
29989 * @cfg {String} rid the reference id
29990 * @cfg {Boolean} active (true|false) Is item active default false
29991 * @cfg {Boolean} disabled (true|false) Is item active default false
29992 * @cfg {String} html
29993 * @cfg {String} position (top|bottom) text position default bottom
29994 * @cfg {String} icon show icon instead of number
29997 * Create a new NavProgressItem
29998 * @param {Object} config The config object
30000 Roo.bootstrap.NavProgressItem = function(config){
30001 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30006 * The raw click event for the entire grid.
30007 * @param {Roo.bootstrap.NavProgressItem} this
30008 * @param {Roo.EventObject} e
30015 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
30021 position : 'bottom',
30024 getAutoCreate : function()
30026 var iconCls = 'roo-navigation-bar-item-icon';
30028 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30032 cls: 'roo-navigation-bar-item',
30042 cfg.cls += ' active';
30045 cfg.cls += ' disabled';
30051 disable : function()
30053 this.setDisabled(true);
30056 enable : function()
30058 this.setDisabled(false);
30061 initEvents: function()
30063 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30065 this.iconEl.on('click', this.onClick, this);
30068 onClick : function(e)
30070 e.preventDefault();
30076 if(this.fireEvent('click', this, e) === false){
30080 this.parent().setActiveItem(this);
30083 isActive: function ()
30085 return this.active;
30088 setActive : function(state)
30090 if(this.active == state){
30094 this.active = state;
30097 this.el.addClass('active');
30101 this.el.removeClass('active');
30106 setDisabled : function(state)
30108 if(this.disabled == state){
30112 this.disabled = state;
30115 this.el.addClass('disabled');
30119 this.el.removeClass('disabled');
30122 tooltipEl : function()
30124 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30137 * @class Roo.bootstrap.FieldLabel
30138 * @extends Roo.bootstrap.Component
30139 * Bootstrap FieldLabel class
30140 * @cfg {String} html contents of the element
30141 * @cfg {String} tag tag of the element default label
30142 * @cfg {String} cls class of the element
30143 * @cfg {String} target label target
30144 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30145 * @cfg {String} invalidClass default "text-warning"
30146 * @cfg {String} validClass default "text-success"
30147 * @cfg {String} iconTooltip default "This field is required"
30148 * @cfg {String} indicatorpos (left|right) default left
30151 * Create a new FieldLabel
30152 * @param {Object} config The config object
30155 Roo.bootstrap.FieldLabel = function(config){
30156 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30161 * Fires after the field has been marked as invalid.
30162 * @param {Roo.form.FieldLabel} this
30163 * @param {String} msg The validation message
30168 * Fires after the field has been validated with no errors.
30169 * @param {Roo.form.FieldLabel} this
30175 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30182 invalidClass : 'has-warning',
30183 validClass : 'has-success',
30184 iconTooltip : 'This field is required',
30185 indicatorpos : 'left',
30187 getAutoCreate : function(){
30190 if (!this.allowBlank) {
30196 cls : 'roo-bootstrap-field-label ' + this.cls,
30201 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30202 tooltip : this.iconTooltip
30211 if(this.indicatorpos == 'right'){
30214 cls : 'roo-bootstrap-field-label ' + this.cls,
30223 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30224 tooltip : this.iconTooltip
30233 initEvents: function()
30235 Roo.bootstrap.Element.superclass.initEvents.call(this);
30237 this.indicator = this.indicatorEl();
30239 if(this.indicator){
30240 this.indicator.removeClass('visible');
30241 this.indicator.addClass('invisible');
30244 Roo.bootstrap.FieldLabel.register(this);
30247 indicatorEl : function()
30249 var indicator = this.el.select('i.roo-required-indicator',true).first();
30260 * Mark this field as valid
30262 markValid : function()
30264 if(this.indicator){
30265 this.indicator.removeClass('visible');
30266 this.indicator.addClass('invisible');
30269 this.el.removeClass(this.invalidClass);
30271 this.el.addClass(this.validClass);
30273 this.fireEvent('valid', this);
30277 * Mark this field as invalid
30278 * @param {String} msg The validation message
30280 markInvalid : function(msg)
30282 if(this.indicator){
30283 this.indicator.removeClass('invisible');
30284 this.indicator.addClass('visible');
30287 this.el.removeClass(this.validClass);
30289 this.el.addClass(this.invalidClass);
30291 this.fireEvent('invalid', this, msg);
30297 Roo.apply(Roo.bootstrap.FieldLabel, {
30302 * register a FieldLabel Group
30303 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30305 register : function(label)
30307 if(this.groups.hasOwnProperty(label.target)){
30311 this.groups[label.target] = label;
30315 * fetch a FieldLabel Group based on the target
30316 * @param {string} target
30317 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30319 get: function(target) {
30320 if (typeof(this.groups[target]) == 'undefined') {
30324 return this.groups[target] ;
30333 * page DateSplitField.
30339 * @class Roo.bootstrap.DateSplitField
30340 * @extends Roo.bootstrap.Component
30341 * Bootstrap DateSplitField class
30342 * @cfg {string} fieldLabel - the label associated
30343 * @cfg {Number} labelWidth set the width of label (0-12)
30344 * @cfg {String} labelAlign (top|left)
30345 * @cfg {Boolean} dayAllowBlank (true|false) default false
30346 * @cfg {Boolean} monthAllowBlank (true|false) default false
30347 * @cfg {Boolean} yearAllowBlank (true|false) default false
30348 * @cfg {string} dayPlaceholder
30349 * @cfg {string} monthPlaceholder
30350 * @cfg {string} yearPlaceholder
30351 * @cfg {string} dayFormat default 'd'
30352 * @cfg {string} monthFormat default 'm'
30353 * @cfg {string} yearFormat default 'Y'
30354 * @cfg {Number} labellg set the width of label (1-12)
30355 * @cfg {Number} labelmd set the width of label (1-12)
30356 * @cfg {Number} labelsm set the width of label (1-12)
30357 * @cfg {Number} labelxs set the width of label (1-12)
30361 * Create a new DateSplitField
30362 * @param {Object} config The config object
30365 Roo.bootstrap.DateSplitField = function(config){
30366 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30372 * getting the data of years
30373 * @param {Roo.bootstrap.DateSplitField} this
30374 * @param {Object} years
30379 * getting the data of days
30380 * @param {Roo.bootstrap.DateSplitField} this
30381 * @param {Object} days
30386 * Fires after the field has been marked as invalid.
30387 * @param {Roo.form.Field} this
30388 * @param {String} msg The validation message
30393 * Fires after the field has been validated with no errors.
30394 * @param {Roo.form.Field} this
30400 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30403 labelAlign : 'top',
30405 dayAllowBlank : false,
30406 monthAllowBlank : false,
30407 yearAllowBlank : false,
30408 dayPlaceholder : '',
30409 monthPlaceholder : '',
30410 yearPlaceholder : '',
30414 isFormField : true,
30420 getAutoCreate : function()
30424 cls : 'row roo-date-split-field-group',
30429 cls : 'form-hidden-field roo-date-split-field-group-value',
30435 var labelCls = 'col-md-12';
30436 var contentCls = 'col-md-4';
30438 if(this.fieldLabel){
30442 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30446 html : this.fieldLabel
30451 if(this.labelAlign == 'left'){
30453 if(this.labelWidth > 12){
30454 label.style = "width: " + this.labelWidth + 'px';
30457 if(this.labelWidth < 13 && this.labelmd == 0){
30458 this.labelmd = this.labelWidth;
30461 if(this.labellg > 0){
30462 labelCls = ' col-lg-' + this.labellg;
30463 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30466 if(this.labelmd > 0){
30467 labelCls = ' col-md-' + this.labelmd;
30468 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30471 if(this.labelsm > 0){
30472 labelCls = ' col-sm-' + this.labelsm;
30473 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30476 if(this.labelxs > 0){
30477 labelCls = ' col-xs-' + this.labelxs;
30478 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30482 label.cls += ' ' + labelCls;
30484 cfg.cn.push(label);
30487 Roo.each(['day', 'month', 'year'], function(t){
30490 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30497 inputEl: function ()
30499 return this.el.select('.roo-date-split-field-group-value', true).first();
30502 onRender : function(ct, position)
30506 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30508 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30510 this.dayField = new Roo.bootstrap.ComboBox({
30511 allowBlank : this.dayAllowBlank,
30512 alwaysQuery : true,
30513 displayField : 'value',
30516 forceSelection : true,
30518 placeholder : this.dayPlaceholder,
30519 selectOnFocus : true,
30520 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30521 triggerAction : 'all',
30523 valueField : 'value',
30524 store : new Roo.data.SimpleStore({
30525 data : (function() {
30527 _this.fireEvent('days', _this, days);
30530 fields : [ 'value' ]
30533 select : function (_self, record, index)
30535 _this.setValue(_this.getValue());
30540 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30542 this.monthField = new Roo.bootstrap.MonthField({
30543 after : '<i class=\"fa fa-calendar\"></i>',
30544 allowBlank : this.monthAllowBlank,
30545 placeholder : this.monthPlaceholder,
30548 render : function (_self)
30550 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30551 e.preventDefault();
30555 select : function (_self, oldvalue, newvalue)
30557 _this.setValue(_this.getValue());
30562 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30564 this.yearField = new Roo.bootstrap.ComboBox({
30565 allowBlank : this.yearAllowBlank,
30566 alwaysQuery : true,
30567 displayField : 'value',
30570 forceSelection : true,
30572 placeholder : this.yearPlaceholder,
30573 selectOnFocus : true,
30574 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30575 triggerAction : 'all',
30577 valueField : 'value',
30578 store : new Roo.data.SimpleStore({
30579 data : (function() {
30581 _this.fireEvent('years', _this, years);
30584 fields : [ 'value' ]
30587 select : function (_self, record, index)
30589 _this.setValue(_this.getValue());
30594 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30597 setValue : function(v, format)
30599 this.inputEl.dom.value = v;
30601 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30603 var d = Date.parseDate(v, f);
30610 this.setDay(d.format(this.dayFormat));
30611 this.setMonth(d.format(this.monthFormat));
30612 this.setYear(d.format(this.yearFormat));
30619 setDay : function(v)
30621 this.dayField.setValue(v);
30622 this.inputEl.dom.value = this.getValue();
30627 setMonth : function(v)
30629 this.monthField.setValue(v, true);
30630 this.inputEl.dom.value = this.getValue();
30635 setYear : function(v)
30637 this.yearField.setValue(v);
30638 this.inputEl.dom.value = this.getValue();
30643 getDay : function()
30645 return this.dayField.getValue();
30648 getMonth : function()
30650 return this.monthField.getValue();
30653 getYear : function()
30655 return this.yearField.getValue();
30658 getValue : function()
30660 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30662 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30672 this.inputEl.dom.value = '';
30677 validate : function()
30679 var d = this.dayField.validate();
30680 var m = this.monthField.validate();
30681 var y = this.yearField.validate();
30686 (!this.dayAllowBlank && !d) ||
30687 (!this.monthAllowBlank && !m) ||
30688 (!this.yearAllowBlank && !y)
30693 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30702 this.markInvalid();
30707 markValid : function()
30710 var label = this.el.select('label', true).first();
30711 var icon = this.el.select('i.fa-star', true).first();
30717 this.fireEvent('valid', this);
30721 * Mark this field as invalid
30722 * @param {String} msg The validation message
30724 markInvalid : function(msg)
30727 var label = this.el.select('label', true).first();
30728 var icon = this.el.select('i.fa-star', true).first();
30730 if(label && !icon){
30731 this.el.select('.roo-date-split-field-label', true).createChild({
30733 cls : 'text-danger fa fa-lg fa-star',
30734 tooltip : 'This field is required',
30735 style : 'margin-right:5px;'
30739 this.fireEvent('invalid', this, msg);
30742 clearInvalid : function()
30744 var label = this.el.select('label', true).first();
30745 var icon = this.el.select('i.fa-star', true).first();
30751 this.fireEvent('valid', this);
30754 getName: function()
30764 * http://masonry.desandro.com
30766 * The idea is to render all the bricks based on vertical width...
30768 * The original code extends 'outlayer' - we might need to use that....
30774 * @class Roo.bootstrap.LayoutMasonry
30775 * @extends Roo.bootstrap.Component
30776 * Bootstrap Layout Masonry class
30779 * Create a new Element
30780 * @param {Object} config The config object
30783 Roo.bootstrap.LayoutMasonry = function(config){
30785 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30789 Roo.bootstrap.LayoutMasonry.register(this);
30795 * Fire after layout the items
30796 * @param {Roo.bootstrap.LayoutMasonry} this
30797 * @param {Roo.EventObject} e
30804 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30807 * @cfg {Boolean} isLayoutInstant = no animation?
30809 isLayoutInstant : false, // needed?
30812 * @cfg {Number} boxWidth width of the columns
30817 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30822 * @cfg {Number} padWidth padding below box..
30827 * @cfg {Number} gutter gutter width..
30832 * @cfg {Number} maxCols maximum number of columns
30838 * @cfg {Boolean} isAutoInitial defalut true
30840 isAutoInitial : true,
30845 * @cfg {Boolean} isHorizontal defalut false
30847 isHorizontal : false,
30849 currentSize : null,
30855 bricks: null, //CompositeElement
30859 _isLayoutInited : false,
30861 // isAlternative : false, // only use for vertical layout...
30864 * @cfg {Number} alternativePadWidth padding below box..
30866 alternativePadWidth : 50,
30868 selectedBrick : [],
30870 getAutoCreate : function(){
30872 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30876 cls: 'blog-masonary-wrapper ' + this.cls,
30878 cls : 'mas-boxes masonary'
30885 getChildContainer: function( )
30887 if (this.boxesEl) {
30888 return this.boxesEl;
30891 this.boxesEl = this.el.select('.mas-boxes').first();
30893 return this.boxesEl;
30897 initEvents : function()
30901 if(this.isAutoInitial){
30902 Roo.log('hook children rendered');
30903 this.on('childrenrendered', function() {
30904 Roo.log('children rendered');
30910 initial : function()
30912 this.selectedBrick = [];
30914 this.currentSize = this.el.getBox(true);
30916 Roo.EventManager.onWindowResize(this.resize, this);
30918 if(!this.isAutoInitial){
30926 //this.layout.defer(500,this);
30930 resize : function()
30932 var cs = this.el.getBox(true);
30935 this.currentSize.width == cs.width &&
30936 this.currentSize.x == cs.x &&
30937 this.currentSize.height == cs.height &&
30938 this.currentSize.y == cs.y
30940 Roo.log("no change in with or X or Y");
30944 this.currentSize = cs;
30950 layout : function()
30952 this._resetLayout();
30954 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30956 this.layoutItems( isInstant );
30958 this._isLayoutInited = true;
30960 this.fireEvent('layout', this);
30964 _resetLayout : function()
30966 if(this.isHorizontal){
30967 this.horizontalMeasureColumns();
30971 this.verticalMeasureColumns();
30975 verticalMeasureColumns : function()
30977 this.getContainerWidth();
30979 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30980 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30984 var boxWidth = this.boxWidth + this.padWidth;
30986 if(this.containerWidth < this.boxWidth){
30987 boxWidth = this.containerWidth
30990 var containerWidth = this.containerWidth;
30992 var cols = Math.floor(containerWidth / boxWidth);
30994 this.cols = Math.max( cols, 1 );
30996 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30998 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31000 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31002 this.colWidth = boxWidth + avail - this.padWidth;
31004 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31005 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
31008 horizontalMeasureColumns : function()
31010 this.getContainerWidth();
31012 var boxWidth = this.boxWidth;
31014 if(this.containerWidth < boxWidth){
31015 boxWidth = this.containerWidth;
31018 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31020 this.el.setHeight(boxWidth);
31024 getContainerWidth : function()
31026 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
31029 layoutItems : function( isInstant )
31031 Roo.log(this.bricks);
31033 var items = Roo.apply([], this.bricks);
31035 if(this.isHorizontal){
31036 this._horizontalLayoutItems( items , isInstant );
31040 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31041 // this._verticalAlternativeLayoutItems( items , isInstant );
31045 this._verticalLayoutItems( items , isInstant );
31049 _verticalLayoutItems : function ( items , isInstant)
31051 if ( !items || !items.length ) {
31056 ['xs', 'xs', 'xs', 'tall'],
31057 ['xs', 'xs', 'tall'],
31058 ['xs', 'xs', 'sm'],
31059 ['xs', 'xs', 'xs'],
31065 ['sm', 'xs', 'xs'],
31069 ['tall', 'xs', 'xs', 'xs'],
31070 ['tall', 'xs', 'xs'],
31082 Roo.each(items, function(item, k){
31084 switch (item.size) {
31085 // these layouts take up a full box,
31096 boxes.push([item]);
31119 var filterPattern = function(box, length)
31127 var pattern = box.slice(0, length);
31131 Roo.each(pattern, function(i){
31132 format.push(i.size);
31135 Roo.each(standard, function(s){
31137 if(String(s) != String(format)){
31146 if(!match && length == 1){
31151 filterPattern(box, length - 1);
31155 queue.push(pattern);
31157 box = box.slice(length, box.length);
31159 filterPattern(box, 4);
31165 Roo.each(boxes, function(box, k){
31171 if(box.length == 1){
31176 filterPattern(box, 4);
31180 this._processVerticalLayoutQueue( queue, isInstant );
31184 // _verticalAlternativeLayoutItems : function( items , isInstant )
31186 // if ( !items || !items.length ) {
31190 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31194 _horizontalLayoutItems : function ( items , isInstant)
31196 if ( !items || !items.length || items.length < 3) {
31202 var eItems = items.slice(0, 3);
31204 items = items.slice(3, items.length);
31207 ['xs', 'xs', 'xs', 'wide'],
31208 ['xs', 'xs', 'wide'],
31209 ['xs', 'xs', 'sm'],
31210 ['xs', 'xs', 'xs'],
31216 ['sm', 'xs', 'xs'],
31220 ['wide', 'xs', 'xs', 'xs'],
31221 ['wide', 'xs', 'xs'],
31234 Roo.each(items, function(item, k){
31236 switch (item.size) {
31247 boxes.push([item]);
31271 var filterPattern = function(box, length)
31279 var pattern = box.slice(0, length);
31283 Roo.each(pattern, function(i){
31284 format.push(i.size);
31287 Roo.each(standard, function(s){
31289 if(String(s) != String(format)){
31298 if(!match && length == 1){
31303 filterPattern(box, length - 1);
31307 queue.push(pattern);
31309 box = box.slice(length, box.length);
31311 filterPattern(box, 4);
31317 Roo.each(boxes, function(box, k){
31323 if(box.length == 1){
31328 filterPattern(box, 4);
31335 var pos = this.el.getBox(true);
31339 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31341 var hit_end = false;
31343 Roo.each(queue, function(box){
31347 Roo.each(box, function(b){
31349 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31359 Roo.each(box, function(b){
31361 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31364 mx = Math.max(mx, b.x);
31368 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31372 Roo.each(box, function(b){
31374 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31388 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31391 /** Sets position of item in DOM
31392 * @param {Element} item
31393 * @param {Number} x - horizontal position
31394 * @param {Number} y - vertical position
31395 * @param {Boolean} isInstant - disables transitions
31397 _processVerticalLayoutQueue : function( queue, isInstant )
31399 var pos = this.el.getBox(true);
31404 for (var i = 0; i < this.cols; i++){
31408 Roo.each(queue, function(box, k){
31410 var col = k % this.cols;
31412 Roo.each(box, function(b,kk){
31414 b.el.position('absolute');
31416 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31417 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31419 if(b.size == 'md-left' || b.size == 'md-right'){
31420 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31421 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31424 b.el.setWidth(width);
31425 b.el.setHeight(height);
31427 b.el.select('iframe',true).setSize(width,height);
31431 for (var i = 0; i < this.cols; i++){
31433 if(maxY[i] < maxY[col]){
31438 col = Math.min(col, i);
31442 x = pos.x + col * (this.colWidth + this.padWidth);
31446 var positions = [];
31448 switch (box.length){
31450 positions = this.getVerticalOneBoxColPositions(x, y, box);
31453 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31456 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31459 positions = this.getVerticalFourBoxColPositions(x, y, box);
31465 Roo.each(box, function(b,kk){
31467 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31469 var sz = b.el.getSize();
31471 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31479 for (var i = 0; i < this.cols; i++){
31480 mY = Math.max(mY, maxY[i]);
31483 this.el.setHeight(mY - pos.y);
31487 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31489 // var pos = this.el.getBox(true);
31492 // var maxX = pos.right;
31494 // var maxHeight = 0;
31496 // Roo.each(items, function(item, k){
31500 // item.el.position('absolute');
31502 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31504 // item.el.setWidth(width);
31506 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31508 // item.el.setHeight(height);
31511 // item.el.setXY([x, y], isInstant ? false : true);
31513 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31516 // y = y + height + this.alternativePadWidth;
31518 // maxHeight = maxHeight + height + this.alternativePadWidth;
31522 // this.el.setHeight(maxHeight);
31526 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31528 var pos = this.el.getBox(true);
31533 var maxX = pos.right;
31535 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31537 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31539 Roo.each(queue, function(box, k){
31541 Roo.each(box, function(b, kk){
31543 b.el.position('absolute');
31545 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31546 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31548 if(b.size == 'md-left' || b.size == 'md-right'){
31549 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31550 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31553 b.el.setWidth(width);
31554 b.el.setHeight(height);
31562 var positions = [];
31564 switch (box.length){
31566 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31569 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31572 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31575 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31581 Roo.each(box, function(b,kk){
31583 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31585 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31593 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31595 Roo.each(eItems, function(b,k){
31597 b.size = (k == 0) ? 'sm' : 'xs';
31598 b.x = (k == 0) ? 2 : 1;
31599 b.y = (k == 0) ? 2 : 1;
31601 b.el.position('absolute');
31603 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31605 b.el.setWidth(width);
31607 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31609 b.el.setHeight(height);
31613 var positions = [];
31616 x : maxX - this.unitWidth * 2 - this.gutter,
31621 x : maxX - this.unitWidth,
31622 y : minY + (this.unitWidth + this.gutter) * 2
31626 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31630 Roo.each(eItems, function(b,k){
31632 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31638 getVerticalOneBoxColPositions : function(x, y, box)
31642 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31644 if(box[0].size == 'md-left'){
31648 if(box[0].size == 'md-right'){
31653 x : x + (this.unitWidth + this.gutter) * rand,
31660 getVerticalTwoBoxColPositions : function(x, y, box)
31664 if(box[0].size == 'xs'){
31668 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31672 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31686 x : x + (this.unitWidth + this.gutter) * 2,
31687 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31694 getVerticalThreeBoxColPositions : function(x, y, box)
31698 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31706 x : x + (this.unitWidth + this.gutter) * 1,
31711 x : x + (this.unitWidth + this.gutter) * 2,
31719 if(box[0].size == 'xs' && box[1].size == 'xs'){
31728 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31732 x : x + (this.unitWidth + this.gutter) * 1,
31746 x : x + (this.unitWidth + this.gutter) * 2,
31751 x : x + (this.unitWidth + this.gutter) * 2,
31752 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31759 getVerticalFourBoxColPositions : function(x, y, box)
31763 if(box[0].size == 'xs'){
31772 y : y + (this.unitHeight + this.gutter) * 1
31777 y : y + (this.unitHeight + this.gutter) * 2
31781 x : x + (this.unitWidth + this.gutter) * 1,
31795 x : x + (this.unitWidth + this.gutter) * 2,
31800 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31801 y : y + (this.unitHeight + this.gutter) * 1
31805 x : x + (this.unitWidth + this.gutter) * 2,
31806 y : y + (this.unitWidth + this.gutter) * 2
31813 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31817 if(box[0].size == 'md-left'){
31819 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31826 if(box[0].size == 'md-right'){
31828 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31829 y : minY + (this.unitWidth + this.gutter) * 1
31835 var rand = Math.floor(Math.random() * (4 - box[0].y));
31838 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31839 y : minY + (this.unitWidth + this.gutter) * rand
31846 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31850 if(box[0].size == 'xs'){
31853 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31858 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31859 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31867 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31872 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31873 y : minY + (this.unitWidth + this.gutter) * 2
31880 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31884 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31887 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31892 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31893 y : minY + (this.unitWidth + this.gutter) * 1
31897 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31898 y : minY + (this.unitWidth + this.gutter) * 2
31905 if(box[0].size == 'xs' && box[1].size == 'xs'){
31908 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31913 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31918 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31919 y : minY + (this.unitWidth + this.gutter) * 1
31927 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31932 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31933 y : minY + (this.unitWidth + this.gutter) * 2
31937 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31938 y : minY + (this.unitWidth + this.gutter) * 2
31945 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31949 if(box[0].size == 'xs'){
31952 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31962 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),
31967 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31968 y : minY + (this.unitWidth + this.gutter) * 1
31976 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31981 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31982 y : minY + (this.unitWidth + this.gutter) * 2
31986 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31987 y : minY + (this.unitWidth + this.gutter) * 2
31991 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),
31992 y : minY + (this.unitWidth + this.gutter) * 2
32000 * remove a Masonry Brick
32001 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32003 removeBrick : function(brick_id)
32009 for (var i = 0; i<this.bricks.length; i++) {
32010 if (this.bricks[i].id == brick_id) {
32011 this.bricks.splice(i,1);
32012 this.el.dom.removeChild(Roo.get(brick_id).dom);
32019 * adds a Masonry Brick
32020 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32022 addBrick : function(cfg)
32024 var cn = new Roo.bootstrap.MasonryBrick(cfg);
32025 //this.register(cn);
32026 cn.parentId = this.id;
32027 cn.render(this.el);
32032 * register a Masonry Brick
32033 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32036 register : function(brick)
32038 this.bricks.push(brick);
32039 brick.masonryId = this.id;
32043 * clear all the Masonry Brick
32045 clearAll : function()
32048 //this.getChildContainer().dom.innerHTML = "";
32049 this.el.dom.innerHTML = '';
32052 getSelected : function()
32054 if (!this.selectedBrick) {
32058 return this.selectedBrick;
32062 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32066 * register a Masonry Layout
32067 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32070 register : function(layout)
32072 this.groups[layout.id] = layout;
32075 * fetch a Masonry Layout based on the masonry layout ID
32076 * @param {string} the masonry layout to add
32077 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32080 get: function(layout_id) {
32081 if (typeof(this.groups[layout_id]) == 'undefined') {
32084 return this.groups[layout_id] ;
32096 * http://masonry.desandro.com
32098 * The idea is to render all the bricks based on vertical width...
32100 * The original code extends 'outlayer' - we might need to use that....
32106 * @class Roo.bootstrap.LayoutMasonryAuto
32107 * @extends Roo.bootstrap.Component
32108 * Bootstrap Layout Masonry class
32111 * Create a new Element
32112 * @param {Object} config The config object
32115 Roo.bootstrap.LayoutMasonryAuto = function(config){
32116 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32119 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32122 * @cfg {Boolean} isFitWidth - resize the width..
32124 isFitWidth : false, // options..
32126 * @cfg {Boolean} isOriginLeft = left align?
32128 isOriginLeft : true,
32130 * @cfg {Boolean} isOriginTop = top align?
32132 isOriginTop : false,
32134 * @cfg {Boolean} isLayoutInstant = no animation?
32136 isLayoutInstant : false, // needed?
32138 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32140 isResizingContainer : true,
32142 * @cfg {Number} columnWidth width of the columns
32148 * @cfg {Number} maxCols maximum number of columns
32153 * @cfg {Number} padHeight padding below box..
32159 * @cfg {Boolean} isAutoInitial defalut true
32162 isAutoInitial : true,
32168 initialColumnWidth : 0,
32169 currentSize : null,
32171 colYs : null, // array.
32178 bricks: null, //CompositeElement
32179 cols : 0, // array?
32180 // element : null, // wrapped now this.el
32181 _isLayoutInited : null,
32184 getAutoCreate : function(){
32188 cls: 'blog-masonary-wrapper ' + this.cls,
32190 cls : 'mas-boxes masonary'
32197 getChildContainer: function( )
32199 if (this.boxesEl) {
32200 return this.boxesEl;
32203 this.boxesEl = this.el.select('.mas-boxes').first();
32205 return this.boxesEl;
32209 initEvents : function()
32213 if(this.isAutoInitial){
32214 Roo.log('hook children rendered');
32215 this.on('childrenrendered', function() {
32216 Roo.log('children rendered');
32223 initial : function()
32225 this.reloadItems();
32227 this.currentSize = this.el.getBox(true);
32229 /// was window resize... - let's see if this works..
32230 Roo.EventManager.onWindowResize(this.resize, this);
32232 if(!this.isAutoInitial){
32237 this.layout.defer(500,this);
32240 reloadItems: function()
32242 this.bricks = this.el.select('.masonry-brick', true);
32244 this.bricks.each(function(b) {
32245 //Roo.log(b.getSize());
32246 if (!b.attr('originalwidth')) {
32247 b.attr('originalwidth', b.getSize().width);
32252 Roo.log(this.bricks.elements.length);
32255 resize : function()
32258 var cs = this.el.getBox(true);
32260 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32261 Roo.log("no change in with or X");
32264 this.currentSize = cs;
32268 layout : function()
32271 this._resetLayout();
32272 //this._manageStamps();
32274 // don't animate first layout
32275 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32276 this.layoutItems( isInstant );
32278 // flag for initalized
32279 this._isLayoutInited = true;
32282 layoutItems : function( isInstant )
32284 //var items = this._getItemsForLayout( this.items );
32285 // original code supports filtering layout items.. we just ignore it..
32287 this._layoutItems( this.bricks , isInstant );
32289 this._postLayout();
32291 _layoutItems : function ( items , isInstant)
32293 //this.fireEvent( 'layout', this, items );
32296 if ( !items || !items.elements.length ) {
32297 // no items, emit event with empty array
32302 items.each(function(item) {
32303 Roo.log("layout item");
32305 // get x/y object from method
32306 var position = this._getItemLayoutPosition( item );
32308 position.item = item;
32309 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32310 queue.push( position );
32313 this._processLayoutQueue( queue );
32315 /** Sets position of item in DOM
32316 * @param {Element} item
32317 * @param {Number} x - horizontal position
32318 * @param {Number} y - vertical position
32319 * @param {Boolean} isInstant - disables transitions
32321 _processLayoutQueue : function( queue )
32323 for ( var i=0, len = queue.length; i < len; i++ ) {
32324 var obj = queue[i];
32325 obj.item.position('absolute');
32326 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32332 * Any logic you want to do after each layout,
32333 * i.e. size the container
32335 _postLayout : function()
32337 this.resizeContainer();
32340 resizeContainer : function()
32342 if ( !this.isResizingContainer ) {
32345 var size = this._getContainerSize();
32347 this.el.setSize(size.width,size.height);
32348 this.boxesEl.setSize(size.width,size.height);
32354 _resetLayout : function()
32356 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32357 this.colWidth = this.el.getWidth();
32358 //this.gutter = this.el.getWidth();
32360 this.measureColumns();
32366 this.colYs.push( 0 );
32372 measureColumns : function()
32374 this.getContainerWidth();
32375 // if columnWidth is 0, default to outerWidth of first item
32376 if ( !this.columnWidth ) {
32377 var firstItem = this.bricks.first();
32378 Roo.log(firstItem);
32379 this.columnWidth = this.containerWidth;
32380 if (firstItem && firstItem.attr('originalwidth') ) {
32381 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32383 // columnWidth fall back to item of first element
32384 Roo.log("set column width?");
32385 this.initialColumnWidth = this.columnWidth ;
32387 // if first elem has no width, default to size of container
32392 if (this.initialColumnWidth) {
32393 this.columnWidth = this.initialColumnWidth;
32398 // column width is fixed at the top - however if container width get's smaller we should
32401 // this bit calcs how man columns..
32403 var columnWidth = this.columnWidth += this.gutter;
32405 // calculate columns
32406 var containerWidth = this.containerWidth + this.gutter;
32408 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32409 // fix rounding errors, typically with gutters
32410 var excess = columnWidth - containerWidth % columnWidth;
32413 // if overshoot is less than a pixel, round up, otherwise floor it
32414 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32415 cols = Math[ mathMethod ]( cols );
32416 this.cols = Math.max( cols, 1 );
32417 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32419 // padding positioning..
32420 var totalColWidth = this.cols * this.columnWidth;
32421 var padavail = this.containerWidth - totalColWidth;
32422 // so for 2 columns - we need 3 'pads'
32424 var padNeeded = (1+this.cols) * this.padWidth;
32426 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32428 this.columnWidth += padExtra
32429 //this.padWidth = Math.floor(padavail / ( this.cols));
32431 // adjust colum width so that padding is fixed??
32433 // we have 3 columns ... total = width * 3
32434 // we have X left over... that should be used by
32436 //if (this.expandC) {
32444 getContainerWidth : function()
32446 /* // container is parent if fit width
32447 var container = this.isFitWidth ? this.element.parentNode : this.element;
32448 // check that this.size and size are there
32449 // IE8 triggers resize on body size change, so they might not be
32451 var size = getSize( container ); //FIXME
32452 this.containerWidth = size && size.innerWidth; //FIXME
32455 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32459 _getItemLayoutPosition : function( item ) // what is item?
32461 // we resize the item to our columnWidth..
32463 item.setWidth(this.columnWidth);
32464 item.autoBoxAdjust = false;
32466 var sz = item.getSize();
32468 // how many columns does this brick span
32469 var remainder = this.containerWidth % this.columnWidth;
32471 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32472 // round if off by 1 pixel, otherwise use ceil
32473 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32474 colSpan = Math.min( colSpan, this.cols );
32476 // normally this should be '1' as we dont' currently allow multi width columns..
32478 var colGroup = this._getColGroup( colSpan );
32479 // get the minimum Y value from the columns
32480 var minimumY = Math.min.apply( Math, colGroup );
32481 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32483 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32485 // position the brick
32487 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32488 y: this.currentSize.y + minimumY + this.padHeight
32492 // apply setHeight to necessary columns
32493 var setHeight = minimumY + sz.height + this.padHeight;
32494 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32496 var setSpan = this.cols + 1 - colGroup.length;
32497 for ( var i = 0; i < setSpan; i++ ) {
32498 this.colYs[ shortColIndex + i ] = setHeight ;
32505 * @param {Number} colSpan - number of columns the element spans
32506 * @returns {Array} colGroup
32508 _getColGroup : function( colSpan )
32510 if ( colSpan < 2 ) {
32511 // if brick spans only one column, use all the column Ys
32516 // how many different places could this brick fit horizontally
32517 var groupCount = this.cols + 1 - colSpan;
32518 // for each group potential horizontal position
32519 for ( var i = 0; i < groupCount; i++ ) {
32520 // make an array of colY values for that one group
32521 var groupColYs = this.colYs.slice( i, i + colSpan );
32522 // and get the max value of the array
32523 colGroup[i] = Math.max.apply( Math, groupColYs );
32528 _manageStamp : function( stamp )
32530 var stampSize = stamp.getSize();
32531 var offset = stamp.getBox();
32532 // get the columns that this stamp affects
32533 var firstX = this.isOriginLeft ? offset.x : offset.right;
32534 var lastX = firstX + stampSize.width;
32535 var firstCol = Math.floor( firstX / this.columnWidth );
32536 firstCol = Math.max( 0, firstCol );
32538 var lastCol = Math.floor( lastX / this.columnWidth );
32539 // lastCol should not go over if multiple of columnWidth #425
32540 lastCol -= lastX % this.columnWidth ? 0 : 1;
32541 lastCol = Math.min( this.cols - 1, lastCol );
32543 // set colYs to bottom of the stamp
32544 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32547 for ( var i = firstCol; i <= lastCol; i++ ) {
32548 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32553 _getContainerSize : function()
32555 this.maxY = Math.max.apply( Math, this.colYs );
32560 if ( this.isFitWidth ) {
32561 size.width = this._getContainerFitWidth();
32567 _getContainerFitWidth : function()
32569 var unusedCols = 0;
32570 // count unused columns
32573 if ( this.colYs[i] !== 0 ) {
32578 // fit container to columns that have been used
32579 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32582 needsResizeLayout : function()
32584 var previousWidth = this.containerWidth;
32585 this.getContainerWidth();
32586 return previousWidth !== this.containerWidth;
32601 * @class Roo.bootstrap.MasonryBrick
32602 * @extends Roo.bootstrap.Component
32603 * Bootstrap MasonryBrick class
32606 * Create a new MasonryBrick
32607 * @param {Object} config The config object
32610 Roo.bootstrap.MasonryBrick = function(config){
32612 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32614 Roo.bootstrap.MasonryBrick.register(this);
32620 * When a MasonryBrick is clcik
32621 * @param {Roo.bootstrap.MasonryBrick} this
32622 * @param {Roo.EventObject} e
32628 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32631 * @cfg {String} title
32635 * @cfg {String} html
32639 * @cfg {String} bgimage
32643 * @cfg {String} videourl
32647 * @cfg {String} cls
32651 * @cfg {String} href
32655 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32660 * @cfg {String} placetitle (center|bottom)
32665 * @cfg {Boolean} isFitContainer defalut true
32667 isFitContainer : true,
32670 * @cfg {Boolean} preventDefault defalut false
32672 preventDefault : false,
32675 * @cfg {Boolean} inverse defalut false
32677 maskInverse : false,
32679 getAutoCreate : function()
32681 if(!this.isFitContainer){
32682 return this.getSplitAutoCreate();
32685 var cls = 'masonry-brick masonry-brick-full';
32687 if(this.href.length){
32688 cls += ' masonry-brick-link';
32691 if(this.bgimage.length){
32692 cls += ' masonry-brick-image';
32695 if(this.maskInverse){
32696 cls += ' mask-inverse';
32699 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32700 cls += ' enable-mask';
32704 cls += ' masonry-' + this.size + '-brick';
32707 if(this.placetitle.length){
32709 switch (this.placetitle) {
32711 cls += ' masonry-center-title';
32714 cls += ' masonry-bottom-title';
32721 if(!this.html.length && !this.bgimage.length){
32722 cls += ' masonry-center-title';
32725 if(!this.html.length && this.bgimage.length){
32726 cls += ' masonry-bottom-title';
32731 cls += ' ' + this.cls;
32735 tag: (this.href.length) ? 'a' : 'div',
32740 cls: 'masonry-brick-mask'
32744 cls: 'masonry-brick-paragraph',
32750 if(this.href.length){
32751 cfg.href = this.href;
32754 var cn = cfg.cn[1].cn;
32756 if(this.title.length){
32759 cls: 'masonry-brick-title',
32764 if(this.html.length){
32767 cls: 'masonry-brick-text',
32772 if (!this.title.length && !this.html.length) {
32773 cfg.cn[1].cls += ' hide';
32776 if(this.bgimage.length){
32779 cls: 'masonry-brick-image-view',
32784 if(this.videourl.length){
32785 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32786 // youtube support only?
32789 cls: 'masonry-brick-image-view',
32792 allowfullscreen : true
32800 getSplitAutoCreate : function()
32802 var cls = 'masonry-brick masonry-brick-split';
32804 if(this.href.length){
32805 cls += ' masonry-brick-link';
32808 if(this.bgimage.length){
32809 cls += ' masonry-brick-image';
32813 cls += ' masonry-' + this.size + '-brick';
32816 switch (this.placetitle) {
32818 cls += ' masonry-center-title';
32821 cls += ' masonry-bottom-title';
32824 if(!this.bgimage.length){
32825 cls += ' masonry-center-title';
32828 if(this.bgimage.length){
32829 cls += ' masonry-bottom-title';
32835 cls += ' ' + this.cls;
32839 tag: (this.href.length) ? 'a' : 'div',
32844 cls: 'masonry-brick-split-head',
32848 cls: 'masonry-brick-paragraph',
32855 cls: 'masonry-brick-split-body',
32861 if(this.href.length){
32862 cfg.href = this.href;
32865 if(this.title.length){
32866 cfg.cn[0].cn[0].cn.push({
32868 cls: 'masonry-brick-title',
32873 if(this.html.length){
32874 cfg.cn[1].cn.push({
32876 cls: 'masonry-brick-text',
32881 if(this.bgimage.length){
32882 cfg.cn[0].cn.push({
32884 cls: 'masonry-brick-image-view',
32889 if(this.videourl.length){
32890 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32891 // youtube support only?
32892 cfg.cn[0].cn.cn.push({
32894 cls: 'masonry-brick-image-view',
32897 allowfullscreen : true
32904 initEvents: function()
32906 switch (this.size) {
32939 this.el.on('touchstart', this.onTouchStart, this);
32940 this.el.on('touchmove', this.onTouchMove, this);
32941 this.el.on('touchend', this.onTouchEnd, this);
32942 this.el.on('contextmenu', this.onContextMenu, this);
32944 this.el.on('mouseenter' ,this.enter, this);
32945 this.el.on('mouseleave', this.leave, this);
32946 this.el.on('click', this.onClick, this);
32949 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32950 this.parent().bricks.push(this);
32955 onClick: function(e, el)
32957 var time = this.endTimer - this.startTimer;
32958 // Roo.log(e.preventDefault());
32961 e.preventDefault();
32966 if(!this.preventDefault){
32970 e.preventDefault();
32972 if (this.activeClass != '') {
32973 this.selectBrick();
32976 this.fireEvent('click', this, e);
32979 enter: function(e, el)
32981 e.preventDefault();
32983 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32987 if(this.bgimage.length && this.html.length){
32988 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32992 leave: function(e, el)
32994 e.preventDefault();
32996 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33000 if(this.bgimage.length && this.html.length){
33001 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33005 onTouchStart: function(e, el)
33007 // e.preventDefault();
33009 this.touchmoved = false;
33011 if(!this.isFitContainer){
33015 if(!this.bgimage.length || !this.html.length){
33019 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33021 this.timer = new Date().getTime();
33025 onTouchMove: function(e, el)
33027 this.touchmoved = true;
33030 onContextMenu : function(e,el)
33032 e.preventDefault();
33033 e.stopPropagation();
33037 onTouchEnd: function(e, el)
33039 // e.preventDefault();
33041 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33048 if(!this.bgimage.length || !this.html.length){
33050 if(this.href.length){
33051 window.location.href = this.href;
33057 if(!this.isFitContainer){
33061 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33063 window.location.href = this.href;
33066 //selection on single brick only
33067 selectBrick : function() {
33069 if (!this.parentId) {
33073 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33074 var index = m.selectedBrick.indexOf(this.id);
33077 m.selectedBrick.splice(index,1);
33078 this.el.removeClass(this.activeClass);
33082 for(var i = 0; i < m.selectedBrick.length; i++) {
33083 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33084 b.el.removeClass(b.activeClass);
33087 m.selectedBrick = [];
33089 m.selectedBrick.push(this.id);
33090 this.el.addClass(this.activeClass);
33094 isSelected : function(){
33095 return this.el.hasClass(this.activeClass);
33100 Roo.apply(Roo.bootstrap.MasonryBrick, {
33103 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33105 * register a Masonry Brick
33106 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33109 register : function(brick)
33111 //this.groups[brick.id] = brick;
33112 this.groups.add(brick.id, brick);
33115 * fetch a masonry brick based on the masonry brick ID
33116 * @param {string} the masonry brick to add
33117 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33120 get: function(brick_id)
33122 // if (typeof(this.groups[brick_id]) == 'undefined') {
33125 // return this.groups[brick_id] ;
33127 if(this.groups.key(brick_id)) {
33128 return this.groups.key(brick_id);
33146 * @class Roo.bootstrap.Brick
33147 * @extends Roo.bootstrap.Component
33148 * Bootstrap Brick class
33151 * Create a new Brick
33152 * @param {Object} config The config object
33155 Roo.bootstrap.Brick = function(config){
33156 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33162 * When a Brick is click
33163 * @param {Roo.bootstrap.Brick} this
33164 * @param {Roo.EventObject} e
33170 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33173 * @cfg {String} title
33177 * @cfg {String} html
33181 * @cfg {String} bgimage
33185 * @cfg {String} cls
33189 * @cfg {String} href
33193 * @cfg {String} video
33197 * @cfg {Boolean} square
33201 getAutoCreate : function()
33203 var cls = 'roo-brick';
33205 if(this.href.length){
33206 cls += ' roo-brick-link';
33209 if(this.bgimage.length){
33210 cls += ' roo-brick-image';
33213 if(!this.html.length && !this.bgimage.length){
33214 cls += ' roo-brick-center-title';
33217 if(!this.html.length && this.bgimage.length){
33218 cls += ' roo-brick-bottom-title';
33222 cls += ' ' + this.cls;
33226 tag: (this.href.length) ? 'a' : 'div',
33231 cls: 'roo-brick-paragraph',
33237 if(this.href.length){
33238 cfg.href = this.href;
33241 var cn = cfg.cn[0].cn;
33243 if(this.title.length){
33246 cls: 'roo-brick-title',
33251 if(this.html.length){
33254 cls: 'roo-brick-text',
33261 if(this.bgimage.length){
33264 cls: 'roo-brick-image-view',
33272 initEvents: function()
33274 if(this.title.length || this.html.length){
33275 this.el.on('mouseenter' ,this.enter, this);
33276 this.el.on('mouseleave', this.leave, this);
33279 Roo.EventManager.onWindowResize(this.resize, this);
33281 if(this.bgimage.length){
33282 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33283 this.imageEl.on('load', this.onImageLoad, this);
33290 onImageLoad : function()
33295 resize : function()
33297 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33299 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33301 if(this.bgimage.length){
33302 var image = this.el.select('.roo-brick-image-view', true).first();
33304 image.setWidth(paragraph.getWidth());
33307 image.setHeight(paragraph.getWidth());
33310 this.el.setHeight(image.getHeight());
33311 paragraph.setHeight(image.getHeight());
33317 enter: function(e, el)
33319 e.preventDefault();
33321 if(this.bgimage.length){
33322 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33323 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33327 leave: function(e, el)
33329 e.preventDefault();
33331 if(this.bgimage.length){
33332 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33333 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33348 * @class Roo.bootstrap.NumberField
33349 * @extends Roo.bootstrap.Input
33350 * Bootstrap NumberField class
33356 * Create a new NumberField
33357 * @param {Object} config The config object
33360 Roo.bootstrap.NumberField = function(config){
33361 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33364 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33367 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33369 allowDecimals : true,
33371 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33373 decimalSeparator : ".",
33375 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33377 decimalPrecision : 2,
33379 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33381 allowNegative : true,
33384 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33388 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33390 minValue : Number.NEGATIVE_INFINITY,
33392 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33394 maxValue : Number.MAX_VALUE,
33396 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33398 minText : "The minimum value for this field is {0}",
33400 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33402 maxText : "The maximum value for this field is {0}",
33404 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33405 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33407 nanText : "{0} is not a valid number",
33409 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33411 thousandsDelimiter : false,
33413 * @cfg {String} valueAlign alignment of value
33415 valueAlign : "left",
33417 getAutoCreate : function()
33419 var hiddenInput = {
33423 cls: 'hidden-number-input'
33427 hiddenInput.name = this.name;
33432 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33434 this.name = hiddenInput.name;
33436 if(cfg.cn.length > 0) {
33437 cfg.cn.push(hiddenInput);
33444 initEvents : function()
33446 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33448 var allowed = "0123456789";
33450 if(this.allowDecimals){
33451 allowed += this.decimalSeparator;
33454 if(this.allowNegative){
33458 if(this.thousandsDelimiter) {
33462 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33464 var keyPress = function(e){
33466 var k = e.getKey();
33468 var c = e.getCharCode();
33471 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33472 allowed.indexOf(String.fromCharCode(c)) === -1
33478 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33482 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33487 this.el.on("keypress", keyPress, this);
33490 validateValue : function(value)
33493 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33497 var num = this.parseValue(value);
33500 this.markInvalid(String.format(this.nanText, value));
33504 if(num < this.minValue){
33505 this.markInvalid(String.format(this.minText, this.minValue));
33509 if(num > this.maxValue){
33510 this.markInvalid(String.format(this.maxText, this.maxValue));
33517 getValue : function()
33519 var v = this.hiddenEl().getValue();
33521 return this.fixPrecision(this.parseValue(v));
33524 parseValue : function(value)
33526 if(this.thousandsDelimiter) {
33528 r = new RegExp(",", "g");
33529 value = value.replace(r, "");
33532 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33533 return isNaN(value) ? '' : value;
33536 fixPrecision : function(value)
33538 if(this.thousandsDelimiter) {
33540 r = new RegExp(",", "g");
33541 value = value.replace(r, "");
33544 var nan = isNaN(value);
33546 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33547 return nan ? '' : value;
33549 return parseFloat(value).toFixed(this.decimalPrecision);
33552 setValue : function(v)
33554 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33560 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33562 this.inputEl().dom.value = (v == '') ? '' :
33563 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33565 if(!this.allowZero && v === '0') {
33566 this.hiddenEl().dom.value = '';
33567 this.inputEl().dom.value = '';
33574 decimalPrecisionFcn : function(v)
33576 return Math.floor(v);
33579 beforeBlur : function()
33581 var v = this.parseValue(this.getRawValue());
33583 if(v || v === 0 || v === ''){
33588 hiddenEl : function()
33590 return this.el.select('input.hidden-number-input',true).first();
33602 * @class Roo.bootstrap.DocumentSlider
33603 * @extends Roo.bootstrap.Component
33604 * Bootstrap DocumentSlider class
33607 * Create a new DocumentViewer
33608 * @param {Object} config The config object
33611 Roo.bootstrap.DocumentSlider = function(config){
33612 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33619 * Fire after initEvent
33620 * @param {Roo.bootstrap.DocumentSlider} this
33625 * Fire after update
33626 * @param {Roo.bootstrap.DocumentSlider} this
33632 * @param {Roo.bootstrap.DocumentSlider} this
33638 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33644 getAutoCreate : function()
33648 cls : 'roo-document-slider',
33652 cls : 'roo-document-slider-header',
33656 cls : 'roo-document-slider-header-title'
33662 cls : 'roo-document-slider-body',
33666 cls : 'roo-document-slider-prev',
33670 cls : 'fa fa-chevron-left'
33676 cls : 'roo-document-slider-thumb',
33680 cls : 'roo-document-slider-image'
33686 cls : 'roo-document-slider-next',
33690 cls : 'fa fa-chevron-right'
33702 initEvents : function()
33704 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33705 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33707 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33708 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33710 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33711 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33713 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33714 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33716 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33717 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33719 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33720 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33722 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33723 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33725 this.thumbEl.on('click', this.onClick, this);
33727 this.prevIndicator.on('click', this.prev, this);
33729 this.nextIndicator.on('click', this.next, this);
33733 initial : function()
33735 if(this.files.length){
33736 this.indicator = 1;
33740 this.fireEvent('initial', this);
33743 update : function()
33745 this.imageEl.attr('src', this.files[this.indicator - 1]);
33747 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33749 this.prevIndicator.show();
33751 if(this.indicator == 1){
33752 this.prevIndicator.hide();
33755 this.nextIndicator.show();
33757 if(this.indicator == this.files.length){
33758 this.nextIndicator.hide();
33761 this.thumbEl.scrollTo('top');
33763 this.fireEvent('update', this);
33766 onClick : function(e)
33768 e.preventDefault();
33770 this.fireEvent('click', this);
33775 e.preventDefault();
33777 this.indicator = Math.max(1, this.indicator - 1);
33784 e.preventDefault();
33786 this.indicator = Math.min(this.files.length, this.indicator + 1);
33800 * @class Roo.bootstrap.RadioSet
33801 * @extends Roo.bootstrap.Input
33802 * Bootstrap RadioSet class
33803 * @cfg {String} indicatorpos (left|right) default left
33804 * @cfg {Boolean} inline (true|false) inline the element (default true)
33805 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33807 * Create a new RadioSet
33808 * @param {Object} config The config object
33811 Roo.bootstrap.RadioSet = function(config){
33813 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33817 Roo.bootstrap.RadioSet.register(this);
33822 * Fires when the element is checked or unchecked.
33823 * @param {Roo.bootstrap.RadioSet} this This radio
33824 * @param {Roo.bootstrap.Radio} item The checked item
33829 * Fires when the element is click.
33830 * @param {Roo.bootstrap.RadioSet} this This radio set
33831 * @param {Roo.bootstrap.Radio} item The checked item
33832 * @param {Roo.EventObject} e The event object
33839 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33847 indicatorpos : 'left',
33849 getAutoCreate : function()
33853 cls : 'roo-radio-set-label',
33857 html : this.fieldLabel
33862 if(this.indicatorpos == 'left'){
33865 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33866 tooltip : 'This field is required'
33871 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33872 tooltip : 'This field is required'
33878 cls : 'roo-radio-set-items'
33881 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33883 if (align === 'left' && this.fieldLabel.length) {
33886 cls : "roo-radio-set-right",
33892 if(this.labelWidth > 12){
33893 label.style = "width: " + this.labelWidth + 'px';
33896 if(this.labelWidth < 13 && this.labelmd == 0){
33897 this.labelmd = this.labelWidth;
33900 if(this.labellg > 0){
33901 label.cls += ' col-lg-' + this.labellg;
33902 items.cls += ' col-lg-' + (12 - this.labellg);
33905 if(this.labelmd > 0){
33906 label.cls += ' col-md-' + this.labelmd;
33907 items.cls += ' col-md-' + (12 - this.labelmd);
33910 if(this.labelsm > 0){
33911 label.cls += ' col-sm-' + this.labelsm;
33912 items.cls += ' col-sm-' + (12 - this.labelsm);
33915 if(this.labelxs > 0){
33916 label.cls += ' col-xs-' + this.labelxs;
33917 items.cls += ' col-xs-' + (12 - this.labelxs);
33923 cls : 'roo-radio-set',
33927 cls : 'roo-radio-set-input',
33930 value : this.value ? this.value : ''
33937 if(this.weight.length){
33938 cfg.cls += ' roo-radio-' + this.weight;
33942 cfg.cls += ' roo-radio-set-inline';
33946 ['xs','sm','md','lg'].map(function(size){
33947 if (settings[size]) {
33948 cfg.cls += ' col-' + size + '-' + settings[size];
33956 initEvents : function()
33958 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33959 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33961 if(!this.fieldLabel.length){
33962 this.labelEl.hide();
33965 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33966 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33968 this.indicator = this.indicatorEl();
33970 if(this.indicator){
33971 this.indicator.addClass('invisible');
33974 this.originalValue = this.getValue();
33978 inputEl: function ()
33980 return this.el.select('.roo-radio-set-input', true).first();
33983 getChildContainer : function()
33985 return this.itemsEl;
33988 register : function(item)
33990 this.radioes.push(item);
33994 validate : function()
33996 if(this.getVisibilityEl().hasClass('hidden')){
34002 Roo.each(this.radioes, function(i){
34011 if(this.allowBlank) {
34015 if(this.disabled || valid){
34020 this.markInvalid();
34025 markValid : function()
34027 if(this.labelEl.isVisible(true)){
34028 this.indicatorEl().removeClass('visible');
34029 this.indicatorEl().addClass('invisible');
34032 this.el.removeClass([this.invalidClass, this.validClass]);
34033 this.el.addClass(this.validClass);
34035 this.fireEvent('valid', this);
34038 markInvalid : function(msg)
34040 if(this.allowBlank || this.disabled){
34044 if(this.labelEl.isVisible(true)){
34045 this.indicatorEl().removeClass('invisible');
34046 this.indicatorEl().addClass('visible');
34049 this.el.removeClass([this.invalidClass, this.validClass]);
34050 this.el.addClass(this.invalidClass);
34052 this.fireEvent('invalid', this, msg);
34056 setValue : function(v, suppressEvent)
34058 if(this.value === v){
34065 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34068 Roo.each(this.radioes, function(i){
34070 i.el.removeClass('checked');
34073 Roo.each(this.radioes, function(i){
34075 if(i.value === v || i.value.toString() === v.toString()){
34077 i.el.addClass('checked');
34079 if(suppressEvent !== true){
34080 this.fireEvent('check', this, i);
34091 clearInvalid : function(){
34093 if(!this.el || this.preventMark){
34097 this.el.removeClass([this.invalidClass]);
34099 this.fireEvent('valid', this);
34104 Roo.apply(Roo.bootstrap.RadioSet, {
34108 register : function(set)
34110 this.groups[set.name] = set;
34113 get: function(name)
34115 if (typeof(this.groups[name]) == 'undefined') {
34119 return this.groups[name] ;
34125 * Ext JS Library 1.1.1
34126 * Copyright(c) 2006-2007, Ext JS, LLC.
34128 * Originally Released Under LGPL - original licence link has changed is not relivant.
34131 * <script type="text/javascript">
34136 * @class Roo.bootstrap.SplitBar
34137 * @extends Roo.util.Observable
34138 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34142 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34143 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34144 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34145 split.minSize = 100;
34146 split.maxSize = 600;
34147 split.animate = true;
34148 split.on('moved', splitterMoved);
34151 * Create a new SplitBar
34152 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34153 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34154 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34155 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34156 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34157 position of the SplitBar).
34159 Roo.bootstrap.SplitBar = function(cfg){
34164 // dragElement : elm
34165 // resizingElement: el,
34167 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34168 // placement : Roo.bootstrap.SplitBar.LEFT ,
34169 // existingProxy ???
34172 this.el = Roo.get(cfg.dragElement, true);
34173 this.el.dom.unselectable = "on";
34175 this.resizingEl = Roo.get(cfg.resizingElement, true);
34179 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34180 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34183 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34186 * The minimum size of the resizing element. (Defaults to 0)
34192 * The maximum size of the resizing element. (Defaults to 2000)
34195 this.maxSize = 2000;
34198 * Whether to animate the transition to the new size
34201 this.animate = false;
34204 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34207 this.useShim = false;
34212 if(!cfg.existingProxy){
34214 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34216 this.proxy = Roo.get(cfg.existingProxy).dom;
34219 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34222 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34225 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34228 this.dragSpecs = {};
34231 * @private The adapter to use to positon and resize elements
34233 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34234 this.adapter.init(this);
34236 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34238 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34239 this.el.addClass("roo-splitbar-h");
34242 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34243 this.el.addClass("roo-splitbar-v");
34249 * Fires when the splitter is moved (alias for {@link #event-moved})
34250 * @param {Roo.bootstrap.SplitBar} this
34251 * @param {Number} newSize the new width or height
34256 * Fires when the splitter is moved
34257 * @param {Roo.bootstrap.SplitBar} this
34258 * @param {Number} newSize the new width or height
34262 * @event beforeresize
34263 * Fires before the splitter is dragged
34264 * @param {Roo.bootstrap.SplitBar} this
34266 "beforeresize" : true,
34268 "beforeapply" : true
34271 Roo.util.Observable.call(this);
34274 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34275 onStartProxyDrag : function(x, y){
34276 this.fireEvent("beforeresize", this);
34278 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34280 o.enableDisplayMode("block");
34281 // all splitbars share the same overlay
34282 Roo.bootstrap.SplitBar.prototype.overlay = o;
34284 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34285 this.overlay.show();
34286 Roo.get(this.proxy).setDisplayed("block");
34287 var size = this.adapter.getElementSize(this);
34288 this.activeMinSize = this.getMinimumSize();;
34289 this.activeMaxSize = this.getMaximumSize();;
34290 var c1 = size - this.activeMinSize;
34291 var c2 = Math.max(this.activeMaxSize - size, 0);
34292 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34293 this.dd.resetConstraints();
34294 this.dd.setXConstraint(
34295 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34296 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34298 this.dd.setYConstraint(0, 0);
34300 this.dd.resetConstraints();
34301 this.dd.setXConstraint(0, 0);
34302 this.dd.setYConstraint(
34303 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34304 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34307 this.dragSpecs.startSize = size;
34308 this.dragSpecs.startPoint = [x, y];
34309 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34313 * @private Called after the drag operation by the DDProxy
34315 onEndProxyDrag : function(e){
34316 Roo.get(this.proxy).setDisplayed(false);
34317 var endPoint = Roo.lib.Event.getXY(e);
34319 this.overlay.hide();
34322 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34323 newSize = this.dragSpecs.startSize +
34324 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34325 endPoint[0] - this.dragSpecs.startPoint[0] :
34326 this.dragSpecs.startPoint[0] - endPoint[0]
34329 newSize = this.dragSpecs.startSize +
34330 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34331 endPoint[1] - this.dragSpecs.startPoint[1] :
34332 this.dragSpecs.startPoint[1] - endPoint[1]
34335 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34336 if(newSize != this.dragSpecs.startSize){
34337 if(this.fireEvent('beforeapply', this, newSize) !== false){
34338 this.adapter.setElementSize(this, newSize);
34339 this.fireEvent("moved", this, newSize);
34340 this.fireEvent("resize", this, newSize);
34346 * Get the adapter this SplitBar uses
34347 * @return The adapter object
34349 getAdapter : function(){
34350 return this.adapter;
34354 * Set the adapter this SplitBar uses
34355 * @param {Object} adapter A SplitBar adapter object
34357 setAdapter : function(adapter){
34358 this.adapter = adapter;
34359 this.adapter.init(this);
34363 * Gets the minimum size for the resizing element
34364 * @return {Number} The minimum size
34366 getMinimumSize : function(){
34367 return this.minSize;
34371 * Sets the minimum size for the resizing element
34372 * @param {Number} minSize The minimum size
34374 setMinimumSize : function(minSize){
34375 this.minSize = minSize;
34379 * Gets the maximum size for the resizing element
34380 * @return {Number} The maximum size
34382 getMaximumSize : function(){
34383 return this.maxSize;
34387 * Sets the maximum size for the resizing element
34388 * @param {Number} maxSize The maximum size
34390 setMaximumSize : function(maxSize){
34391 this.maxSize = maxSize;
34395 * Sets the initialize size for the resizing element
34396 * @param {Number} size The initial size
34398 setCurrentSize : function(size){
34399 var oldAnimate = this.animate;
34400 this.animate = false;
34401 this.adapter.setElementSize(this, size);
34402 this.animate = oldAnimate;
34406 * Destroy this splitbar.
34407 * @param {Boolean} removeEl True to remove the element
34409 destroy : function(removeEl){
34411 this.shim.remove();
34414 this.proxy.parentNode.removeChild(this.proxy);
34422 * @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.
34424 Roo.bootstrap.SplitBar.createProxy = function(dir){
34425 var proxy = new Roo.Element(document.createElement("div"));
34426 proxy.unselectable();
34427 var cls = 'roo-splitbar-proxy';
34428 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34429 document.body.appendChild(proxy.dom);
34434 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34435 * Default Adapter. It assumes the splitter and resizing element are not positioned
34436 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34438 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34441 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34442 // do nothing for now
34443 init : function(s){
34447 * Called before drag operations to get the current size of the resizing element.
34448 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34450 getElementSize : function(s){
34451 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34452 return s.resizingEl.getWidth();
34454 return s.resizingEl.getHeight();
34459 * Called after drag operations to set the size of the resizing element.
34460 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34461 * @param {Number} newSize The new size to set
34462 * @param {Function} onComplete A function to be invoked when resizing is complete
34464 setElementSize : function(s, newSize, onComplete){
34465 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34467 s.resizingEl.setWidth(newSize);
34469 onComplete(s, newSize);
34472 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34477 s.resizingEl.setHeight(newSize);
34479 onComplete(s, newSize);
34482 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34489 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34490 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34491 * Adapter that moves the splitter element to align with the resized sizing element.
34492 * Used with an absolute positioned SplitBar.
34493 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34494 * document.body, make sure you assign an id to the body element.
34496 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34497 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34498 this.container = Roo.get(container);
34501 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34502 init : function(s){
34503 this.basic.init(s);
34506 getElementSize : function(s){
34507 return this.basic.getElementSize(s);
34510 setElementSize : function(s, newSize, onComplete){
34511 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34514 moveSplitter : function(s){
34515 var yes = Roo.bootstrap.SplitBar;
34516 switch(s.placement){
34518 s.el.setX(s.resizingEl.getRight());
34521 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34524 s.el.setY(s.resizingEl.getBottom());
34527 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34534 * Orientation constant - Create a vertical SplitBar
34538 Roo.bootstrap.SplitBar.VERTICAL = 1;
34541 * Orientation constant - Create a horizontal SplitBar
34545 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34548 * Placement constant - The resizing element is to the left of the splitter element
34552 Roo.bootstrap.SplitBar.LEFT = 1;
34555 * Placement constant - The resizing element is to the right of the splitter element
34559 Roo.bootstrap.SplitBar.RIGHT = 2;
34562 * Placement constant - The resizing element is positioned above the splitter element
34566 Roo.bootstrap.SplitBar.TOP = 3;
34569 * Placement constant - The resizing element is positioned under splitter element
34573 Roo.bootstrap.SplitBar.BOTTOM = 4;
34574 Roo.namespace("Roo.bootstrap.layout");/*
34576 * Ext JS Library 1.1.1
34577 * Copyright(c) 2006-2007, Ext JS, LLC.
34579 * Originally Released Under LGPL - original licence link has changed is not relivant.
34582 * <script type="text/javascript">
34586 * @class Roo.bootstrap.layout.Manager
34587 * @extends Roo.bootstrap.Component
34588 * Base class for layout managers.
34590 Roo.bootstrap.layout.Manager = function(config)
34592 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34598 /** false to disable window resize monitoring @type Boolean */
34599 this.monitorWindowResize = true;
34604 * Fires when a layout is performed.
34605 * @param {Roo.LayoutManager} this
34609 * @event regionresized
34610 * Fires when the user resizes a region.
34611 * @param {Roo.LayoutRegion} region The resized region
34612 * @param {Number} newSize The new size (width for east/west, height for north/south)
34614 "regionresized" : true,
34616 * @event regioncollapsed
34617 * Fires when a region is collapsed.
34618 * @param {Roo.LayoutRegion} region The collapsed region
34620 "regioncollapsed" : true,
34622 * @event regionexpanded
34623 * Fires when a region is expanded.
34624 * @param {Roo.LayoutRegion} region The expanded region
34626 "regionexpanded" : true
34628 this.updating = false;
34631 this.el = Roo.get(config.el);
34637 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34642 monitorWindowResize : true,
34648 onRender : function(ct, position)
34651 this.el = Roo.get(ct);
34654 //this.fireEvent('render',this);
34658 initEvents: function()
34662 // ie scrollbar fix
34663 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34664 document.body.scroll = "no";
34665 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34666 this.el.position('relative');
34668 this.id = this.el.id;
34669 this.el.addClass("roo-layout-container");
34670 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34671 if(this.el.dom != document.body ) {
34672 this.el.on('resize', this.layout,this);
34673 this.el.on('show', this.layout,this);
34679 * Returns true if this layout is currently being updated
34680 * @return {Boolean}
34682 isUpdating : function(){
34683 return this.updating;
34687 * Suspend the LayoutManager from doing auto-layouts while
34688 * making multiple add or remove calls
34690 beginUpdate : function(){
34691 this.updating = true;
34695 * Restore auto-layouts and optionally disable the manager from performing a layout
34696 * @param {Boolean} noLayout true to disable a layout update
34698 endUpdate : function(noLayout){
34699 this.updating = false;
34705 layout: function(){
34709 onRegionResized : function(region, newSize){
34710 this.fireEvent("regionresized", region, newSize);
34714 onRegionCollapsed : function(region){
34715 this.fireEvent("regioncollapsed", region);
34718 onRegionExpanded : function(region){
34719 this.fireEvent("regionexpanded", region);
34723 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34724 * performs box-model adjustments.
34725 * @return {Object} The size as an object {width: (the width), height: (the height)}
34727 getViewSize : function()
34730 if(this.el.dom != document.body){
34731 size = this.el.getSize();
34733 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34735 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34736 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34741 * Returns the Element this layout is bound to.
34742 * @return {Roo.Element}
34744 getEl : function(){
34749 * Returns the specified region.
34750 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34751 * @return {Roo.LayoutRegion}
34753 getRegion : function(target){
34754 return this.regions[target.toLowerCase()];
34757 onWindowResize : function(){
34758 if(this.monitorWindowResize){
34765 * Ext JS Library 1.1.1
34766 * Copyright(c) 2006-2007, Ext JS, LLC.
34768 * Originally Released Under LGPL - original licence link has changed is not relivant.
34771 * <script type="text/javascript">
34774 * @class Roo.bootstrap.layout.Border
34775 * @extends Roo.bootstrap.layout.Manager
34776 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34777 * please see: examples/bootstrap/nested.html<br><br>
34779 <b>The container the layout is rendered into can be either the body element or any other element.
34780 If it is not the body element, the container needs to either be an absolute positioned element,
34781 or you will need to add "position:relative" to the css of the container. You will also need to specify
34782 the container size if it is not the body element.</b>
34785 * Create a new Border
34786 * @param {Object} config Configuration options
34788 Roo.bootstrap.layout.Border = function(config){
34789 config = config || {};
34790 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34794 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34795 if(config[region]){
34796 config[region].region = region;
34797 this.addRegion(config[region]);
34803 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34805 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34807 * Creates and adds a new region if it doesn't already exist.
34808 * @param {String} target The target region key (north, south, east, west or center).
34809 * @param {Object} config The regions config object
34810 * @return {BorderLayoutRegion} The new region
34812 addRegion : function(config)
34814 if(!this.regions[config.region]){
34815 var r = this.factory(config);
34816 this.bindRegion(r);
34818 return this.regions[config.region];
34822 bindRegion : function(r){
34823 this.regions[r.config.region] = r;
34825 r.on("visibilitychange", this.layout, this);
34826 r.on("paneladded", this.layout, this);
34827 r.on("panelremoved", this.layout, this);
34828 r.on("invalidated", this.layout, this);
34829 r.on("resized", this.onRegionResized, this);
34830 r.on("collapsed", this.onRegionCollapsed, this);
34831 r.on("expanded", this.onRegionExpanded, this);
34835 * Performs a layout update.
34837 layout : function()
34839 if(this.updating) {
34843 // render all the rebions if they have not been done alreayd?
34844 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34845 if(this.regions[region] && !this.regions[region].bodyEl){
34846 this.regions[region].onRender(this.el)
34850 var size = this.getViewSize();
34851 var w = size.width;
34852 var h = size.height;
34857 //var x = 0, y = 0;
34859 var rs = this.regions;
34860 var north = rs["north"];
34861 var south = rs["south"];
34862 var west = rs["west"];
34863 var east = rs["east"];
34864 var center = rs["center"];
34865 //if(this.hideOnLayout){ // not supported anymore
34866 //c.el.setStyle("display", "none");
34868 if(north && north.isVisible()){
34869 var b = north.getBox();
34870 var m = north.getMargins();
34871 b.width = w - (m.left+m.right);
34874 centerY = b.height + b.y + m.bottom;
34875 centerH -= centerY;
34876 north.updateBox(this.safeBox(b));
34878 if(south && south.isVisible()){
34879 var b = south.getBox();
34880 var m = south.getMargins();
34881 b.width = w - (m.left+m.right);
34883 var totalHeight = (b.height + m.top + m.bottom);
34884 b.y = h - totalHeight + m.top;
34885 centerH -= totalHeight;
34886 south.updateBox(this.safeBox(b));
34888 if(west && west.isVisible()){
34889 var b = west.getBox();
34890 var m = west.getMargins();
34891 b.height = centerH - (m.top+m.bottom);
34893 b.y = centerY + m.top;
34894 var totalWidth = (b.width + m.left + m.right);
34895 centerX += totalWidth;
34896 centerW -= totalWidth;
34897 west.updateBox(this.safeBox(b));
34899 if(east && east.isVisible()){
34900 var b = east.getBox();
34901 var m = east.getMargins();
34902 b.height = centerH - (m.top+m.bottom);
34903 var totalWidth = (b.width + m.left + m.right);
34904 b.x = w - totalWidth + m.left;
34905 b.y = centerY + m.top;
34906 centerW -= totalWidth;
34907 east.updateBox(this.safeBox(b));
34910 var m = center.getMargins();
34912 x: centerX + m.left,
34913 y: centerY + m.top,
34914 width: centerW - (m.left+m.right),
34915 height: centerH - (m.top+m.bottom)
34917 //if(this.hideOnLayout){
34918 //center.el.setStyle("display", "block");
34920 center.updateBox(this.safeBox(centerBox));
34923 this.fireEvent("layout", this);
34927 safeBox : function(box){
34928 box.width = Math.max(0, box.width);
34929 box.height = Math.max(0, box.height);
34934 * Adds a ContentPanel (or subclass) to this layout.
34935 * @param {String} target The target region key (north, south, east, west or center).
34936 * @param {Roo.ContentPanel} panel The panel to add
34937 * @return {Roo.ContentPanel} The added panel
34939 add : function(target, panel){
34941 target = target.toLowerCase();
34942 return this.regions[target].add(panel);
34946 * Remove a ContentPanel (or subclass) to this layout.
34947 * @param {String} target The target region key (north, south, east, west or center).
34948 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34949 * @return {Roo.ContentPanel} The removed panel
34951 remove : function(target, panel){
34952 target = target.toLowerCase();
34953 return this.regions[target].remove(panel);
34957 * Searches all regions for a panel with the specified id
34958 * @param {String} panelId
34959 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34961 findPanel : function(panelId){
34962 var rs = this.regions;
34963 for(var target in rs){
34964 if(typeof rs[target] != "function"){
34965 var p = rs[target].getPanel(panelId);
34975 * Searches all regions for a panel with the specified id and activates (shows) it.
34976 * @param {String/ContentPanel} panelId The panels id or the panel itself
34977 * @return {Roo.ContentPanel} The shown panel or null
34979 showPanel : function(panelId) {
34980 var rs = this.regions;
34981 for(var target in rs){
34982 var r = rs[target];
34983 if(typeof r != "function"){
34984 if(r.hasPanel(panelId)){
34985 return r.showPanel(panelId);
34993 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34994 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34997 restoreState : function(provider){
34999 provider = Roo.state.Manager;
35001 var sm = new Roo.LayoutStateManager();
35002 sm.init(this, provider);
35008 * Adds a xtype elements to the layout.
35012 xtype : 'ContentPanel',
35019 xtype : 'NestedLayoutPanel',
35025 items : [ ... list of content panels or nested layout panels.. ]
35029 * @param {Object} cfg Xtype definition of item to add.
35031 addxtype : function(cfg)
35033 // basically accepts a pannel...
35034 // can accept a layout region..!?!?
35035 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35038 // theory? children can only be panels??
35040 //if (!cfg.xtype.match(/Panel$/)) {
35045 if (typeof(cfg.region) == 'undefined') {
35046 Roo.log("Failed to add Panel, region was not set");
35050 var region = cfg.region;
35056 xitems = cfg.items;
35063 case 'Content': // ContentPanel (el, cfg)
35064 case 'Scroll': // ContentPanel (el, cfg)
35066 cfg.autoCreate = true;
35067 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35069 // var el = this.el.createChild();
35070 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35073 this.add(region, ret);
35077 case 'TreePanel': // our new panel!
35078 cfg.el = this.el.createChild();
35079 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35080 this.add(region, ret);
35085 // create a new Layout (which is a Border Layout...
35087 var clayout = cfg.layout;
35088 clayout.el = this.el.createChild();
35089 clayout.items = clayout.items || [];
35093 // replace this exitems with the clayout ones..
35094 xitems = clayout.items;
35096 // force background off if it's in center...
35097 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35098 cfg.background = false;
35100 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
35103 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35104 //console.log('adding nested layout panel ' + cfg.toSource());
35105 this.add(region, ret);
35106 nb = {}; /// find first...
35111 // needs grid and region
35113 //var el = this.getRegion(region).el.createChild();
35115 *var el = this.el.createChild();
35116 // create the grid first...
35117 cfg.grid.container = el;
35118 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35121 if (region == 'center' && this.active ) {
35122 cfg.background = false;
35125 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35127 this.add(region, ret);
35129 if (cfg.background) {
35130 // render grid on panel activation (if panel background)
35131 ret.on('activate', function(gp) {
35132 if (!gp.grid.rendered) {
35133 // gp.grid.render(el);
35137 // cfg.grid.render(el);
35143 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35144 // it was the old xcomponent building that caused this before.
35145 // espeically if border is the top element in the tree.
35155 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35157 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35158 this.add(region, ret);
35162 throw "Can not add '" + cfg.xtype + "' to Border";
35168 this.beginUpdate();
35172 Roo.each(xitems, function(i) {
35173 region = nb && i.region ? i.region : false;
35175 var add = ret.addxtype(i);
35178 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35179 if (!i.background) {
35180 abn[region] = nb[region] ;
35187 // make the last non-background panel active..
35188 //if (nb) { Roo.log(abn); }
35191 for(var r in abn) {
35192 region = this.getRegion(r);
35194 // tried using nb[r], but it does not work..
35196 region.showPanel(abn[r]);
35207 factory : function(cfg)
35210 var validRegions = Roo.bootstrap.layout.Border.regions;
35212 var target = cfg.region;
35215 var r = Roo.bootstrap.layout;
35219 return new r.North(cfg);
35221 return new r.South(cfg);
35223 return new r.East(cfg);
35225 return new r.West(cfg);
35227 return new r.Center(cfg);
35229 throw 'Layout region "'+target+'" not supported.';
35236 * Ext JS Library 1.1.1
35237 * Copyright(c) 2006-2007, Ext JS, LLC.
35239 * Originally Released Under LGPL - original licence link has changed is not relivant.
35242 * <script type="text/javascript">
35246 * @class Roo.bootstrap.layout.Basic
35247 * @extends Roo.util.Observable
35248 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35249 * and does not have a titlebar, tabs or any other features. All it does is size and position
35250 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35251 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35252 * @cfg {string} region the region that it inhabits..
35253 * @cfg {bool} skipConfig skip config?
35257 Roo.bootstrap.layout.Basic = function(config){
35259 this.mgr = config.mgr;
35261 this.position = config.region;
35263 var skipConfig = config.skipConfig;
35267 * @scope Roo.BasicLayoutRegion
35271 * @event beforeremove
35272 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35273 * @param {Roo.LayoutRegion} this
35274 * @param {Roo.ContentPanel} panel The panel
35275 * @param {Object} e The cancel event object
35277 "beforeremove" : true,
35279 * @event invalidated
35280 * Fires when the layout for this region is changed.
35281 * @param {Roo.LayoutRegion} this
35283 "invalidated" : true,
35285 * @event visibilitychange
35286 * Fires when this region is shown or hidden
35287 * @param {Roo.LayoutRegion} this
35288 * @param {Boolean} visibility true or false
35290 "visibilitychange" : true,
35292 * @event paneladded
35293 * Fires when a panel is added.
35294 * @param {Roo.LayoutRegion} this
35295 * @param {Roo.ContentPanel} panel The panel
35297 "paneladded" : true,
35299 * @event panelremoved
35300 * Fires when a panel is removed.
35301 * @param {Roo.LayoutRegion} this
35302 * @param {Roo.ContentPanel} panel The panel
35304 "panelremoved" : true,
35306 * @event beforecollapse
35307 * Fires when this region before collapse.
35308 * @param {Roo.LayoutRegion} this
35310 "beforecollapse" : true,
35313 * Fires when this region is collapsed.
35314 * @param {Roo.LayoutRegion} this
35316 "collapsed" : true,
35319 * Fires when this region is expanded.
35320 * @param {Roo.LayoutRegion} this
35325 * Fires when this region is slid into view.
35326 * @param {Roo.LayoutRegion} this
35328 "slideshow" : true,
35331 * Fires when this region slides out of view.
35332 * @param {Roo.LayoutRegion} this
35334 "slidehide" : true,
35336 * @event panelactivated
35337 * Fires when a panel is activated.
35338 * @param {Roo.LayoutRegion} this
35339 * @param {Roo.ContentPanel} panel The activated panel
35341 "panelactivated" : true,
35344 * Fires when the user resizes this region.
35345 * @param {Roo.LayoutRegion} this
35346 * @param {Number} newSize The new size (width for east/west, height for north/south)
35350 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35351 this.panels = new Roo.util.MixedCollection();
35352 this.panels.getKey = this.getPanelId.createDelegate(this);
35354 this.activePanel = null;
35355 // ensure listeners are added...
35357 if (config.listeners || config.events) {
35358 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35359 listeners : config.listeners || {},
35360 events : config.events || {}
35364 if(skipConfig !== true){
35365 this.applyConfig(config);
35369 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35371 getPanelId : function(p){
35375 applyConfig : function(config){
35376 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35377 this.config = config;
35382 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35383 * the width, for horizontal (north, south) the height.
35384 * @param {Number} newSize The new width or height
35386 resizeTo : function(newSize){
35387 var el = this.el ? this.el :
35388 (this.activePanel ? this.activePanel.getEl() : null);
35390 switch(this.position){
35393 el.setWidth(newSize);
35394 this.fireEvent("resized", this, newSize);
35398 el.setHeight(newSize);
35399 this.fireEvent("resized", this, newSize);
35405 getBox : function(){
35406 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35409 getMargins : function(){
35410 return this.margins;
35413 updateBox : function(box){
35415 var el = this.activePanel.getEl();
35416 el.dom.style.left = box.x + "px";
35417 el.dom.style.top = box.y + "px";
35418 this.activePanel.setSize(box.width, box.height);
35422 * Returns the container element for this region.
35423 * @return {Roo.Element}
35425 getEl : function(){
35426 return this.activePanel;
35430 * Returns true if this region is currently visible.
35431 * @return {Boolean}
35433 isVisible : function(){
35434 return this.activePanel ? true : false;
35437 setActivePanel : function(panel){
35438 panel = this.getPanel(panel);
35439 if(this.activePanel && this.activePanel != panel){
35440 this.activePanel.setActiveState(false);
35441 this.activePanel.getEl().setLeftTop(-10000,-10000);
35443 this.activePanel = panel;
35444 panel.setActiveState(true);
35446 panel.setSize(this.box.width, this.box.height);
35448 this.fireEvent("panelactivated", this, panel);
35449 this.fireEvent("invalidated");
35453 * Show the specified panel.
35454 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35455 * @return {Roo.ContentPanel} The shown panel or null
35457 showPanel : function(panel){
35458 panel = this.getPanel(panel);
35460 this.setActivePanel(panel);
35466 * Get the active panel for this region.
35467 * @return {Roo.ContentPanel} The active panel or null
35469 getActivePanel : function(){
35470 return this.activePanel;
35474 * Add the passed ContentPanel(s)
35475 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35476 * @return {Roo.ContentPanel} The panel added (if only one was added)
35478 add : function(panel){
35479 if(arguments.length > 1){
35480 for(var i = 0, len = arguments.length; i < len; i++) {
35481 this.add(arguments[i]);
35485 if(this.hasPanel(panel)){
35486 this.showPanel(panel);
35489 var el = panel.getEl();
35490 if(el.dom.parentNode != this.mgr.el.dom){
35491 this.mgr.el.dom.appendChild(el.dom);
35493 if(panel.setRegion){
35494 panel.setRegion(this);
35496 this.panels.add(panel);
35497 el.setStyle("position", "absolute");
35498 if(!panel.background){
35499 this.setActivePanel(panel);
35500 if(this.config.initialSize && this.panels.getCount()==1){
35501 this.resizeTo(this.config.initialSize);
35504 this.fireEvent("paneladded", this, panel);
35509 * Returns true if the panel is in this region.
35510 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35511 * @return {Boolean}
35513 hasPanel : function(panel){
35514 if(typeof panel == "object"){ // must be panel obj
35515 panel = panel.getId();
35517 return this.getPanel(panel) ? true : false;
35521 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35522 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35523 * @param {Boolean} preservePanel Overrides the config preservePanel option
35524 * @return {Roo.ContentPanel} The panel that was removed
35526 remove : function(panel, preservePanel){
35527 panel = this.getPanel(panel);
35532 this.fireEvent("beforeremove", this, panel, e);
35533 if(e.cancel === true){
35536 var panelId = panel.getId();
35537 this.panels.removeKey(panelId);
35542 * Returns the panel specified or null if it's not in this region.
35543 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35544 * @return {Roo.ContentPanel}
35546 getPanel : function(id){
35547 if(typeof id == "object"){ // must be panel obj
35550 return this.panels.get(id);
35554 * Returns this regions position (north/south/east/west/center).
35557 getPosition: function(){
35558 return this.position;
35562 * Ext JS Library 1.1.1
35563 * Copyright(c) 2006-2007, Ext JS, LLC.
35565 * Originally Released Under LGPL - original licence link has changed is not relivant.
35568 * <script type="text/javascript">
35572 * @class Roo.bootstrap.layout.Region
35573 * @extends Roo.bootstrap.layout.Basic
35574 * This class represents a region in a layout manager.
35576 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35577 * @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})
35578 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35579 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35580 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35581 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35582 * @cfg {String} title The title for the region (overrides panel titles)
35583 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35584 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35585 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35586 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35587 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35588 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35589 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35590 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35591 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35592 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35594 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35595 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35596 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35597 * @cfg {Number} width For East/West panels
35598 * @cfg {Number} height For North/South panels
35599 * @cfg {Boolean} split To show the splitter
35600 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35602 * @cfg {string} cls Extra CSS classes to add to region
35604 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35605 * @cfg {string} region the region that it inhabits..
35608 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35609 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35611 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35612 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35613 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35615 Roo.bootstrap.layout.Region = function(config)
35617 this.applyConfig(config);
35619 var mgr = config.mgr;
35620 var pos = config.region;
35621 config.skipConfig = true;
35622 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35625 this.onRender(mgr.el);
35628 this.visible = true;
35629 this.collapsed = false;
35630 this.unrendered_panels = [];
35633 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35635 position: '', // set by wrapper (eg. north/south etc..)
35636 unrendered_panels : null, // unrendered panels.
35637 createBody : function(){
35638 /** This region's body element
35639 * @type Roo.Element */
35640 this.bodyEl = this.el.createChild({
35642 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35646 onRender: function(ctr, pos)
35648 var dh = Roo.DomHelper;
35649 /** This region's container element
35650 * @type Roo.Element */
35651 this.el = dh.append(ctr.dom, {
35653 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35655 /** This region's title element
35656 * @type Roo.Element */
35658 this.titleEl = dh.append(this.el.dom,
35661 unselectable: "on",
35662 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35664 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35665 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35668 this.titleEl.enableDisplayMode();
35669 /** This region's title text element
35670 * @type HTMLElement */
35671 this.titleTextEl = this.titleEl.dom.firstChild;
35672 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35674 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35675 this.closeBtn.enableDisplayMode();
35676 this.closeBtn.on("click", this.closeClicked, this);
35677 this.closeBtn.hide();
35679 this.createBody(this.config);
35680 if(this.config.hideWhenEmpty){
35682 this.on("paneladded", this.validateVisibility, this);
35683 this.on("panelremoved", this.validateVisibility, this);
35685 if(this.autoScroll){
35686 this.bodyEl.setStyle("overflow", "auto");
35688 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35690 //if(c.titlebar !== false){
35691 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35692 this.titleEl.hide();
35694 this.titleEl.show();
35695 if(this.config.title){
35696 this.titleTextEl.innerHTML = this.config.title;
35700 if(this.config.collapsed){
35701 this.collapse(true);
35703 if(this.config.hidden){
35707 if (this.unrendered_panels && this.unrendered_panels.length) {
35708 for (var i =0;i< this.unrendered_panels.length; i++) {
35709 this.add(this.unrendered_panels[i]);
35711 this.unrendered_panels = null;
35717 applyConfig : function(c)
35720 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35721 var dh = Roo.DomHelper;
35722 if(c.titlebar !== false){
35723 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35724 this.collapseBtn.on("click", this.collapse, this);
35725 this.collapseBtn.enableDisplayMode();
35727 if(c.showPin === true || this.showPin){
35728 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35729 this.stickBtn.enableDisplayMode();
35730 this.stickBtn.on("click", this.expand, this);
35731 this.stickBtn.hide();
35736 /** This region's collapsed element
35737 * @type Roo.Element */
35740 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35741 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35744 if(c.floatable !== false){
35745 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35746 this.collapsedEl.on("click", this.collapseClick, this);
35749 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35750 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35751 id: "message", unselectable: "on", style:{"float":"left"}});
35752 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35754 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35755 this.expandBtn.on("click", this.expand, this);
35759 if(this.collapseBtn){
35760 this.collapseBtn.setVisible(c.collapsible == true);
35763 this.cmargins = c.cmargins || this.cmargins ||
35764 (this.position == "west" || this.position == "east" ?
35765 {top: 0, left: 2, right:2, bottom: 0} :
35766 {top: 2, left: 0, right:0, bottom: 2});
35768 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35771 this.bottomTabs = c.tabPosition != "top";
35773 this.autoScroll = c.autoScroll || false;
35778 this.duration = c.duration || .30;
35779 this.slideDuration = c.slideDuration || .45;
35784 * Returns true if this region is currently visible.
35785 * @return {Boolean}
35787 isVisible : function(){
35788 return this.visible;
35792 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35793 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35795 //setCollapsedTitle : function(title){
35796 // title = title || " ";
35797 // if(this.collapsedTitleTextEl){
35798 // this.collapsedTitleTextEl.innerHTML = title;
35802 getBox : function(){
35804 // if(!this.collapsed){
35805 b = this.el.getBox(false, true);
35807 // b = this.collapsedEl.getBox(false, true);
35812 getMargins : function(){
35813 return this.margins;
35814 //return this.collapsed ? this.cmargins : this.margins;
35817 highlight : function(){
35818 this.el.addClass("x-layout-panel-dragover");
35821 unhighlight : function(){
35822 this.el.removeClass("x-layout-panel-dragover");
35825 updateBox : function(box)
35827 if (!this.bodyEl) {
35828 return; // not rendered yet..
35832 if(!this.collapsed){
35833 this.el.dom.style.left = box.x + "px";
35834 this.el.dom.style.top = box.y + "px";
35835 this.updateBody(box.width, box.height);
35837 this.collapsedEl.dom.style.left = box.x + "px";
35838 this.collapsedEl.dom.style.top = box.y + "px";
35839 this.collapsedEl.setSize(box.width, box.height);
35842 this.tabs.autoSizeTabs();
35846 updateBody : function(w, h)
35849 this.el.setWidth(w);
35850 w -= this.el.getBorderWidth("rl");
35851 if(this.config.adjustments){
35852 w += this.config.adjustments[0];
35855 if(h !== null && h > 0){
35856 this.el.setHeight(h);
35857 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35858 h -= this.el.getBorderWidth("tb");
35859 if(this.config.adjustments){
35860 h += this.config.adjustments[1];
35862 this.bodyEl.setHeight(h);
35864 h = this.tabs.syncHeight(h);
35867 if(this.panelSize){
35868 w = w !== null ? w : this.panelSize.width;
35869 h = h !== null ? h : this.panelSize.height;
35871 if(this.activePanel){
35872 var el = this.activePanel.getEl();
35873 w = w !== null ? w : el.getWidth();
35874 h = h !== null ? h : el.getHeight();
35875 this.panelSize = {width: w, height: h};
35876 this.activePanel.setSize(w, h);
35878 if(Roo.isIE && this.tabs){
35879 this.tabs.el.repaint();
35884 * Returns the container element for this region.
35885 * @return {Roo.Element}
35887 getEl : function(){
35892 * Hides this region.
35895 //if(!this.collapsed){
35896 this.el.dom.style.left = "-2000px";
35899 // this.collapsedEl.dom.style.left = "-2000px";
35900 // this.collapsedEl.hide();
35902 this.visible = false;
35903 this.fireEvent("visibilitychange", this, false);
35907 * Shows this region if it was previously hidden.
35910 //if(!this.collapsed){
35913 // this.collapsedEl.show();
35915 this.visible = true;
35916 this.fireEvent("visibilitychange", this, true);
35919 closeClicked : function(){
35920 if(this.activePanel){
35921 this.remove(this.activePanel);
35925 collapseClick : function(e){
35927 e.stopPropagation();
35930 e.stopPropagation();
35936 * Collapses this region.
35937 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35940 collapse : function(skipAnim, skipCheck = false){
35941 if(this.collapsed) {
35945 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35947 this.collapsed = true;
35949 this.split.el.hide();
35951 if(this.config.animate && skipAnim !== true){
35952 this.fireEvent("invalidated", this);
35953 this.animateCollapse();
35955 this.el.setLocation(-20000,-20000);
35957 this.collapsedEl.show();
35958 this.fireEvent("collapsed", this);
35959 this.fireEvent("invalidated", this);
35965 animateCollapse : function(){
35970 * Expands this region if it was previously collapsed.
35971 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35972 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35975 expand : function(e, skipAnim){
35977 e.stopPropagation();
35979 if(!this.collapsed || this.el.hasActiveFx()) {
35983 this.afterSlideIn();
35986 this.collapsed = false;
35987 if(this.config.animate && skipAnim !== true){
35988 this.animateExpand();
35992 this.split.el.show();
35994 this.collapsedEl.setLocation(-2000,-2000);
35995 this.collapsedEl.hide();
35996 this.fireEvent("invalidated", this);
35997 this.fireEvent("expanded", this);
36001 animateExpand : function(){
36005 initTabs : function()
36007 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36009 var ts = new Roo.bootstrap.panel.Tabs({
36010 el: this.bodyEl.dom,
36011 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36012 disableTooltips: this.config.disableTabTips,
36013 toolbar : this.config.toolbar
36016 if(this.config.hideTabs){
36017 ts.stripWrap.setDisplayed(false);
36020 ts.resizeTabs = this.config.resizeTabs === true;
36021 ts.minTabWidth = this.config.minTabWidth || 40;
36022 ts.maxTabWidth = this.config.maxTabWidth || 250;
36023 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36024 ts.monitorResize = false;
36025 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36026 ts.bodyEl.addClass('roo-layout-tabs-body');
36027 this.panels.each(this.initPanelAsTab, this);
36030 initPanelAsTab : function(panel){
36031 var ti = this.tabs.addTab(
36035 this.config.closeOnTab && panel.isClosable(),
36038 if(panel.tabTip !== undefined){
36039 ti.setTooltip(panel.tabTip);
36041 ti.on("activate", function(){
36042 this.setActivePanel(panel);
36045 if(this.config.closeOnTab){
36046 ti.on("beforeclose", function(t, e){
36048 this.remove(panel);
36052 panel.tabItem = ti;
36057 updatePanelTitle : function(panel, title)
36059 if(this.activePanel == panel){
36060 this.updateTitle(title);
36063 var ti = this.tabs.getTab(panel.getEl().id);
36065 if(panel.tabTip !== undefined){
36066 ti.setTooltip(panel.tabTip);
36071 updateTitle : function(title){
36072 if(this.titleTextEl && !this.config.title){
36073 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
36077 setActivePanel : function(panel)
36079 panel = this.getPanel(panel);
36080 if(this.activePanel && this.activePanel != panel){
36081 if(this.activePanel.setActiveState(false) === false){
36085 this.activePanel = panel;
36086 panel.setActiveState(true);
36087 if(this.panelSize){
36088 panel.setSize(this.panelSize.width, this.panelSize.height);
36091 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36093 this.updateTitle(panel.getTitle());
36095 this.fireEvent("invalidated", this);
36097 this.fireEvent("panelactivated", this, panel);
36101 * Shows the specified panel.
36102 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36103 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36105 showPanel : function(panel)
36107 panel = this.getPanel(panel);
36110 var tab = this.tabs.getTab(panel.getEl().id);
36111 if(tab.isHidden()){
36112 this.tabs.unhideTab(tab.id);
36116 this.setActivePanel(panel);
36123 * Get the active panel for this region.
36124 * @return {Roo.ContentPanel} The active panel or null
36126 getActivePanel : function(){
36127 return this.activePanel;
36130 validateVisibility : function(){
36131 if(this.panels.getCount() < 1){
36132 this.updateTitle(" ");
36133 this.closeBtn.hide();
36136 if(!this.isVisible()){
36143 * Adds the passed ContentPanel(s) to this region.
36144 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36145 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36147 add : function(panel)
36149 if(arguments.length > 1){
36150 for(var i = 0, len = arguments.length; i < len; i++) {
36151 this.add(arguments[i]);
36156 // if we have not been rendered yet, then we can not really do much of this..
36157 if (!this.bodyEl) {
36158 this.unrendered_panels.push(panel);
36165 if(this.hasPanel(panel)){
36166 this.showPanel(panel);
36169 panel.setRegion(this);
36170 this.panels.add(panel);
36171 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36172 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36173 // and hide them... ???
36174 this.bodyEl.dom.appendChild(panel.getEl().dom);
36175 if(panel.background !== true){
36176 this.setActivePanel(panel);
36178 this.fireEvent("paneladded", this, panel);
36185 this.initPanelAsTab(panel);
36189 if(panel.background !== true){
36190 this.tabs.activate(panel.getEl().id);
36192 this.fireEvent("paneladded", this, panel);
36197 * Hides the tab for the specified panel.
36198 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36200 hidePanel : function(panel){
36201 if(this.tabs && (panel = this.getPanel(panel))){
36202 this.tabs.hideTab(panel.getEl().id);
36207 * Unhides the tab for a previously hidden panel.
36208 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36210 unhidePanel : function(panel){
36211 if(this.tabs && (panel = this.getPanel(panel))){
36212 this.tabs.unhideTab(panel.getEl().id);
36216 clearPanels : function(){
36217 while(this.panels.getCount() > 0){
36218 this.remove(this.panels.first());
36223 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36224 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36225 * @param {Boolean} preservePanel Overrides the config preservePanel option
36226 * @return {Roo.ContentPanel} The panel that was removed
36228 remove : function(panel, preservePanel)
36230 panel = this.getPanel(panel);
36235 this.fireEvent("beforeremove", this, panel, e);
36236 if(e.cancel === true){
36239 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36240 var panelId = panel.getId();
36241 this.panels.removeKey(panelId);
36243 document.body.appendChild(panel.getEl().dom);
36246 this.tabs.removeTab(panel.getEl().id);
36247 }else if (!preservePanel){
36248 this.bodyEl.dom.removeChild(panel.getEl().dom);
36250 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36251 var p = this.panels.first();
36252 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36253 tempEl.appendChild(p.getEl().dom);
36254 this.bodyEl.update("");
36255 this.bodyEl.dom.appendChild(p.getEl().dom);
36257 this.updateTitle(p.getTitle());
36259 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36260 this.setActivePanel(p);
36262 panel.setRegion(null);
36263 if(this.activePanel == panel){
36264 this.activePanel = null;
36266 if(this.config.autoDestroy !== false && preservePanel !== true){
36267 try{panel.destroy();}catch(e){}
36269 this.fireEvent("panelremoved", this, panel);
36274 * Returns the TabPanel component used by this region
36275 * @return {Roo.TabPanel}
36277 getTabs : function(){
36281 createTool : function(parentEl, className){
36282 var btn = Roo.DomHelper.append(parentEl, {
36284 cls: "x-layout-tools-button",
36287 cls: "roo-layout-tools-button-inner " + className,
36291 btn.addClassOnOver("roo-layout-tools-button-over");
36296 * Ext JS Library 1.1.1
36297 * Copyright(c) 2006-2007, Ext JS, LLC.
36299 * Originally Released Under LGPL - original licence link has changed is not relivant.
36302 * <script type="text/javascript">
36308 * @class Roo.SplitLayoutRegion
36309 * @extends Roo.LayoutRegion
36310 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36312 Roo.bootstrap.layout.Split = function(config){
36313 this.cursor = config.cursor;
36314 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36317 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36319 splitTip : "Drag to resize.",
36320 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36321 useSplitTips : false,
36323 applyConfig : function(config){
36324 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36327 onRender : function(ctr,pos) {
36329 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36330 if(!this.config.split){
36335 var splitEl = Roo.DomHelper.append(ctr.dom, {
36337 id: this.el.id + "-split",
36338 cls: "roo-layout-split roo-layout-split-"+this.position,
36341 /** The SplitBar for this region
36342 * @type Roo.SplitBar */
36343 // does not exist yet...
36344 Roo.log([this.position, this.orientation]);
36346 this.split = new Roo.bootstrap.SplitBar({
36347 dragElement : splitEl,
36348 resizingElement: this.el,
36349 orientation : this.orientation
36352 this.split.on("moved", this.onSplitMove, this);
36353 this.split.useShim = this.config.useShim === true;
36354 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36355 if(this.useSplitTips){
36356 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36358 //if(config.collapsible){
36359 // this.split.el.on("dblclick", this.collapse, this);
36362 if(typeof this.config.minSize != "undefined"){
36363 this.split.minSize = this.config.minSize;
36365 if(typeof this.config.maxSize != "undefined"){
36366 this.split.maxSize = this.config.maxSize;
36368 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36369 this.hideSplitter();
36374 getHMaxSize : function(){
36375 var cmax = this.config.maxSize || 10000;
36376 var center = this.mgr.getRegion("center");
36377 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36380 getVMaxSize : function(){
36381 var cmax = this.config.maxSize || 10000;
36382 var center = this.mgr.getRegion("center");
36383 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36386 onSplitMove : function(split, newSize){
36387 this.fireEvent("resized", this, newSize);
36391 * Returns the {@link Roo.SplitBar} for this region.
36392 * @return {Roo.SplitBar}
36394 getSplitBar : function(){
36399 this.hideSplitter();
36400 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36403 hideSplitter : function(){
36405 this.split.el.setLocation(-2000,-2000);
36406 this.split.el.hide();
36412 this.split.el.show();
36414 Roo.bootstrap.layout.Split.superclass.show.call(this);
36417 beforeSlide: function(){
36418 if(Roo.isGecko){// firefox overflow auto bug workaround
36419 this.bodyEl.clip();
36421 this.tabs.bodyEl.clip();
36423 if(this.activePanel){
36424 this.activePanel.getEl().clip();
36426 if(this.activePanel.beforeSlide){
36427 this.activePanel.beforeSlide();
36433 afterSlide : function(){
36434 if(Roo.isGecko){// firefox overflow auto bug workaround
36435 this.bodyEl.unclip();
36437 this.tabs.bodyEl.unclip();
36439 if(this.activePanel){
36440 this.activePanel.getEl().unclip();
36441 if(this.activePanel.afterSlide){
36442 this.activePanel.afterSlide();
36448 initAutoHide : function(){
36449 if(this.autoHide !== false){
36450 if(!this.autoHideHd){
36451 var st = new Roo.util.DelayedTask(this.slideIn, this);
36452 this.autoHideHd = {
36453 "mouseout": function(e){
36454 if(!e.within(this.el, true)){
36458 "mouseover" : function(e){
36464 this.el.on(this.autoHideHd);
36468 clearAutoHide : function(){
36469 if(this.autoHide !== false){
36470 this.el.un("mouseout", this.autoHideHd.mouseout);
36471 this.el.un("mouseover", this.autoHideHd.mouseover);
36475 clearMonitor : function(){
36476 Roo.get(document).un("click", this.slideInIf, this);
36479 // these names are backwards but not changed for compat
36480 slideOut : function(){
36481 if(this.isSlid || this.el.hasActiveFx()){
36484 this.isSlid = true;
36485 if(this.collapseBtn){
36486 this.collapseBtn.hide();
36488 this.closeBtnState = this.closeBtn.getStyle('display');
36489 this.closeBtn.hide();
36491 this.stickBtn.show();
36494 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36495 this.beforeSlide();
36496 this.el.setStyle("z-index", 10001);
36497 this.el.slideIn(this.getSlideAnchor(), {
36498 callback: function(){
36500 this.initAutoHide();
36501 Roo.get(document).on("click", this.slideInIf, this);
36502 this.fireEvent("slideshow", this);
36509 afterSlideIn : function(){
36510 this.clearAutoHide();
36511 this.isSlid = false;
36512 this.clearMonitor();
36513 this.el.setStyle("z-index", "");
36514 if(this.collapseBtn){
36515 this.collapseBtn.show();
36517 this.closeBtn.setStyle('display', this.closeBtnState);
36519 this.stickBtn.hide();
36521 this.fireEvent("slidehide", this);
36524 slideIn : function(cb){
36525 if(!this.isSlid || this.el.hasActiveFx()){
36529 this.isSlid = false;
36530 this.beforeSlide();
36531 this.el.slideOut(this.getSlideAnchor(), {
36532 callback: function(){
36533 this.el.setLeftTop(-10000, -10000);
36535 this.afterSlideIn();
36543 slideInIf : function(e){
36544 if(!e.within(this.el)){
36549 animateCollapse : function(){
36550 this.beforeSlide();
36551 this.el.setStyle("z-index", 20000);
36552 var anchor = this.getSlideAnchor();
36553 this.el.slideOut(anchor, {
36554 callback : function(){
36555 this.el.setStyle("z-index", "");
36556 this.collapsedEl.slideIn(anchor, {duration:.3});
36558 this.el.setLocation(-10000,-10000);
36560 this.fireEvent("collapsed", this);
36567 animateExpand : function(){
36568 this.beforeSlide();
36569 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36570 this.el.setStyle("z-index", 20000);
36571 this.collapsedEl.hide({
36574 this.el.slideIn(this.getSlideAnchor(), {
36575 callback : function(){
36576 this.el.setStyle("z-index", "");
36579 this.split.el.show();
36581 this.fireEvent("invalidated", this);
36582 this.fireEvent("expanded", this);
36610 getAnchor : function(){
36611 return this.anchors[this.position];
36614 getCollapseAnchor : function(){
36615 return this.canchors[this.position];
36618 getSlideAnchor : function(){
36619 return this.sanchors[this.position];
36622 getAlignAdj : function(){
36623 var cm = this.cmargins;
36624 switch(this.position){
36640 getExpandAdj : function(){
36641 var c = this.collapsedEl, cm = this.cmargins;
36642 switch(this.position){
36644 return [-(cm.right+c.getWidth()+cm.left), 0];
36647 return [cm.right+c.getWidth()+cm.left, 0];
36650 return [0, -(cm.top+cm.bottom+c.getHeight())];
36653 return [0, cm.top+cm.bottom+c.getHeight()];
36659 * Ext JS Library 1.1.1
36660 * Copyright(c) 2006-2007, Ext JS, LLC.
36662 * Originally Released Under LGPL - original licence link has changed is not relivant.
36665 * <script type="text/javascript">
36668 * These classes are private internal classes
36670 Roo.bootstrap.layout.Center = function(config){
36671 config.region = "center";
36672 Roo.bootstrap.layout.Region.call(this, config);
36673 this.visible = true;
36674 this.minWidth = config.minWidth || 20;
36675 this.minHeight = config.minHeight || 20;
36678 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36680 // center panel can't be hidden
36684 // center panel can't be hidden
36687 getMinWidth: function(){
36688 return this.minWidth;
36691 getMinHeight: function(){
36692 return this.minHeight;
36705 Roo.bootstrap.layout.North = function(config)
36707 config.region = 'north';
36708 config.cursor = 'n-resize';
36710 Roo.bootstrap.layout.Split.call(this, config);
36714 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36715 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36716 this.split.el.addClass("roo-layout-split-v");
36718 var size = config.initialSize || config.height;
36719 if(typeof size != "undefined"){
36720 this.el.setHeight(size);
36723 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36725 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36729 getBox : function(){
36730 if(this.collapsed){
36731 return this.collapsedEl.getBox();
36733 var box = this.el.getBox();
36735 box.height += this.split.el.getHeight();
36740 updateBox : function(box){
36741 if(this.split && !this.collapsed){
36742 box.height -= this.split.el.getHeight();
36743 this.split.el.setLeft(box.x);
36744 this.split.el.setTop(box.y+box.height);
36745 this.split.el.setWidth(box.width);
36747 if(this.collapsed){
36748 this.updateBody(box.width, null);
36750 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36758 Roo.bootstrap.layout.South = function(config){
36759 config.region = 'south';
36760 config.cursor = 's-resize';
36761 Roo.bootstrap.layout.Split.call(this, config);
36763 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36764 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36765 this.split.el.addClass("roo-layout-split-v");
36767 var size = config.initialSize || config.height;
36768 if(typeof size != "undefined"){
36769 this.el.setHeight(size);
36773 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36774 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36775 getBox : function(){
36776 if(this.collapsed){
36777 return this.collapsedEl.getBox();
36779 var box = this.el.getBox();
36781 var sh = this.split.el.getHeight();
36788 updateBox : function(box){
36789 if(this.split && !this.collapsed){
36790 var sh = this.split.el.getHeight();
36793 this.split.el.setLeft(box.x);
36794 this.split.el.setTop(box.y-sh);
36795 this.split.el.setWidth(box.width);
36797 if(this.collapsed){
36798 this.updateBody(box.width, null);
36800 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36804 Roo.bootstrap.layout.East = function(config){
36805 config.region = "east";
36806 config.cursor = "e-resize";
36807 Roo.bootstrap.layout.Split.call(this, config);
36809 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36810 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36811 this.split.el.addClass("roo-layout-split-h");
36813 var size = config.initialSize || config.width;
36814 if(typeof size != "undefined"){
36815 this.el.setWidth(size);
36818 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36819 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36820 getBox : function(){
36821 if(this.collapsed){
36822 return this.collapsedEl.getBox();
36824 var box = this.el.getBox();
36826 var sw = this.split.el.getWidth();
36833 updateBox : function(box){
36834 if(this.split && !this.collapsed){
36835 var sw = this.split.el.getWidth();
36837 this.split.el.setLeft(box.x);
36838 this.split.el.setTop(box.y);
36839 this.split.el.setHeight(box.height);
36842 if(this.collapsed){
36843 this.updateBody(null, box.height);
36845 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36849 Roo.bootstrap.layout.West = function(config){
36850 config.region = "west";
36851 config.cursor = "w-resize";
36853 Roo.bootstrap.layout.Split.call(this, config);
36855 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36856 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36857 this.split.el.addClass("roo-layout-split-h");
36861 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36862 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36864 onRender: function(ctr, pos)
36866 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36867 var size = this.config.initialSize || this.config.width;
36868 if(typeof size != "undefined"){
36869 this.el.setWidth(size);
36873 getBox : function(){
36874 if(this.collapsed){
36875 return this.collapsedEl.getBox();
36877 var box = this.el.getBox();
36879 box.width += this.split.el.getWidth();
36884 updateBox : function(box){
36885 if(this.split && !this.collapsed){
36886 var sw = this.split.el.getWidth();
36888 this.split.el.setLeft(box.x+box.width);
36889 this.split.el.setTop(box.y);
36890 this.split.el.setHeight(box.height);
36892 if(this.collapsed){
36893 this.updateBody(null, box.height);
36895 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36898 Roo.namespace("Roo.bootstrap.panel");/*
36900 * Ext JS Library 1.1.1
36901 * Copyright(c) 2006-2007, Ext JS, LLC.
36903 * Originally Released Under LGPL - original licence link has changed is not relivant.
36906 * <script type="text/javascript">
36909 * @class Roo.ContentPanel
36910 * @extends Roo.util.Observable
36911 * A basic ContentPanel element.
36912 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36913 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36914 * @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
36915 * @cfg {Boolean} closable True if the panel can be closed/removed
36916 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36917 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36918 * @cfg {Toolbar} toolbar A toolbar for this panel
36919 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36920 * @cfg {String} title The title for this panel
36921 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36922 * @cfg {String} url Calls {@link #setUrl} with this value
36923 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36924 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36925 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36926 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36927 * @cfg {Boolean} badges render the badges
36930 * Create a new ContentPanel.
36931 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36932 * @param {String/Object} config A string to set only the title or a config object
36933 * @param {String} content (optional) Set the HTML content for this panel
36934 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36936 Roo.bootstrap.panel.Content = function( config){
36938 this.tpl = config.tpl || false;
36940 var el = config.el;
36941 var content = config.content;
36943 if(config.autoCreate){ // xtype is available if this is called from factory
36946 this.el = Roo.get(el);
36947 if(!this.el && config && config.autoCreate){
36948 if(typeof config.autoCreate == "object"){
36949 if(!config.autoCreate.id){
36950 config.autoCreate.id = config.id||el;
36952 this.el = Roo.DomHelper.append(document.body,
36953 config.autoCreate, true);
36955 var elcfg = { tag: "div",
36956 cls: "roo-layout-inactive-content",
36960 elcfg.html = config.html;
36964 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36967 this.closable = false;
36968 this.loaded = false;
36969 this.active = false;
36972 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36974 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36976 this.wrapEl = this.el; //this.el.wrap();
36978 if (config.toolbar.items) {
36979 ti = config.toolbar.items ;
36980 delete config.toolbar.items ;
36984 this.toolbar.render(this.wrapEl, 'before');
36985 for(var i =0;i < ti.length;i++) {
36986 // Roo.log(['add child', items[i]]);
36987 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36989 this.toolbar.items = nitems;
36990 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36991 delete config.toolbar;
36995 // xtype created footer. - not sure if will work as we normally have to render first..
36996 if (this.footer && !this.footer.el && this.footer.xtype) {
36997 if (!this.wrapEl) {
36998 this.wrapEl = this.el.wrap();
37001 this.footer.container = this.wrapEl.createChild();
37003 this.footer = Roo.factory(this.footer, Roo);
37008 if(typeof config == "string"){
37009 this.title = config;
37011 Roo.apply(this, config);
37015 this.resizeEl = Roo.get(this.resizeEl, true);
37017 this.resizeEl = this.el;
37019 // handle view.xtype
37027 * Fires when this panel is activated.
37028 * @param {Roo.ContentPanel} this
37032 * @event deactivate
37033 * Fires when this panel is activated.
37034 * @param {Roo.ContentPanel} this
37036 "deactivate" : true,
37040 * Fires when this panel is resized if fitToFrame is true.
37041 * @param {Roo.ContentPanel} this
37042 * @param {Number} width The width after any component adjustments
37043 * @param {Number} height The height after any component adjustments
37049 * Fires when this tab is created
37050 * @param {Roo.ContentPanel} this
37061 if(this.autoScroll){
37062 this.resizeEl.setStyle("overflow", "auto");
37064 // fix randome scrolling
37065 //this.el.on('scroll', function() {
37066 // Roo.log('fix random scolling');
37067 // this.scrollTo('top',0);
37070 content = content || this.content;
37072 this.setContent(content);
37074 if(config && config.url){
37075 this.setUrl(this.url, this.params, this.loadOnce);
37080 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37082 if (this.view && typeof(this.view.xtype) != 'undefined') {
37083 this.view.el = this.el.appendChild(document.createElement("div"));
37084 this.view = Roo.factory(this.view);
37085 this.view.render && this.view.render(false, '');
37089 this.fireEvent('render', this);
37092 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37096 setRegion : function(region){
37097 this.region = region;
37098 this.setActiveClass(region && !this.background);
37102 setActiveClass: function(state)
37105 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37106 this.el.setStyle('position','relative');
37108 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37109 this.el.setStyle('position', 'absolute');
37114 * Returns the toolbar for this Panel if one was configured.
37115 * @return {Roo.Toolbar}
37117 getToolbar : function(){
37118 return this.toolbar;
37121 setActiveState : function(active)
37123 this.active = active;
37124 this.setActiveClass(active);
37126 if(this.fireEvent("deactivate", this) === false){
37131 this.fireEvent("activate", this);
37135 * Updates this panel's element
37136 * @param {String} content The new content
37137 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37139 setContent : function(content, loadScripts){
37140 this.el.update(content, loadScripts);
37143 ignoreResize : function(w, h){
37144 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37147 this.lastSize = {width: w, height: h};
37152 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37153 * @return {Roo.UpdateManager} The UpdateManager
37155 getUpdateManager : function(){
37156 return this.el.getUpdateManager();
37159 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37160 * @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:
37163 url: "your-url.php",
37164 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37165 callback: yourFunction,
37166 scope: yourObject, //(optional scope)
37169 text: "Loading...",
37174 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37175 * 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.
37176 * @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}
37177 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37178 * @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.
37179 * @return {Roo.ContentPanel} this
37182 var um = this.el.getUpdateManager();
37183 um.update.apply(um, arguments);
37189 * 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.
37190 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37191 * @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)
37192 * @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)
37193 * @return {Roo.UpdateManager} The UpdateManager
37195 setUrl : function(url, params, loadOnce){
37196 if(this.refreshDelegate){
37197 this.removeListener("activate", this.refreshDelegate);
37199 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37200 this.on("activate", this.refreshDelegate);
37201 return this.el.getUpdateManager();
37204 _handleRefresh : function(url, params, loadOnce){
37205 if(!loadOnce || !this.loaded){
37206 var updater = this.el.getUpdateManager();
37207 updater.update(url, params, this._setLoaded.createDelegate(this));
37211 _setLoaded : function(){
37212 this.loaded = true;
37216 * Returns this panel's id
37219 getId : function(){
37224 * Returns this panel's element - used by regiosn to add.
37225 * @return {Roo.Element}
37227 getEl : function(){
37228 return this.wrapEl || this.el;
37233 adjustForComponents : function(width, height)
37235 //Roo.log('adjustForComponents ');
37236 if(this.resizeEl != this.el){
37237 width -= this.el.getFrameWidth('lr');
37238 height -= this.el.getFrameWidth('tb');
37241 var te = this.toolbar.getEl();
37242 te.setWidth(width);
37243 height -= te.getHeight();
37246 var te = this.footer.getEl();
37247 te.setWidth(width);
37248 height -= te.getHeight();
37252 if(this.adjustments){
37253 width += this.adjustments[0];
37254 height += this.adjustments[1];
37256 return {"width": width, "height": height};
37259 setSize : function(width, height){
37260 if(this.fitToFrame && !this.ignoreResize(width, height)){
37261 if(this.fitContainer && this.resizeEl != this.el){
37262 this.el.setSize(width, height);
37264 var size = this.adjustForComponents(width, height);
37265 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37266 this.fireEvent('resize', this, size.width, size.height);
37271 * Returns this panel's title
37274 getTitle : function(){
37276 if (typeof(this.title) != 'object') {
37281 for (var k in this.title) {
37282 if (!this.title.hasOwnProperty(k)) {
37286 if (k.indexOf('-') >= 0) {
37287 var s = k.split('-');
37288 for (var i = 0; i<s.length; i++) {
37289 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37292 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37299 * Set this panel's title
37300 * @param {String} title
37302 setTitle : function(title){
37303 this.title = title;
37305 this.region.updatePanelTitle(this, title);
37310 * Returns true is this panel was configured to be closable
37311 * @return {Boolean}
37313 isClosable : function(){
37314 return this.closable;
37317 beforeSlide : function(){
37319 this.resizeEl.clip();
37322 afterSlide : function(){
37324 this.resizeEl.unclip();
37328 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37329 * Will fail silently if the {@link #setUrl} method has not been called.
37330 * This does not activate the panel, just updates its content.
37332 refresh : function(){
37333 if(this.refreshDelegate){
37334 this.loaded = false;
37335 this.refreshDelegate();
37340 * Destroys this panel
37342 destroy : function(){
37343 this.el.removeAllListeners();
37344 var tempEl = document.createElement("span");
37345 tempEl.appendChild(this.el.dom);
37346 tempEl.innerHTML = "";
37352 * form - if the content panel contains a form - this is a reference to it.
37353 * @type {Roo.form.Form}
37357 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37358 * This contains a reference to it.
37364 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37374 * @param {Object} cfg Xtype definition of item to add.
37378 getChildContainer: function () {
37379 return this.getEl();
37384 var ret = new Roo.factory(cfg);
37389 if (cfg.xtype.match(/^Form$/)) {
37392 //if (this.footer) {
37393 // el = this.footer.container.insertSibling(false, 'before');
37395 el = this.el.createChild();
37398 this.form = new Roo.form.Form(cfg);
37401 if ( this.form.allItems.length) {
37402 this.form.render(el.dom);
37406 // should only have one of theses..
37407 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37408 // views.. should not be just added - used named prop 'view''
37410 cfg.el = this.el.appendChild(document.createElement("div"));
37413 var ret = new Roo.factory(cfg);
37415 ret.render && ret.render(false, ''); // render blank..
37425 * @class Roo.bootstrap.panel.Grid
37426 * @extends Roo.bootstrap.panel.Content
37428 * Create a new GridPanel.
37429 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37430 * @param {Object} config A the config object
37436 Roo.bootstrap.panel.Grid = function(config)
37440 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37441 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37443 config.el = this.wrapper;
37444 //this.el = this.wrapper;
37446 if (config.container) {
37447 // ctor'ed from a Border/panel.grid
37450 this.wrapper.setStyle("overflow", "hidden");
37451 this.wrapper.addClass('roo-grid-container');
37456 if(config.toolbar){
37457 var tool_el = this.wrapper.createChild();
37458 this.toolbar = Roo.factory(config.toolbar);
37460 if (config.toolbar.items) {
37461 ti = config.toolbar.items ;
37462 delete config.toolbar.items ;
37466 this.toolbar.render(tool_el);
37467 for(var i =0;i < ti.length;i++) {
37468 // Roo.log(['add child', items[i]]);
37469 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37471 this.toolbar.items = nitems;
37473 delete config.toolbar;
37476 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37477 config.grid.scrollBody = true;;
37478 config.grid.monitorWindowResize = false; // turn off autosizing
37479 config.grid.autoHeight = false;
37480 config.grid.autoWidth = false;
37482 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37484 if (config.background) {
37485 // render grid on panel activation (if panel background)
37486 this.on('activate', function(gp) {
37487 if (!gp.grid.rendered) {
37488 gp.grid.render(this.wrapper);
37489 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37494 this.grid.render(this.wrapper);
37495 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37498 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37499 // ??? needed ??? config.el = this.wrapper;
37504 // xtype created footer. - not sure if will work as we normally have to render first..
37505 if (this.footer && !this.footer.el && this.footer.xtype) {
37507 var ctr = this.grid.getView().getFooterPanel(true);
37508 this.footer.dataSource = this.grid.dataSource;
37509 this.footer = Roo.factory(this.footer, Roo);
37510 this.footer.render(ctr);
37520 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37521 getId : function(){
37522 return this.grid.id;
37526 * Returns the grid for this panel
37527 * @return {Roo.bootstrap.Table}
37529 getGrid : function(){
37533 setSize : function(width, height){
37534 if(!this.ignoreResize(width, height)){
37535 var grid = this.grid;
37536 var size = this.adjustForComponents(width, height);
37537 var gridel = grid.getGridEl();
37538 gridel.setSize(size.width, size.height);
37540 var thd = grid.getGridEl().select('thead',true).first();
37541 var tbd = grid.getGridEl().select('tbody', true).first();
37543 tbd.setSize(width, height - thd.getHeight());
37552 beforeSlide : function(){
37553 this.grid.getView().scroller.clip();
37556 afterSlide : function(){
37557 this.grid.getView().scroller.unclip();
37560 destroy : function(){
37561 this.grid.destroy();
37563 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37568 * @class Roo.bootstrap.panel.Nest
37569 * @extends Roo.bootstrap.panel.Content
37571 * Create a new Panel, that can contain a layout.Border.
37574 * @param {Roo.BorderLayout} layout The layout for this panel
37575 * @param {String/Object} config A string to set only the title or a config object
37577 Roo.bootstrap.panel.Nest = function(config)
37579 // construct with only one argument..
37580 /* FIXME - implement nicer consturctors
37581 if (layout.layout) {
37583 layout = config.layout;
37584 delete config.layout;
37586 if (layout.xtype && !layout.getEl) {
37587 // then layout needs constructing..
37588 layout = Roo.factory(layout, Roo);
37592 config.el = config.layout.getEl();
37594 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37596 config.layout.monitorWindowResize = false; // turn off autosizing
37597 this.layout = config.layout;
37598 this.layout.getEl().addClass("roo-layout-nested-layout");
37605 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37607 setSize : function(width, height){
37608 if(!this.ignoreResize(width, height)){
37609 var size = this.adjustForComponents(width, height);
37610 var el = this.layout.getEl();
37611 if (size.height < 1) {
37612 el.setWidth(size.width);
37614 el.setSize(size.width, size.height);
37616 var touch = el.dom.offsetWidth;
37617 this.layout.layout();
37618 // ie requires a double layout on the first pass
37619 if(Roo.isIE && !this.initialized){
37620 this.initialized = true;
37621 this.layout.layout();
37626 // activate all subpanels if not currently active..
37628 setActiveState : function(active){
37629 this.active = active;
37630 this.setActiveClass(active);
37633 this.fireEvent("deactivate", this);
37637 this.fireEvent("activate", this);
37638 // not sure if this should happen before or after..
37639 if (!this.layout) {
37640 return; // should not happen..
37643 for (var r in this.layout.regions) {
37644 reg = this.layout.getRegion(r);
37645 if (reg.getActivePanel()) {
37646 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37647 reg.setActivePanel(reg.getActivePanel());
37650 if (!reg.panels.length) {
37653 reg.showPanel(reg.getPanel(0));
37662 * Returns the nested BorderLayout for this panel
37663 * @return {Roo.BorderLayout}
37665 getLayout : function(){
37666 return this.layout;
37670 * Adds a xtype elements to the layout of the nested panel
37674 xtype : 'ContentPanel',
37681 xtype : 'NestedLayoutPanel',
37687 items : [ ... list of content panels or nested layout panels.. ]
37691 * @param {Object} cfg Xtype definition of item to add.
37693 addxtype : function(cfg) {
37694 return this.layout.addxtype(cfg);
37699 * Ext JS Library 1.1.1
37700 * Copyright(c) 2006-2007, Ext JS, LLC.
37702 * Originally Released Under LGPL - original licence link has changed is not relivant.
37705 * <script type="text/javascript">
37708 * @class Roo.TabPanel
37709 * @extends Roo.util.Observable
37710 * A lightweight tab container.
37714 // basic tabs 1, built from existing content
37715 var tabs = new Roo.TabPanel("tabs1");
37716 tabs.addTab("script", "View Script");
37717 tabs.addTab("markup", "View Markup");
37718 tabs.activate("script");
37720 // more advanced tabs, built from javascript
37721 var jtabs = new Roo.TabPanel("jtabs");
37722 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37724 // set up the UpdateManager
37725 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37726 var updater = tab2.getUpdateManager();
37727 updater.setDefaultUrl("ajax1.htm");
37728 tab2.on('activate', updater.refresh, updater, true);
37730 // Use setUrl for Ajax loading
37731 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37732 tab3.setUrl("ajax2.htm", null, true);
37735 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37738 jtabs.activate("jtabs-1");
37741 * Create a new TabPanel.
37742 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37743 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37745 Roo.bootstrap.panel.Tabs = function(config){
37747 * The container element for this TabPanel.
37748 * @type Roo.Element
37750 this.el = Roo.get(config.el);
37753 if(typeof config == "boolean"){
37754 this.tabPosition = config ? "bottom" : "top";
37756 Roo.apply(this, config);
37760 if(this.tabPosition == "bottom"){
37761 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37762 this.el.addClass("roo-tabs-bottom");
37764 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37765 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37766 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37768 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37770 if(this.tabPosition != "bottom"){
37771 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37772 * @type Roo.Element
37774 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37775 this.el.addClass("roo-tabs-top");
37779 this.bodyEl.setStyle("position", "relative");
37781 this.active = null;
37782 this.activateDelegate = this.activate.createDelegate(this);
37787 * Fires when the active tab changes
37788 * @param {Roo.TabPanel} this
37789 * @param {Roo.TabPanelItem} activePanel The new active tab
37793 * @event beforetabchange
37794 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37795 * @param {Roo.TabPanel} this
37796 * @param {Object} e Set cancel to true on this object to cancel the tab change
37797 * @param {Roo.TabPanelItem} tab The tab being changed to
37799 "beforetabchange" : true
37802 Roo.EventManager.onWindowResize(this.onResize, this);
37803 this.cpad = this.el.getPadding("lr");
37804 this.hiddenCount = 0;
37807 // toolbar on the tabbar support...
37808 if (this.toolbar) {
37809 alert("no toolbar support yet");
37810 this.toolbar = false;
37812 var tcfg = this.toolbar;
37813 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37814 this.toolbar = new Roo.Toolbar(tcfg);
37815 if (Roo.isSafari) {
37816 var tbl = tcfg.container.child('table', true);
37817 tbl.setAttribute('width', '100%');
37825 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37828 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37830 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37832 tabPosition : "top",
37834 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37836 currentTabWidth : 0,
37838 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37842 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37846 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37848 preferredTabWidth : 175,
37850 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37852 resizeTabs : false,
37854 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37856 monitorResize : true,
37858 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37863 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37864 * @param {String} id The id of the div to use <b>or create</b>
37865 * @param {String} text The text for the tab
37866 * @param {String} content (optional) Content to put in the TabPanelItem body
37867 * @param {Boolean} closable (optional) True to create a close icon on the tab
37868 * @return {Roo.TabPanelItem} The created TabPanelItem
37870 addTab : function(id, text, content, closable, tpl)
37872 var item = new Roo.bootstrap.panel.TabItem({
37876 closable : closable,
37879 this.addTabItem(item);
37881 item.setContent(content);
37887 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37888 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37889 * @return {Roo.TabPanelItem}
37891 getTab : function(id){
37892 return this.items[id];
37896 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37897 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37899 hideTab : function(id){
37900 var t = this.items[id];
37903 this.hiddenCount++;
37904 this.autoSizeTabs();
37909 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37910 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37912 unhideTab : function(id){
37913 var t = this.items[id];
37915 t.setHidden(false);
37916 this.hiddenCount--;
37917 this.autoSizeTabs();
37922 * Adds an existing {@link Roo.TabPanelItem}.
37923 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37925 addTabItem : function(item){
37926 this.items[item.id] = item;
37927 this.items.push(item);
37928 // if(this.resizeTabs){
37929 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37930 // this.autoSizeTabs();
37932 // item.autoSize();
37937 * Removes a {@link Roo.TabPanelItem}.
37938 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37940 removeTab : function(id){
37941 var items = this.items;
37942 var tab = items[id];
37943 if(!tab) { return; }
37944 var index = items.indexOf(tab);
37945 if(this.active == tab && items.length > 1){
37946 var newTab = this.getNextAvailable(index);
37951 this.stripEl.dom.removeChild(tab.pnode.dom);
37952 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37953 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37955 items.splice(index, 1);
37956 delete this.items[tab.id];
37957 tab.fireEvent("close", tab);
37958 tab.purgeListeners();
37959 this.autoSizeTabs();
37962 getNextAvailable : function(start){
37963 var items = this.items;
37965 // look for a next tab that will slide over to
37966 // replace the one being removed
37967 while(index < items.length){
37968 var item = items[++index];
37969 if(item && !item.isHidden()){
37973 // if one isn't found select the previous tab (on the left)
37976 var item = items[--index];
37977 if(item && !item.isHidden()){
37985 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37986 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37988 disableTab : function(id){
37989 var tab = this.items[id];
37990 if(tab && this.active != tab){
37996 * Enables a {@link Roo.TabPanelItem} that is disabled.
37997 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37999 enableTab : function(id){
38000 var tab = this.items[id];
38005 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38006 * @param {String/Number} id The id or index of the TabPanelItem to activate.
38007 * @return {Roo.TabPanelItem} The TabPanelItem.
38009 activate : function(id){
38010 var tab = this.items[id];
38014 if(tab == this.active || tab.disabled){
38018 this.fireEvent("beforetabchange", this, e, tab);
38019 if(e.cancel !== true && !tab.disabled){
38021 this.active.hide();
38023 this.active = this.items[id];
38024 this.active.show();
38025 this.fireEvent("tabchange", this, this.active);
38031 * Gets the active {@link Roo.TabPanelItem}.
38032 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38034 getActiveTab : function(){
38035 return this.active;
38039 * Updates the tab body element to fit the height of the container element
38040 * for overflow scrolling
38041 * @param {Number} targetHeight (optional) Override the starting height from the elements height
38043 syncHeight : function(targetHeight){
38044 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38045 var bm = this.bodyEl.getMargins();
38046 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38047 this.bodyEl.setHeight(newHeight);
38051 onResize : function(){
38052 if(this.monitorResize){
38053 this.autoSizeTabs();
38058 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38060 beginUpdate : function(){
38061 this.updating = true;
38065 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38067 endUpdate : function(){
38068 this.updating = false;
38069 this.autoSizeTabs();
38073 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38075 autoSizeTabs : function(){
38076 var count = this.items.length;
38077 var vcount = count - this.hiddenCount;
38078 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38081 var w = Math.max(this.el.getWidth() - this.cpad, 10);
38082 var availWidth = Math.floor(w / vcount);
38083 var b = this.stripBody;
38084 if(b.getWidth() > w){
38085 var tabs = this.items;
38086 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38087 if(availWidth < this.minTabWidth){
38088 /*if(!this.sleft){ // incomplete scrolling code
38089 this.createScrollButtons();
38092 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38095 if(this.currentTabWidth < this.preferredTabWidth){
38096 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38102 * Returns the number of tabs in this TabPanel.
38105 getCount : function(){
38106 return this.items.length;
38110 * Resizes all the tabs to the passed width
38111 * @param {Number} The new width
38113 setTabWidth : function(width){
38114 this.currentTabWidth = width;
38115 for(var i = 0, len = this.items.length; i < len; i++) {
38116 if(!this.items[i].isHidden()) {
38117 this.items[i].setWidth(width);
38123 * Destroys this TabPanel
38124 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38126 destroy : function(removeEl){
38127 Roo.EventManager.removeResizeListener(this.onResize, this);
38128 for(var i = 0, len = this.items.length; i < len; i++){
38129 this.items[i].purgeListeners();
38131 if(removeEl === true){
38132 this.el.update("");
38137 createStrip : function(container)
38139 var strip = document.createElement("nav");
38140 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38141 container.appendChild(strip);
38145 createStripList : function(strip)
38147 // div wrapper for retard IE
38148 // returns the "tr" element.
38149 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38150 //'<div class="x-tabs-strip-wrap">'+
38151 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38152 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38153 return strip.firstChild; //.firstChild.firstChild.firstChild;
38155 createBody : function(container)
38157 var body = document.createElement("div");
38158 Roo.id(body, "tab-body");
38159 //Roo.fly(body).addClass("x-tabs-body");
38160 Roo.fly(body).addClass("tab-content");
38161 container.appendChild(body);
38164 createItemBody :function(bodyEl, id){
38165 var body = Roo.getDom(id);
38167 body = document.createElement("div");
38170 //Roo.fly(body).addClass("x-tabs-item-body");
38171 Roo.fly(body).addClass("tab-pane");
38172 bodyEl.insertBefore(body, bodyEl.firstChild);
38176 createStripElements : function(stripEl, text, closable, tpl)
38178 var td = document.createElement("li"); // was td..
38181 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38184 stripEl.appendChild(td);
38186 td.className = "x-tabs-closable";
38187 if(!this.closeTpl){
38188 this.closeTpl = new Roo.Template(
38189 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38190 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38191 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38194 var el = this.closeTpl.overwrite(td, {"text": text});
38195 var close = el.getElementsByTagName("div")[0];
38196 var inner = el.getElementsByTagName("em")[0];
38197 return {"el": el, "close": close, "inner": inner};
38200 // not sure what this is..
38201 // if(!this.tabTpl){
38202 //this.tabTpl = new Roo.Template(
38203 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38204 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38206 // this.tabTpl = new Roo.Template(
38207 // '<a href="#">' +
38208 // '<span unselectable="on"' +
38209 // (this.disableTooltips ? '' : ' title="{text}"') +
38210 // ' >{text}</span></a>'
38216 var template = tpl || this.tabTpl || false;
38220 template = new Roo.Template(
38222 '<span unselectable="on"' +
38223 (this.disableTooltips ? '' : ' title="{text}"') +
38224 ' >{text}</span></a>'
38228 switch (typeof(template)) {
38232 template = new Roo.Template(template);
38238 var el = template.overwrite(td, {"text": text});
38240 var inner = el.getElementsByTagName("span")[0];
38242 return {"el": el, "inner": inner};
38250 * @class Roo.TabPanelItem
38251 * @extends Roo.util.Observable
38252 * Represents an individual item (tab plus body) in a TabPanel.
38253 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38254 * @param {String} id The id of this TabPanelItem
38255 * @param {String} text The text for the tab of this TabPanelItem
38256 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38258 Roo.bootstrap.panel.TabItem = function(config){
38260 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38261 * @type Roo.TabPanel
38263 this.tabPanel = config.panel;
38265 * The id for this TabPanelItem
38268 this.id = config.id;
38270 this.disabled = false;
38272 this.text = config.text;
38274 this.loaded = false;
38275 this.closable = config.closable;
38278 * The body element for this TabPanelItem.
38279 * @type Roo.Element
38281 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38282 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38283 this.bodyEl.setStyle("display", "block");
38284 this.bodyEl.setStyle("zoom", "1");
38285 //this.hideAction();
38287 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38289 this.el = Roo.get(els.el);
38290 this.inner = Roo.get(els.inner, true);
38291 this.textEl = Roo.get(this.el.dom.firstChild, true);
38292 this.pnode = Roo.get(els.el.parentNode, true);
38293 // this.el.on("mousedown", this.onTabMouseDown, this);
38294 this.el.on("click", this.onTabClick, this);
38296 if(config.closable){
38297 var c = Roo.get(els.close, true);
38298 c.dom.title = this.closeText;
38299 c.addClassOnOver("close-over");
38300 c.on("click", this.closeClick, this);
38306 * Fires when this tab becomes the active tab.
38307 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38308 * @param {Roo.TabPanelItem} this
38312 * @event beforeclose
38313 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38314 * @param {Roo.TabPanelItem} this
38315 * @param {Object} e Set cancel to true on this object to cancel the close.
38317 "beforeclose": true,
38320 * Fires when this tab is closed.
38321 * @param {Roo.TabPanelItem} this
38325 * @event deactivate
38326 * Fires when this tab is no longer the active tab.
38327 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38328 * @param {Roo.TabPanelItem} this
38330 "deactivate" : true
38332 this.hidden = false;
38334 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38337 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38339 purgeListeners : function(){
38340 Roo.util.Observable.prototype.purgeListeners.call(this);
38341 this.el.removeAllListeners();
38344 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38347 this.pnode.addClass("active");
38350 this.tabPanel.stripWrap.repaint();
38352 this.fireEvent("activate", this.tabPanel, this);
38356 * Returns true if this tab is the active tab.
38357 * @return {Boolean}
38359 isActive : function(){
38360 return this.tabPanel.getActiveTab() == this;
38364 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38367 this.pnode.removeClass("active");
38369 this.fireEvent("deactivate", this.tabPanel, this);
38372 hideAction : function(){
38373 this.bodyEl.hide();
38374 this.bodyEl.setStyle("position", "absolute");
38375 this.bodyEl.setLeft("-20000px");
38376 this.bodyEl.setTop("-20000px");
38379 showAction : function(){
38380 this.bodyEl.setStyle("position", "relative");
38381 this.bodyEl.setTop("");
38382 this.bodyEl.setLeft("");
38383 this.bodyEl.show();
38387 * Set the tooltip for the tab.
38388 * @param {String} tooltip The tab's tooltip
38390 setTooltip : function(text){
38391 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38392 this.textEl.dom.qtip = text;
38393 this.textEl.dom.removeAttribute('title');
38395 this.textEl.dom.title = text;
38399 onTabClick : function(e){
38400 e.preventDefault();
38401 this.tabPanel.activate(this.id);
38404 onTabMouseDown : function(e){
38405 e.preventDefault();
38406 this.tabPanel.activate(this.id);
38409 getWidth : function(){
38410 return this.inner.getWidth();
38413 setWidth : function(width){
38414 var iwidth = width - this.pnode.getPadding("lr");
38415 this.inner.setWidth(iwidth);
38416 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38417 this.pnode.setWidth(width);
38421 * Show or hide the tab
38422 * @param {Boolean} hidden True to hide or false to show.
38424 setHidden : function(hidden){
38425 this.hidden = hidden;
38426 this.pnode.setStyle("display", hidden ? "none" : "");
38430 * Returns true if this tab is "hidden"
38431 * @return {Boolean}
38433 isHidden : function(){
38434 return this.hidden;
38438 * Returns the text for this tab
38441 getText : function(){
38445 autoSize : function(){
38446 //this.el.beginMeasure();
38447 this.textEl.setWidth(1);
38449 * #2804 [new] Tabs in Roojs
38450 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38452 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38453 //this.el.endMeasure();
38457 * Sets the text for the tab (Note: this also sets the tooltip text)
38458 * @param {String} text The tab's text and tooltip
38460 setText : function(text){
38462 this.textEl.update(text);
38463 this.setTooltip(text);
38464 //if(!this.tabPanel.resizeTabs){
38465 // this.autoSize();
38469 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38471 activate : function(){
38472 this.tabPanel.activate(this.id);
38476 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38478 disable : function(){
38479 if(this.tabPanel.active != this){
38480 this.disabled = true;
38481 this.pnode.addClass("disabled");
38486 * Enables this TabPanelItem if it was previously disabled.
38488 enable : function(){
38489 this.disabled = false;
38490 this.pnode.removeClass("disabled");
38494 * Sets the content for this TabPanelItem.
38495 * @param {String} content The content
38496 * @param {Boolean} loadScripts true to look for and load scripts
38498 setContent : function(content, loadScripts){
38499 this.bodyEl.update(content, loadScripts);
38503 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38504 * @return {Roo.UpdateManager} The UpdateManager
38506 getUpdateManager : function(){
38507 return this.bodyEl.getUpdateManager();
38511 * Set a URL to be used to load the content for this TabPanelItem.
38512 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38513 * @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)
38514 * @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)
38515 * @return {Roo.UpdateManager} The UpdateManager
38517 setUrl : function(url, params, loadOnce){
38518 if(this.refreshDelegate){
38519 this.un('activate', this.refreshDelegate);
38521 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38522 this.on("activate", this.refreshDelegate);
38523 return this.bodyEl.getUpdateManager();
38527 _handleRefresh : function(url, params, loadOnce){
38528 if(!loadOnce || !this.loaded){
38529 var updater = this.bodyEl.getUpdateManager();
38530 updater.update(url, params, this._setLoaded.createDelegate(this));
38535 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38536 * Will fail silently if the setUrl method has not been called.
38537 * This does not activate the panel, just updates its content.
38539 refresh : function(){
38540 if(this.refreshDelegate){
38541 this.loaded = false;
38542 this.refreshDelegate();
38547 _setLoaded : function(){
38548 this.loaded = true;
38552 closeClick : function(e){
38555 this.fireEvent("beforeclose", this, o);
38556 if(o.cancel !== true){
38557 this.tabPanel.removeTab(this.id);
38561 * The text displayed in the tooltip for the close icon.
38564 closeText : "Close this tab"
38567 * This script refer to:
38568 * Title: International Telephone Input
38569 * Author: Jack O'Connor
38570 * Code version: v12.1.12
38571 * Availability: https://github.com/jackocnr/intl-tel-input.git
38574 Roo.bootstrap.PhoneInputData = function() {
38577 "Afghanistan (افغانستان)",
38582 "Albania (Shqipëri)",
38587 "Algeria (الجزائر)",
38612 "Antigua and Barbuda",
38622 "Armenia (Հայաստան)",
38638 "Austria (Österreich)",
38643 "Azerbaijan (Azərbaycan)",
38653 "Bahrain (البحرين)",
38658 "Bangladesh (বাংলাদেশ)",
38668 "Belarus (Беларусь)",
38673 "Belgium (België)",
38703 "Bosnia and Herzegovina (Босна и Херцеговина)",
38718 "British Indian Ocean Territory",
38723 "British Virgin Islands",
38733 "Bulgaria (България)",
38743 "Burundi (Uburundi)",
38748 "Cambodia (កម្ពុជា)",
38753 "Cameroon (Cameroun)",
38762 ["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"]
38765 "Cape Verde (Kabu Verdi)",
38770 "Caribbean Netherlands",
38781 "Central African Republic (République centrafricaine)",
38801 "Christmas Island",
38807 "Cocos (Keeling) Islands",
38818 "Comoros (جزر القمر)",
38823 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38828 "Congo (Republic) (Congo-Brazzaville)",
38848 "Croatia (Hrvatska)",
38869 "Czech Republic (Česká republika)",
38874 "Denmark (Danmark)",
38889 "Dominican Republic (República Dominicana)",
38893 ["809", "829", "849"]
38911 "Equatorial Guinea (Guinea Ecuatorial)",
38931 "Falkland Islands (Islas Malvinas)",
38936 "Faroe Islands (Føroyar)",
38957 "French Guiana (Guyane française)",
38962 "French Polynesia (Polynésie française)",
38977 "Georgia (საქართველო)",
38982 "Germany (Deutschland)",
39002 "Greenland (Kalaallit Nunaat)",
39039 "Guinea-Bissau (Guiné Bissau)",
39064 "Hungary (Magyarország)",
39069 "Iceland (Ísland)",
39089 "Iraq (العراق)",
39105 "Israel (ישראל)",
39132 "Jordan (الأردن)",
39137 "Kazakhstan (Казахстан)",
39158 "Kuwait (الكويت)",
39163 "Kyrgyzstan (Кыргызстан)",
39173 "Latvia (Latvija)",
39178 "Lebanon (لبنان)",
39193 "Libya (ليبيا)",
39203 "Lithuania (Lietuva)",
39218 "Macedonia (FYROM) (Македонија)",
39223 "Madagascar (Madagasikara)",
39253 "Marshall Islands",
39263 "Mauritania (موريتانيا)",
39268 "Mauritius (Moris)",
39289 "Moldova (Republica Moldova)",
39299 "Mongolia (Монгол)",
39304 "Montenegro (Crna Gora)",
39314 "Morocco (المغرب)",
39320 "Mozambique (Moçambique)",
39325 "Myanmar (Burma) (မြန်မာ)",
39330 "Namibia (Namibië)",
39345 "Netherlands (Nederland)",
39350 "New Caledonia (Nouvelle-Calédonie)",
39385 "North Korea (조선 민주주의 인민 공화국)",
39390 "Northern Mariana Islands",
39406 "Pakistan (پاکستان)",
39416 "Palestine (فلسطين)",
39426 "Papua New Guinea",
39468 "Réunion (La Réunion)",
39474 "Romania (România)",
39490 "Saint Barthélemy",
39501 "Saint Kitts and Nevis",
39511 "Saint Martin (Saint-Martin (partie française))",
39517 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39522 "Saint Vincent and the Grenadines",
39537 "São Tomé and Príncipe (São Tomé e Príncipe)",
39542 "Saudi Arabia (المملكة العربية السعودية)",
39547 "Senegal (Sénégal)",
39577 "Slovakia (Slovensko)",
39582 "Slovenia (Slovenija)",
39592 "Somalia (Soomaaliya)",
39602 "South Korea (대한민국)",
39607 "South Sudan (جنوب السودان)",
39617 "Sri Lanka (ශ්රී ලංකාව)",
39622 "Sudan (السودان)",
39632 "Svalbard and Jan Mayen",
39643 "Sweden (Sverige)",
39648 "Switzerland (Schweiz)",
39653 "Syria (سوريا)",
39698 "Trinidad and Tobago",
39703 "Tunisia (تونس)",
39708 "Turkey (Türkiye)",
39718 "Turks and Caicos Islands",
39728 "U.S. Virgin Islands",
39738 "Ukraine (Україна)",
39743 "United Arab Emirates (الإمارات العربية المتحدة)",
39765 "Uzbekistan (Oʻzbekiston)",
39775 "Vatican City (Città del Vaticano)",
39786 "Vietnam (Việt Nam)",
39791 "Wallis and Futuna (Wallis-et-Futuna)",
39796 "Western Sahara (الصحراء الغربية)",
39802 "Yemen (اليمن)",
39826 * This script refer to:
39827 * Title: International Telephone Input
39828 * Author: Jack O'Connor
39829 * Code version: v12.1.12
39830 * Availability: https://github.com/jackocnr/intl-tel-input.git
39834 * @class Roo.bootstrap.PhoneInput
39835 * @extends Roo.bootstrap.TriggerField
39836 * An input with International dial-code selection
39838 * @cfg {String} defaultDialCode default '+852'
39839 * @cfg {Array} preferedCountries default []
39842 * Create a new PhoneInput.
39843 * @param {Object} config Configuration options
39846 Roo.bootstrap.PhoneInput = function(config) {
39847 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39850 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39852 listWidth: undefined,
39854 selectedClass: 'active',
39856 invalidClass : "has-warning",
39858 validClass: 'has-success',
39860 allowed: '0123456789',
39863 * @cfg {String} defaultDialCode The default dial code when initializing the input
39865 defaultDialCode: '+852',
39868 * @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
39870 preferedCountries: false,
39872 getAutoCreate : function()
39874 var data = Roo.bootstrap.PhoneInputData();
39875 var align = this.labelAlign || this.parentLabelAlign();
39878 this.allCountries = [];
39879 this.dialCodeMapping = [];
39881 for (var i = 0; i < data.length; i++) {
39883 this.allCountries[i] = {
39887 priority: c[3] || 0,
39888 areaCodes: c[4] || null
39890 this.dialCodeMapping[c[2]] = {
39893 priority: c[3] || 0,
39894 areaCodes: c[4] || null
39906 cls : 'form-control tel-input',
39907 autocomplete: 'new-password'
39910 var hiddenInput = {
39913 cls: 'hidden-tel-input'
39917 hiddenInput.name = this.name;
39920 if (this.disabled) {
39921 input.disabled = true;
39924 var flag_container = {
39941 cls: this.hasFeedback ? 'has-feedback' : '',
39947 cls: 'dial-code-holder',
39954 cls: 'roo-select2-container input-group',
39961 if (this.fieldLabel.length) {
39964 tooltip: 'This field is required'
39970 cls: 'control-label',
39976 html: this.fieldLabel
39979 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39985 if(this.indicatorpos == 'right') {
39986 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39993 if(align == 'left') {
40001 if(this.labelWidth > 12){
40002 label.style = "width: " + this.labelWidth + 'px';
40004 if(this.labelWidth < 13 && this.labelmd == 0){
40005 this.labelmd = this.labelWidth;
40007 if(this.labellg > 0){
40008 label.cls += ' col-lg-' + this.labellg;
40009 input.cls += ' col-lg-' + (12 - this.labellg);
40011 if(this.labelmd > 0){
40012 label.cls += ' col-md-' + this.labelmd;
40013 container.cls += ' col-md-' + (12 - this.labelmd);
40015 if(this.labelsm > 0){
40016 label.cls += ' col-sm-' + this.labelsm;
40017 container.cls += ' col-sm-' + (12 - this.labelsm);
40019 if(this.labelxs > 0){
40020 label.cls += ' col-xs-' + this.labelxs;
40021 container.cls += ' col-xs-' + (12 - this.labelxs);
40031 var settings = this;
40033 ['xs','sm','md','lg'].map(function(size){
40034 if (settings[size]) {
40035 cfg.cls += ' col-' + size + '-' + settings[size];
40039 this.store = new Roo.data.Store({
40040 proxy : new Roo.data.MemoryProxy({}),
40041 reader : new Roo.data.JsonReader({
40052 'name' : 'dialCode',
40056 'name' : 'priority',
40060 'name' : 'areaCodes',
40067 if(!this.preferedCountries) {
40068 this.preferedCountries = [
40075 var p = this.preferedCountries.reverse();
40078 for (var i = 0; i < p.length; i++) {
40079 for (var j = 0; j < this.allCountries.length; j++) {
40080 if(this.allCountries[j].iso2 == p[i]) {
40081 var t = this.allCountries[j];
40082 this.allCountries.splice(j,1);
40083 this.allCountries.unshift(t);
40089 this.store.proxy.data = {
40091 data: this.allCountries
40097 initEvents : function()
40100 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40102 this.indicator = this.indicatorEl();
40103 this.flag = this.flagEl();
40104 this.dialCodeHolder = this.dialCodeHolderEl();
40106 this.trigger = this.el.select('div.flag-box',true).first();
40107 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40112 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40113 _this.list.setWidth(lw);
40116 this.list.on('mouseover', this.onViewOver, this);
40117 this.list.on('mousemove', this.onViewMove, this);
40118 this.inputEl().on("keyup", this.onKeyUp, this);
40120 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40122 this.view = new Roo.View(this.list, this.tpl, {
40123 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40126 this.view.on('click', this.onViewClick, this);
40127 this.setValue(this.defaultDialCode);
40130 onTriggerClick : function(e)
40132 Roo.log('trigger click');
40137 if(this.isExpanded()){
40139 this.hasFocus = false;
40141 this.store.load({});
40142 this.hasFocus = true;
40147 isExpanded : function()
40149 return this.list.isVisible();
40152 collapse : function()
40154 if(!this.isExpanded()){
40158 Roo.get(document).un('mousedown', this.collapseIf, this);
40159 Roo.get(document).un('mousewheel', this.collapseIf, this);
40160 this.fireEvent('collapse', this);
40164 expand : function()
40168 if(this.isExpanded() || !this.hasFocus){
40172 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40173 this.list.setWidth(lw);
40176 this.restrictHeight();
40178 Roo.get(document).on('mousedown', this.collapseIf, this);
40179 Roo.get(document).on('mousewheel', this.collapseIf, this);
40181 this.fireEvent('expand', this);
40184 restrictHeight : function()
40186 this.list.alignTo(this.inputEl(), this.listAlign);
40187 this.list.alignTo(this.inputEl(), this.listAlign);
40190 onViewOver : function(e, t)
40192 if(this.inKeyMode){
40195 var item = this.view.findItemFromChild(t);
40198 var index = this.view.indexOf(item);
40199 this.select(index, false);
40204 onViewClick : function(view, doFocus, el, e)
40206 var index = this.view.getSelectedIndexes()[0];
40208 var r = this.store.getAt(index);
40211 this.onSelect(r, index);
40213 if(doFocus !== false && !this.blockFocus){
40214 this.inputEl().focus();
40218 onViewMove : function(e, t)
40220 this.inKeyMode = false;
40223 select : function(index, scrollIntoView)
40225 this.selectedIndex = index;
40226 this.view.select(index);
40227 if(scrollIntoView !== false){
40228 var el = this.view.getNode(index);
40230 this.list.scrollChildIntoView(el, false);
40235 createList : function()
40237 this.list = Roo.get(document.body).createChild({
40239 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40240 style: 'display:none'
40243 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40246 collapseIf : function(e)
40248 var in_combo = e.within(this.el);
40249 var in_list = e.within(this.list);
40250 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40252 if (in_combo || in_list || is_list) {
40258 onSelect : function(record, index)
40260 if(this.fireEvent('beforeselect', this, record, index) !== false){
40262 this.setFlagClass(record.data.iso2);
40263 this.setDialCode(record.data.dialCode);
40264 this.hasFocus = false;
40266 this.fireEvent('select', this, record, index);
40270 flagEl : function()
40272 var flag = this.el.select('div.flag',true).first();
40279 dialCodeHolderEl : function()
40281 var d = this.el.select('input.dial-code-holder',true).first();
40288 setDialCode : function(v)
40290 this.dialCodeHolder.dom.value = '+'+v;
40293 setFlagClass : function(n)
40295 this.flag.dom.className = 'flag '+n;
40298 getValue : function()
40300 var v = this.inputEl().getValue();
40301 if(this.dialCodeHolder) {
40302 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40307 setValue : function(v)
40309 var d = this.getDialCode(v);
40311 //invalid dial code
40312 if(v.length == 0 || !d || d.length == 0) {
40314 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40315 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40321 this.setFlagClass(this.dialCodeMapping[d].iso2);
40322 this.setDialCode(d);
40323 this.inputEl().dom.value = v.replace('+'+d,'');
40324 this.hiddenEl().dom.value = this.getValue();
40329 getDialCode : function(v)
40333 if (v.length == 0) {
40334 return this.dialCodeHolder.dom.value;
40338 if (v.charAt(0) != "+") {
40341 var numericChars = "";
40342 for (var i = 1; i < v.length; i++) {
40343 var c = v.charAt(i);
40346 if (this.dialCodeMapping[numericChars]) {
40347 dialCode = v.substr(1, i);
40349 if (numericChars.length == 4) {
40359 this.setValue(this.defaultDialCode);
40363 hiddenEl : function()
40365 return this.el.select('input.hidden-tel-input',true).first();
40368 onKeyUp : function(e){
40370 var k = e.getKey();
40371 var c = e.getCharCode();
40374 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40375 this.allowed.indexOf(String.fromCharCode(c)) === -1
40380 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40383 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40387 this.setValue(this.getValue());
40392 * @class Roo.bootstrap.MoneyField
40393 * @extends Roo.bootstrap.ComboBox
40394 * Bootstrap MoneyField class
40397 * Create a new MoneyField.
40398 * @param {Object} config Configuration options
40401 Roo.bootstrap.MoneyField = function(config) {
40403 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40407 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40410 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40412 allowDecimals : true,
40414 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40416 decimalSeparator : ".",
40418 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40420 decimalPrecision : 0,
40422 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40424 allowNegative : true,
40426 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40430 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40432 minValue : Number.NEGATIVE_INFINITY,
40434 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40436 maxValue : Number.MAX_VALUE,
40438 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40440 minText : "The minimum value for this field is {0}",
40442 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40444 maxText : "The maximum value for this field is {0}",
40446 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40447 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40449 nanText : "{0} is not a valid number",
40451 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40455 * @cfg {String} defaults currency of the MoneyField
40456 * value should be in lkey
40458 defaultCurrency : false,
40460 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40462 thousandsDelimiter : false,
40472 getAutoCreate : function()
40474 var align = this.labelAlign || this.parentLabelAlign();
40486 cls : 'form-control roo-money-amount-input',
40487 autocomplete: 'new-password'
40490 var hiddenInput = {
40494 cls: 'hidden-number-input'
40498 hiddenInput.name = this.name;
40501 if (this.disabled) {
40502 input.disabled = true;
40505 var clg = 12 - this.inputlg;
40506 var cmd = 12 - this.inputmd;
40507 var csm = 12 - this.inputsm;
40508 var cxs = 12 - this.inputxs;
40512 cls : 'row roo-money-field',
40516 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40520 cls: 'roo-select2-container input-group',
40524 cls : 'form-control roo-money-currency-input',
40525 autocomplete: 'new-password',
40527 name : this.currencyName
40531 cls : 'input-group-addon',
40545 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40549 cls: this.hasFeedback ? 'has-feedback' : '',
40560 if (this.fieldLabel.length) {
40563 tooltip: 'This field is required'
40569 cls: 'control-label',
40575 html: this.fieldLabel
40578 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40584 if(this.indicatorpos == 'right') {
40585 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40592 if(align == 'left') {
40600 if(this.labelWidth > 12){
40601 label.style = "width: " + this.labelWidth + 'px';
40603 if(this.labelWidth < 13 && this.labelmd == 0){
40604 this.labelmd = this.labelWidth;
40606 if(this.labellg > 0){
40607 label.cls += ' col-lg-' + this.labellg;
40608 input.cls += ' col-lg-' + (12 - this.labellg);
40610 if(this.labelmd > 0){
40611 label.cls += ' col-md-' + this.labelmd;
40612 container.cls += ' col-md-' + (12 - this.labelmd);
40614 if(this.labelsm > 0){
40615 label.cls += ' col-sm-' + this.labelsm;
40616 container.cls += ' col-sm-' + (12 - this.labelsm);
40618 if(this.labelxs > 0){
40619 label.cls += ' col-xs-' + this.labelxs;
40620 container.cls += ' col-xs-' + (12 - this.labelxs);
40631 var settings = this;
40633 ['xs','sm','md','lg'].map(function(size){
40634 if (settings[size]) {
40635 cfg.cls += ' col-' + size + '-' + settings[size];
40642 initEvents : function()
40644 this.indicator = this.indicatorEl();
40646 this.initCurrencyEvent();
40648 this.initNumberEvent();
40651 initCurrencyEvent : function()
40654 throw "can not find store for combo";
40657 this.store = Roo.factory(this.store, Roo.data);
40658 this.store.parent = this;
40662 this.triggerEl = this.el.select('.input-group-addon', true).first();
40664 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40669 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40670 _this.list.setWidth(lw);
40673 this.list.on('mouseover', this.onViewOver, this);
40674 this.list.on('mousemove', this.onViewMove, this);
40675 this.list.on('scroll', this.onViewScroll, this);
40678 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40681 this.view = new Roo.View(this.list, this.tpl, {
40682 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40685 this.view.on('click', this.onViewClick, this);
40687 this.store.on('beforeload', this.onBeforeLoad, this);
40688 this.store.on('load', this.onLoad, this);
40689 this.store.on('loadexception', this.onLoadException, this);
40691 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40692 "up" : function(e){
40693 this.inKeyMode = true;
40697 "down" : function(e){
40698 if(!this.isExpanded()){
40699 this.onTriggerClick();
40701 this.inKeyMode = true;
40706 "enter" : function(e){
40709 if(this.fireEvent("specialkey", this, e)){
40710 this.onViewClick(false);
40716 "esc" : function(e){
40720 "tab" : function(e){
40723 if(this.fireEvent("specialkey", this, e)){
40724 this.onViewClick(false);
40732 doRelay : function(foo, bar, hname){
40733 if(hname == 'down' || this.scope.isExpanded()){
40734 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40742 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40746 initNumberEvent : function(e)
40748 this.inputEl().on("keydown" , this.fireKey, this);
40749 this.inputEl().on("focus", this.onFocus, this);
40750 this.inputEl().on("blur", this.onBlur, this);
40752 this.inputEl().relayEvent('keyup', this);
40754 if(this.indicator){
40755 this.indicator.addClass('invisible');
40758 this.originalValue = this.getValue();
40760 if(this.validationEvent == 'keyup'){
40761 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40762 this.inputEl().on('keyup', this.filterValidation, this);
40764 else if(this.validationEvent !== false){
40765 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40768 if(this.selectOnFocus){
40769 this.on("focus", this.preFocus, this);
40772 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40773 this.inputEl().on("keypress", this.filterKeys, this);
40775 this.inputEl().relayEvent('keypress', this);
40778 var allowed = "0123456789";
40780 if(this.allowDecimals){
40781 allowed += this.decimalSeparator;
40784 if(this.allowNegative){
40788 if(this.thousandsDelimiter) {
40792 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40794 var keyPress = function(e){
40796 var k = e.getKey();
40798 var c = e.getCharCode();
40801 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40802 allowed.indexOf(String.fromCharCode(c)) === -1
40808 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40812 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40817 this.inputEl().on("keypress", keyPress, this);
40821 onTriggerClick : function(e)
40828 this.loadNext = false;
40830 if(this.isExpanded()){
40835 this.hasFocus = true;
40837 if(this.triggerAction == 'all') {
40838 this.doQuery(this.allQuery, true);
40842 this.doQuery(this.getRawValue());
40845 getCurrency : function()
40847 var v = this.currencyEl().getValue();
40852 restrictHeight : function()
40854 this.list.alignTo(this.currencyEl(), this.listAlign);
40855 this.list.alignTo(this.currencyEl(), this.listAlign);
40858 onViewClick : function(view, doFocus, el, e)
40860 var index = this.view.getSelectedIndexes()[0];
40862 var r = this.store.getAt(index);
40865 this.onSelect(r, index);
40869 onSelect : function(record, index){
40871 if(this.fireEvent('beforeselect', this, record, index) !== false){
40873 this.setFromCurrencyData(index > -1 ? record.data : false);
40877 this.fireEvent('select', this, record, index);
40881 setFromCurrencyData : function(o)
40885 this.lastCurrency = o;
40887 if (this.currencyField) {
40888 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40890 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40893 this.lastSelectionText = currency;
40895 //setting default currency
40896 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40897 this.setCurrency(this.defaultCurrency);
40901 this.setCurrency(currency);
40904 setFromData : function(o)
40908 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40910 this.setFromCurrencyData(c);
40915 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40917 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40920 this.setValue(value);
40924 setCurrency : function(v)
40926 this.currencyValue = v;
40929 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40934 setValue : function(v)
40936 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40942 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40944 this.inputEl().dom.value = (v == '') ? '' :
40945 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40947 if(!this.allowZero && v === '0') {
40948 this.hiddenEl().dom.value = '';
40949 this.inputEl().dom.value = '';
40956 getRawValue : function()
40958 var v = this.inputEl().getValue();
40963 getValue : function()
40965 return this.fixPrecision(this.parseValue(this.getRawValue()));
40968 parseValue : function(value)
40970 if(this.thousandsDelimiter) {
40972 r = new RegExp(",", "g");
40973 value = value.replace(r, "");
40976 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40977 return isNaN(value) ? '' : value;
40981 fixPrecision : function(value)
40983 if(this.thousandsDelimiter) {
40985 r = new RegExp(",", "g");
40986 value = value.replace(r, "");
40989 var nan = isNaN(value);
40991 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40992 return nan ? '' : value;
40994 return parseFloat(value).toFixed(this.decimalPrecision);
40997 decimalPrecisionFcn : function(v)
40999 return Math.floor(v);
41002 validateValue : function(value)
41004 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41008 var num = this.parseValue(value);
41011 this.markInvalid(String.format(this.nanText, value));
41015 if(num < this.minValue){
41016 this.markInvalid(String.format(this.minText, this.minValue));
41020 if(num > this.maxValue){
41021 this.markInvalid(String.format(this.maxText, this.maxValue));
41028 validate : function()
41030 if(this.disabled || this.allowBlank){
41035 var currency = this.getCurrency();
41037 if(this.validateValue(this.getRawValue()) && currency.length){
41042 this.markInvalid();
41046 getName: function()
41051 beforeBlur : function()
41057 var v = this.parseValue(this.getRawValue());
41064 onBlur : function()
41068 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41069 //this.el.removeClass(this.focusClass);
41072 this.hasFocus = false;
41074 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41078 var v = this.getValue();
41080 if(String(v) !== String(this.startValue)){
41081 this.fireEvent('change', this, v, this.startValue);
41084 this.fireEvent("blur", this);
41087 inputEl : function()
41089 return this.el.select('.roo-money-amount-input', true).first();
41092 currencyEl : function()
41094 return this.el.select('.roo-money-currency-input', true).first();
41097 hiddenEl : function()
41099 return this.el.select('input.hidden-number-input',true).first();