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;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over 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 the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2651 onRender : function(ct, position)
2653 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656 var cfg = Roo.apply({}, this.getAutoCreate());
2659 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661 //if (!cfg.name.length) {
2665 cfg.cls += ' ' + this.cls;
2668 cfg.style = this.style;
2670 this.el = Roo.get(document.body).createChild(cfg, position);
2672 //var type = this.el.dom.type;
2675 if(this.tabIndex !== undefined){
2676 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679 this.dialogEl = this.el.select('.modal-dialog',true).first();
2680 this.bodyEl = this.el.select('.modal-body',true).first();
2681 this.closeEl = this.el.select('.modal-header .close', true).first();
2682 this.headerEl = this.el.select('.modal-header',true).first();
2683 this.titleEl = this.el.select('.modal-title',true).first();
2684 this.footerEl = this.el.select('.modal-footer',true).first();
2686 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688 //this.el.addClass("x-dlg-modal");
2690 if (this.buttons.length) {
2691 Roo.each(this.buttons, function(bb) {
2692 var b = Roo.apply({}, bb);
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.render(this.el.select('.modal-footer div').first());
2705 // render the children.
2708 if(typeof(this.items) != 'undefined'){
2709 var items = this.items;
2712 for(var i =0;i < items.length;i++) {
2713 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717 this.items = nitems;
2719 // where are these used - they used to be body/close/footer
2723 //this.el.addClass([this.fieldClass, this.cls]);
2727 getAutoCreate : function()
2731 html : this.html || ''
2736 cls : 'modal-title',
2740 if(this.specificTitle){
2746 if (this.allow_close) {
2758 if(this.size.length){
2759 size = 'modal-' + this.size;
2766 cls: "modal-dialog " + size,
2769 cls : "modal-content",
2772 cls : 'modal-header',
2777 cls : 'modal-footer',
2781 cls: 'btn-' + this.buttonPosition
2798 modal.cls += ' fade';
2804 getChildContainer : function() {
2809 getButtonContainer : function() {
2810 return this.el.select('.modal-footer div',true).first();
2813 initEvents : function()
2815 if (this.allow_close) {
2816 this.closeEl.on('click', this.hide, this);
2818 Roo.EventManager.onWindowResize(this.resize, this, true);
2825 this.maskEl.setSize(
2826 Roo.lib.Dom.getViewWidth(true),
2827 Roo.lib.Dom.getViewHeight(true)
2830 if (this.fitwindow) {
2832 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2833 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2838 if(this.max_width !== 0) {
2840 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2843 this.setSize(w, this.height);
2847 if(!this.fit_content) {
2848 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2852 var body_childs = this.bodyEl.dom.childNodes;
2853 // does not seem to give enough space...
2854 var full_height = 60 + this.headerEl.getHeight() + this.footerEl.getHeight();
2855 for(var i = 0; i < body_childs.length; i++) {
2856 full_height += body_childs[i].offsetHeight;
2859 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2864 setSize : function(w,h)
2874 if (!this.rendered) {
2878 //this.el.setStyle('display', 'block');
2879 this.el.removeClass('hideing');
2880 this.el.addClass('show');
2882 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2885 this.el.addClass('in');
2888 this.el.addClass('in');
2891 // not sure how we can show data in here..
2893 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2896 Roo.get(document.body).addClass("x-body-masked");
2898 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2899 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2900 this.maskEl.addClass('show');
2904 this.fireEvent('show', this);
2906 // set zindex here - otherwise it appears to be ignored...
2907 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2910 this.items.forEach( function(e) {
2911 e.layout ? e.layout() : false;
2919 if(this.fireEvent("beforehide", this) !== false){
2920 this.maskEl.removeClass('show');
2921 Roo.get(document.body).removeClass("x-body-masked");
2922 this.el.removeClass('in');
2923 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2925 if(this.animate){ // why
2926 this.el.addClass('hideing');
2928 if (!this.el.hasClass('hideing')) {
2929 return; // it's been shown again...
2931 this.el.removeClass('show');
2932 this.el.removeClass('hideing');
2936 this.el.removeClass('show');
2938 this.fireEvent('hide', this);
2941 isVisible : function()
2944 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2948 addButton : function(str, cb)
2952 var b = Roo.apply({}, { html : str } );
2953 b.xns = b.xns || Roo.bootstrap;
2954 b.xtype = b.xtype || 'Button';
2955 if (typeof(b.listeners) == 'undefined') {
2956 b.listeners = { click : cb.createDelegate(this) };
2959 var btn = Roo.factory(b);
2961 btn.render(this.el.select('.modal-footer div').first());
2967 setDefaultButton : function(btn)
2969 //this.el.select('.modal-footer').()
2973 resizeTo: function(w,h)
2977 this.dialogEl.setWidth(w);
2978 if (this.diff === false) {
2979 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2982 this.bodyEl.setHeight(h-this.diff);
2984 this.fireEvent('resize', this);
2987 setContentSize : function(w, h)
2991 onButtonClick: function(btn,e)
2994 this.fireEvent('btnclick', btn.name, e);
2997 * Set the title of the Dialog
2998 * @param {String} str new Title
3000 setTitle: function(str) {
3001 this.titleEl.dom.innerHTML = str;
3004 * Set the body of the Dialog
3005 * @param {String} str new Title
3007 setBody: function(str) {
3008 this.bodyEl.dom.innerHTML = str;
3011 * Set the body of the Dialog using the template
3012 * @param {Obj} data - apply this data to the template and replace the body contents.
3014 applyBody: function(obj)
3017 Roo.log("Error - using apply Body without a template");
3020 this.tmpl.overwrite(this.bodyEl, obj);
3026 Roo.apply(Roo.bootstrap.Modal, {
3028 * Button config that displays a single OK button
3037 * Button config that displays Yes and No buttons
3053 * Button config that displays OK and Cancel buttons
3068 * Button config that displays Yes, No and Cancel buttons
3092 * messagebox - can be used as a replace
3096 * @class Roo.MessageBox
3097 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3101 Roo.Msg.alert('Status', 'Changes saved successfully.');
3103 // Prompt for user data:
3104 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3106 // process text value...
3110 // Show a dialog using config options:
3112 title:'Save Changes?',
3113 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3114 buttons: Roo.Msg.YESNOCANCEL,
3121 Roo.bootstrap.MessageBox = function(){
3122 var dlg, opt, mask, waitTimer;
3123 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3124 var buttons, activeTextEl, bwidth;
3128 var handleButton = function(button){
3130 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3134 var handleHide = function(){
3136 dlg.el.removeClass(opt.cls);
3139 // Roo.TaskMgr.stop(waitTimer);
3140 // waitTimer = null;
3145 var updateButtons = function(b){
3148 buttons["ok"].hide();
3149 buttons["cancel"].hide();
3150 buttons["yes"].hide();
3151 buttons["no"].hide();
3152 //dlg.footer.dom.style.display = 'none';
3155 dlg.footerEl.dom.style.display = '';
3156 for(var k in buttons){
3157 if(typeof buttons[k] != "function"){
3160 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3161 width += buttons[k].el.getWidth()+15;
3171 var handleEsc = function(d, k, e){
3172 if(opt && opt.closable !== false){
3182 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3183 * @return {Roo.BasicDialog} The BasicDialog element
3185 getDialog : function(){
3187 dlg = new Roo.bootstrap.Modal( {
3190 //constraintoviewport:false,
3192 //collapsible : false,
3197 //buttonAlign:"center",
3198 closeClick : function(){
3199 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3202 handleButton("cancel");
3207 dlg.on("hide", handleHide);
3209 //dlg.addKeyListener(27, handleEsc);
3211 this.buttons = buttons;
3212 var bt = this.buttonText;
3213 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3214 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3215 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3216 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3218 bodyEl = dlg.bodyEl.createChild({
3220 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3221 '<textarea class="roo-mb-textarea"></textarea>' +
3222 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3224 msgEl = bodyEl.dom.firstChild;
3225 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3226 textboxEl.enableDisplayMode();
3227 textboxEl.addKeyListener([10,13], function(){
3228 if(dlg.isVisible() && opt && opt.buttons){
3231 }else if(opt.buttons.yes){
3232 handleButton("yes");
3236 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3237 textareaEl.enableDisplayMode();
3238 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3239 progressEl.enableDisplayMode();
3241 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3242 var pf = progressEl.dom.firstChild;
3244 pp = Roo.get(pf.firstChild);
3245 pp.setHeight(pf.offsetHeight);
3253 * Updates the message box body text
3254 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3255 * the XHTML-compliant non-breaking space character '&#160;')
3256 * @return {Roo.MessageBox} This message box
3258 updateText : function(text)
3260 if(!dlg.isVisible() && !opt.width){
3261 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3262 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3264 msgEl.innerHTML = text || ' ';
3266 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3267 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3269 Math.min(opt.width || cw , this.maxWidth),
3270 Math.max(opt.minWidth || this.minWidth, bwidth)
3273 activeTextEl.setWidth(w);
3275 if(dlg.isVisible()){
3276 dlg.fixedcenter = false;
3278 // to big, make it scroll. = But as usual stupid IE does not support
3281 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3282 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3283 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3285 bodyEl.dom.style.height = '';
3286 bodyEl.dom.style.overflowY = '';
3289 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3291 bodyEl.dom.style.overflowX = '';
3294 dlg.setContentSize(w, bodyEl.getHeight());
3295 if(dlg.isVisible()){
3296 dlg.fixedcenter = true;
3302 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3303 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3304 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3305 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3306 * @return {Roo.MessageBox} This message box
3308 updateProgress : function(value, text){
3310 this.updateText(text);
3313 if (pp) { // weird bug on my firefox - for some reason this is not defined
3314 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3315 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3321 * Returns true if the message box is currently displayed
3322 * @return {Boolean} True if the message box is visible, else false
3324 isVisible : function(){
3325 return dlg && dlg.isVisible();
3329 * Hides the message box if it is displayed
3332 if(this.isVisible()){
3338 * Displays a new message box, or reinitializes an existing message box, based on the config options
3339 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3340 * The following config object properties are supported:
3342 Property Type Description
3343 ---------- --------------- ------------------------------------------------------------------------------------
3344 animEl String/Element An id or Element from which the message box should animate as it opens and
3345 closes (defaults to undefined)
3346 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3347 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3348 closable Boolean False to hide the top-right close button (defaults to true). Note that
3349 progress and wait dialogs will ignore this property and always hide the
3350 close button as they can only be closed programmatically.
3351 cls String A custom CSS class to apply to the message box element
3352 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3353 displayed (defaults to 75)
3354 fn Function A callback function to execute after closing the dialog. The arguments to the
3355 function will be btn (the name of the button that was clicked, if applicable,
3356 e.g. "ok"), and text (the value of the active text field, if applicable).
3357 Progress and wait dialogs will ignore this option since they do not respond to
3358 user actions and can only be closed programmatically, so any required function
3359 should be called by the same code after it closes the dialog.
3360 icon String A CSS class that provides a background image to be used as an icon for
3361 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3362 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3363 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3364 modal Boolean False to allow user interaction with the page while the message box is
3365 displayed (defaults to true)
3366 msg String A string that will replace the existing message box body text (defaults
3367 to the XHTML-compliant non-breaking space character ' ')
3368 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3369 progress Boolean True to display a progress bar (defaults to false)
3370 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3371 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3372 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3373 title String The title text
3374 value String The string value to set into the active textbox element if displayed
3375 wait Boolean True to display a progress bar (defaults to false)
3376 width Number The width of the dialog in pixels
3383 msg: 'Please enter your address:',
3385 buttons: Roo.MessageBox.OKCANCEL,
3388 animEl: 'addAddressBtn'
3391 * @param {Object} config Configuration options
3392 * @return {Roo.MessageBox} This message box
3394 show : function(options)
3397 // this causes nightmares if you show one dialog after another
3398 // especially on callbacks..
3400 if(this.isVisible()){
3403 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3404 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3405 Roo.log("New Dialog Message:" + options.msg )
3406 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3407 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3410 var d = this.getDialog();
3412 d.setTitle(opt.title || " ");
3413 d.closeEl.setDisplayed(opt.closable !== false);
3414 activeTextEl = textboxEl;
3415 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3420 textareaEl.setHeight(typeof opt.multiline == "number" ?
3421 opt.multiline : this.defaultTextHeight);
3422 activeTextEl = textareaEl;
3431 progressEl.setDisplayed(opt.progress === true);
3432 this.updateProgress(0);
3433 activeTextEl.dom.value = opt.value || "";
3435 dlg.setDefaultButton(activeTextEl);
3437 var bs = opt.buttons;
3441 }else if(bs && bs.yes){
3442 db = buttons["yes"];
3444 dlg.setDefaultButton(db);
3446 bwidth = updateButtons(opt.buttons);
3447 this.updateText(opt.msg);
3449 d.el.addClass(opt.cls);
3451 d.proxyDrag = opt.proxyDrag === true;
3452 d.modal = opt.modal !== false;
3453 d.mask = opt.modal !== false ? mask : false;
3455 // force it to the end of the z-index stack so it gets a cursor in FF
3456 document.body.appendChild(dlg.el.dom);
3457 d.animateTarget = null;
3458 d.show(options.animEl);
3464 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3465 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3466 * and closing the message box when the process is complete.
3467 * @param {String} title The title bar text
3468 * @param {String} msg The message box body text
3469 * @return {Roo.MessageBox} This message box
3471 progress : function(title, msg){
3478 minWidth: this.minProgressWidth,
3485 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3486 * If a callback function is passed it will be called after the user clicks the button, and the
3487 * id of the button that was clicked will be passed as the only parameter to the callback
3488 * (could also be the top-right close button).
3489 * @param {String} title The title bar text
3490 * @param {String} msg The message box body text
3491 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3492 * @param {Object} scope (optional) The scope of the callback function
3493 * @return {Roo.MessageBox} This message box
3495 alert : function(title, msg, fn, scope)
3510 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3511 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3512 * You are responsible for closing the message box when the process is complete.
3513 * @param {String} msg The message box body text
3514 * @param {String} title (optional) The title bar text
3515 * @return {Roo.MessageBox} This message box
3517 wait : function(msg, title){
3528 waitTimer = Roo.TaskMgr.start({
3530 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3538 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3539 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3540 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3541 * @param {String} title The title bar text
3542 * @param {String} msg The message box body text
3543 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3544 * @param {Object} scope (optional) The scope of the callback function
3545 * @return {Roo.MessageBox} This message box
3547 confirm : function(title, msg, fn, scope){
3551 buttons: this.YESNO,
3560 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3561 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3562 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3563 * (could also be the top-right close button) and the text that was entered will be passed as the two
3564 * parameters to the callback.
3565 * @param {String} title The title bar text
3566 * @param {String} msg The message box body text
3567 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3568 * @param {Object} scope (optional) The scope of the callback function
3569 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3570 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3571 * @return {Roo.MessageBox} This message box
3573 prompt : function(title, msg, fn, scope, multiline){
3577 buttons: this.OKCANCEL,
3582 multiline: multiline,
3589 * Button config that displays a single OK button
3594 * Button config that displays Yes and No buttons
3597 YESNO : {yes:true, no:true},
3599 * Button config that displays OK and Cancel buttons
3602 OKCANCEL : {ok:true, cancel:true},
3604 * Button config that displays Yes, No and Cancel buttons
3607 YESNOCANCEL : {yes:true, no:true, cancel:true},
3610 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3613 defaultTextHeight : 75,
3615 * The maximum width in pixels of the message box (defaults to 600)
3620 * The minimum width in pixels of the message box (defaults to 100)
3625 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3626 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3629 minProgressWidth : 250,
3631 * An object containing the default button text strings that can be overriden for localized language support.
3632 * Supported properties are: ok, cancel, yes and no.
3633 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3646 * Shorthand for {@link Roo.MessageBox}
3648 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3649 Roo.Msg = Roo.Msg || Roo.MessageBox;
3658 * @class Roo.bootstrap.Navbar
3659 * @extends Roo.bootstrap.Component
3660 * Bootstrap Navbar class
3663 * Create a new Navbar
3664 * @param {Object} config The config object
3668 Roo.bootstrap.Navbar = function(config){
3669 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3673 * @event beforetoggle
3674 * Fire before toggle the menu
3675 * @param {Roo.EventObject} e
3677 "beforetoggle" : true
3681 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3690 getAutoCreate : function(){
3693 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3697 initEvents :function ()
3699 //Roo.log(this.el.select('.navbar-toggle',true));
3700 this.el.select('.navbar-toggle',true).on('click', function() {
3701 if(this.fireEvent('beforetoggle', this) !== false){
3702 this.el.select('.navbar-collapse',true).toggleClass('in');
3712 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3714 var size = this.el.getSize();
3715 this.maskEl.setSize(size.width, size.height);
3716 this.maskEl.enableDisplayMode("block");
3725 getChildContainer : function()
3727 if (this.el.select('.collapse').getCount()) {
3728 return this.el.select('.collapse',true).first();
3761 * @class Roo.bootstrap.NavSimplebar
3762 * @extends Roo.bootstrap.Navbar
3763 * Bootstrap Sidebar class
3765 * @cfg {Boolean} inverse is inverted color
3767 * @cfg {String} type (nav | pills | tabs)
3768 * @cfg {Boolean} arrangement stacked | justified
3769 * @cfg {String} align (left | right) alignment
3771 * @cfg {Boolean} main (true|false) main nav bar? default false
3772 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3774 * @cfg {String} tag (header|footer|nav|div) default is nav
3780 * Create a new Sidebar
3781 * @param {Object} config The config object
3785 Roo.bootstrap.NavSimplebar = function(config){
3786 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3789 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3805 getAutoCreate : function(){
3809 tag : this.tag || 'div',
3822 this.type = this.type || 'nav';
3823 if (['tabs','pills'].indexOf(this.type)!==-1) {
3824 cfg.cn[0].cls += ' nav-' + this.type
3828 if (this.type!=='nav') {
3829 Roo.log('nav type must be nav/tabs/pills')
3831 cfg.cn[0].cls += ' navbar-nav'
3837 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3838 cfg.cn[0].cls += ' nav-' + this.arrangement;
3842 if (this.align === 'right') {
3843 cfg.cn[0].cls += ' navbar-right';
3847 cfg.cls += ' navbar-inverse';
3874 * @class Roo.bootstrap.NavHeaderbar
3875 * @extends Roo.bootstrap.NavSimplebar
3876 * Bootstrap Sidebar class
3878 * @cfg {String} brand what is brand
3879 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3880 * @cfg {String} brand_href href of the brand
3881 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3882 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3883 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3884 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3887 * Create a new Sidebar
3888 * @param {Object} config The config object
3892 Roo.bootstrap.NavHeaderbar = function(config){
3893 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3897 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3904 desktopCenter : false,
3907 getAutoCreate : function(){
3910 tag: this.nav || 'nav',
3917 if (this.desktopCenter) {
3918 cn.push({cls : 'container', cn : []});
3925 cls: 'navbar-header',
3930 cls: 'navbar-toggle',
3931 'data-toggle': 'collapse',
3936 html: 'Toggle navigation'
3958 cls: 'collapse navbar-collapse',
3962 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3964 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3965 cfg.cls += ' navbar-' + this.position;
3967 // tag can override this..
3969 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3972 if (this.brand !== '') {
3975 href: this.brand_href ? this.brand_href : '#',
3976 cls: 'navbar-brand',
3984 cfg.cls += ' main-nav';
3992 getHeaderChildContainer : function()
3994 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3995 return this.el.select('.navbar-header',true).first();
3998 return this.getChildContainer();
4002 initEvents : function()
4004 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4006 if (this.autohide) {
4011 Roo.get(document).on('scroll',function(e) {
4012 var ns = Roo.get(document).getScroll().top;
4013 var os = prevScroll;
4017 ft.removeClass('slideDown');
4018 ft.addClass('slideUp');
4021 ft.removeClass('slideUp');
4022 ft.addClass('slideDown');
4043 * @class Roo.bootstrap.NavSidebar
4044 * @extends Roo.bootstrap.Navbar
4045 * Bootstrap Sidebar class
4048 * Create a new Sidebar
4049 * @param {Object} config The config object
4053 Roo.bootstrap.NavSidebar = function(config){
4054 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4057 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4059 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4061 getAutoCreate : function(){
4066 cls: 'sidebar sidebar-nav'
4088 * @class Roo.bootstrap.NavGroup
4089 * @extends Roo.bootstrap.Component
4090 * Bootstrap NavGroup class
4091 * @cfg {String} align (left|right)
4092 * @cfg {Boolean} inverse
4093 * @cfg {String} type (nav|pills|tab) default nav
4094 * @cfg {String} navId - reference Id for navbar.
4098 * Create a new nav group
4099 * @param {Object} config The config object
4102 Roo.bootstrap.NavGroup = function(config){
4103 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4106 Roo.bootstrap.NavGroup.register(this);
4110 * Fires when the active item changes
4111 * @param {Roo.bootstrap.NavGroup} this
4112 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4113 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4120 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4131 getAutoCreate : function()
4133 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4140 if (['tabs','pills'].indexOf(this.type)!==-1) {
4141 cfg.cls += ' nav-' + this.type
4143 if (this.type!=='nav') {
4144 Roo.log('nav type must be nav/tabs/pills')
4146 cfg.cls += ' navbar-nav'
4149 if (this.parent() && this.parent().sidebar) {
4152 cls: 'dashboard-menu sidebar-menu'
4158 if (this.form === true) {
4164 if (this.align === 'right') {
4165 cfg.cls += ' navbar-right';
4167 cfg.cls += ' navbar-left';
4171 if (this.align === 'right') {
4172 cfg.cls += ' navbar-right';
4176 cfg.cls += ' navbar-inverse';
4184 * sets the active Navigation item
4185 * @param {Roo.bootstrap.NavItem} the new current navitem
4187 setActiveItem : function(item)
4190 Roo.each(this.navItems, function(v){
4195 v.setActive(false, true);
4202 item.setActive(true, true);
4203 this.fireEvent('changed', this, item, prev);
4208 * gets the active Navigation item
4209 * @return {Roo.bootstrap.NavItem} the current navitem
4211 getActive : function()
4215 Roo.each(this.navItems, function(v){
4226 indexOfNav : function()
4230 Roo.each(this.navItems, function(v,i){
4241 * adds a Navigation item
4242 * @param {Roo.bootstrap.NavItem} the navitem to add
4244 addItem : function(cfg)
4246 var cn = new Roo.bootstrap.NavItem(cfg);
4248 cn.parentId = this.id;
4249 cn.onRender(this.el, null);
4253 * register a Navigation item
4254 * @param {Roo.bootstrap.NavItem} the navitem to add
4256 register : function(item)
4258 this.navItems.push( item);
4259 item.navId = this.navId;
4264 * clear all the Navigation item
4267 clearAll : function()
4270 this.el.dom.innerHTML = '';
4273 getNavItem: function(tabId)
4276 Roo.each(this.navItems, function(e) {
4277 if (e.tabId == tabId) {
4287 setActiveNext : function()
4289 var i = this.indexOfNav(this.getActive());
4290 if (i > this.navItems.length) {
4293 this.setActiveItem(this.navItems[i+1]);
4295 setActivePrev : function()
4297 var i = this.indexOfNav(this.getActive());
4301 this.setActiveItem(this.navItems[i-1]);
4303 clearWasActive : function(except) {
4304 Roo.each(this.navItems, function(e) {
4305 if (e.tabId != except.tabId && e.was_active) {
4306 e.was_active = false;
4313 getWasActive : function ()
4316 Roo.each(this.navItems, function(e) {
4331 Roo.apply(Roo.bootstrap.NavGroup, {
4335 * register a Navigation Group
4336 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4338 register : function(navgrp)
4340 this.groups[navgrp.navId] = navgrp;
4344 * fetch a Navigation Group based on the navigation ID
4345 * @param {string} the navgroup to add
4346 * @returns {Roo.bootstrap.NavGroup} the navgroup
4348 get: function(navId) {
4349 if (typeof(this.groups[navId]) == 'undefined') {
4351 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4353 return this.groups[navId] ;
4368 * @class Roo.bootstrap.NavItem
4369 * @extends Roo.bootstrap.Component
4370 * Bootstrap Navbar.NavItem class
4371 * @cfg {String} href link to
4372 * @cfg {String} html content of button
4373 * @cfg {String} badge text inside badge
4374 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4375 * @cfg {String} glyphicon name of glyphicon
4376 * @cfg {String} icon name of font awesome icon
4377 * @cfg {Boolean} active Is item active
4378 * @cfg {Boolean} disabled Is item disabled
4380 * @cfg {Boolean} preventDefault (true | false) default false
4381 * @cfg {String} tabId the tab that this item activates.
4382 * @cfg {String} tagtype (a|span) render as a href or span?
4383 * @cfg {Boolean} animateRef (true|false) link to element default false
4386 * Create a new Navbar Item
4387 * @param {Object} config The config object
4389 Roo.bootstrap.NavItem = function(config){
4390 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4395 * The raw click event for the entire grid.
4396 * @param {Roo.EventObject} e
4401 * Fires when the active item active state changes
4402 * @param {Roo.bootstrap.NavItem} this
4403 * @param {boolean} state the new state
4409 * Fires when scroll to element
4410 * @param {Roo.bootstrap.NavItem} this
4411 * @param {Object} options
4412 * @param {Roo.EventObject} e
4420 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4428 preventDefault : false,
4435 getAutoCreate : function(){
4444 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4446 if (this.disabled) {
4447 cfg.cls += ' disabled';
4450 if (this.href || this.html || this.glyphicon || this.icon) {
4454 href : this.href || "#",
4455 html: this.html || ''
4460 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4463 if(this.glyphicon) {
4464 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4469 cfg.cn[0].html += " <span class='caret'></span>";
4473 if (this.badge !== '') {
4475 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4483 initEvents: function()
4485 if (typeof (this.menu) != 'undefined') {
4486 this.menu.parentType = this.xtype;
4487 this.menu.triggerEl = this.el;
4488 this.menu = this.addxtype(Roo.apply({}, this.menu));
4491 this.el.select('a',true).on('click', this.onClick, this);
4493 if(this.tagtype == 'span'){
4494 this.el.select('span',true).on('click', this.onClick, this);
4497 // at this point parent should be available..
4498 this.parent().register(this);
4501 onClick : function(e)
4503 if (e.getTarget('.dropdown-menu-item')) {
4504 // did you click on a menu itemm.... - then don't trigger onclick..
4509 this.preventDefault ||
4512 Roo.log("NavItem - prevent Default?");
4516 if (this.disabled) {
4520 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4521 if (tg && tg.transition) {
4522 Roo.log("waiting for the transitionend");
4528 //Roo.log("fire event clicked");
4529 if(this.fireEvent('click', this, e) === false){
4533 if(this.tagtype == 'span'){
4537 //Roo.log(this.href);
4538 var ael = this.el.select('a',true).first();
4541 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4542 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4543 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4544 return; // ignore... - it's a 'hash' to another page.
4546 Roo.log("NavItem - prevent Default?");
4548 this.scrollToElement(e);
4552 var p = this.parent();
4554 if (['tabs','pills'].indexOf(p.type)!==-1) {
4555 if (typeof(p.setActiveItem) !== 'undefined') {
4556 p.setActiveItem(this);
4560 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4561 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4562 // remove the collapsed menu expand...
4563 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4567 isActive: function () {
4570 setActive : function(state, fire, is_was_active)
4572 if (this.active && !state && this.navId) {
4573 this.was_active = true;
4574 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4576 nv.clearWasActive(this);
4580 this.active = state;
4583 this.el.removeClass('active');
4584 } else if (!this.el.hasClass('active')) {
4585 this.el.addClass('active');
4588 this.fireEvent('changed', this, state);
4591 // show a panel if it's registered and related..
4593 if (!this.navId || !this.tabId || !state || is_was_active) {
4597 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4601 var pan = tg.getPanelByName(this.tabId);
4605 // if we can not flip to new panel - go back to old nav highlight..
4606 if (false == tg.showPanel(pan)) {
4607 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4609 var onav = nv.getWasActive();
4611 onav.setActive(true, false, true);
4620 // this should not be here...
4621 setDisabled : function(state)
4623 this.disabled = state;
4625 this.el.removeClass('disabled');
4626 } else if (!this.el.hasClass('disabled')) {
4627 this.el.addClass('disabled');
4633 * Fetch the element to display the tooltip on.
4634 * @return {Roo.Element} defaults to this.el
4636 tooltipEl : function()
4638 return this.el.select('' + this.tagtype + '', true).first();
4641 scrollToElement : function(e)
4643 var c = document.body;
4646 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4648 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4649 c = document.documentElement;
4652 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4658 var o = target.calcOffsetsTo(c);
4665 this.fireEvent('scrollto', this, options, e);
4667 Roo.get(c).scrollTo('top', options.value, true);
4680 * <span> icon </span>
4681 * <span> text </span>
4682 * <span>badge </span>
4686 * @class Roo.bootstrap.NavSidebarItem
4687 * @extends Roo.bootstrap.NavItem
4688 * Bootstrap Navbar.NavSidebarItem class
4689 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4690 * {Boolean} open is the menu open
4691 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4692 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4693 * {String} buttonSize (sm|md|lg)the extra classes for the button
4694 * {Boolean} showArrow show arrow next to the text (default true)
4696 * Create a new Navbar Button
4697 * @param {Object} config The config object
4699 Roo.bootstrap.NavSidebarItem = function(config){
4700 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4705 * The raw click event for the entire grid.
4706 * @param {Roo.EventObject} e
4711 * Fires when the active item active state changes
4712 * @param {Roo.bootstrap.NavSidebarItem} this
4713 * @param {boolean} state the new state
4721 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4723 badgeWeight : 'default',
4729 buttonWeight : 'default',
4735 getAutoCreate : function(){
4740 href : this.href || '#',
4746 if(this.buttonView){
4749 href : this.href || '#',
4750 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4763 cfg.cls += ' active';
4766 if (this.disabled) {
4767 cfg.cls += ' disabled';
4770 cfg.cls += ' open x-open';
4773 if (this.glyphicon || this.icon) {
4774 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4775 a.cn.push({ tag : 'i', cls : c }) ;
4778 if(!this.buttonView){
4781 html : this.html || ''
4788 if (this.badge !== '') {
4789 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4795 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4798 a.cls += ' dropdown-toggle treeview' ;
4804 initEvents : function()
4806 if (typeof (this.menu) != 'undefined') {
4807 this.menu.parentType = this.xtype;
4808 this.menu.triggerEl = this.el;
4809 this.menu = this.addxtype(Roo.apply({}, this.menu));
4812 this.el.on('click', this.onClick, this);
4814 if(this.badge !== ''){
4815 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4820 onClick : function(e)
4827 if(this.preventDefault){
4831 this.fireEvent('click', this);
4834 disable : function()
4836 this.setDisabled(true);
4841 this.setDisabled(false);
4844 setDisabled : function(state)
4846 if(this.disabled == state){
4850 this.disabled = state;
4853 this.el.addClass('disabled');
4857 this.el.removeClass('disabled');
4862 setActive : function(state)
4864 if(this.active == state){
4868 this.active = state;
4871 this.el.addClass('active');
4875 this.el.removeClass('active');
4880 isActive: function ()
4885 setBadge : function(str)
4891 this.badgeEl.dom.innerHTML = str;
4908 * @class Roo.bootstrap.Row
4909 * @extends Roo.bootstrap.Component
4910 * Bootstrap Row class (contains columns...)
4914 * @param {Object} config The config object
4917 Roo.bootstrap.Row = function(config){
4918 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4921 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4923 getAutoCreate : function(){
4942 * @class Roo.bootstrap.Element
4943 * @extends Roo.bootstrap.Component
4944 * Bootstrap Element class
4945 * @cfg {String} html contents of the element
4946 * @cfg {String} tag tag of the element
4947 * @cfg {String} cls class of the element
4948 * @cfg {Boolean} preventDefault (true|false) default false
4949 * @cfg {Boolean} clickable (true|false) default false
4952 * Create a new Element
4953 * @param {Object} config The config object
4956 Roo.bootstrap.Element = function(config){
4957 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4963 * When a element is chick
4964 * @param {Roo.bootstrap.Element} this
4965 * @param {Roo.EventObject} e
4971 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4976 preventDefault: false,
4979 getAutoCreate : function(){
4983 // cls: this.cls, double assign in parent class Component.js :: onRender
4990 initEvents: function()
4992 Roo.bootstrap.Element.superclass.initEvents.call(this);
4995 this.el.on('click', this.onClick, this);
5000 onClick : function(e)
5002 if(this.preventDefault){
5006 this.fireEvent('click', this, e);
5009 getValue : function()
5011 return this.el.dom.innerHTML;
5014 setValue : function(value)
5016 this.el.dom.innerHTML = value;
5031 * @class Roo.bootstrap.Pagination
5032 * @extends Roo.bootstrap.Component
5033 * Bootstrap Pagination class
5034 * @cfg {String} size xs | sm | md | lg
5035 * @cfg {Boolean} inverse false | true
5038 * Create a new Pagination
5039 * @param {Object} config The config object
5042 Roo.bootstrap.Pagination = function(config){
5043 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5046 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5052 getAutoCreate : function(){
5058 cfg.cls += ' inverse';
5064 cfg.cls += " " + this.cls;
5082 * @class Roo.bootstrap.PaginationItem
5083 * @extends Roo.bootstrap.Component
5084 * Bootstrap PaginationItem class
5085 * @cfg {String} html text
5086 * @cfg {String} href the link
5087 * @cfg {Boolean} preventDefault (true | false) default true
5088 * @cfg {Boolean} active (true | false) default false
5089 * @cfg {Boolean} disabled default false
5093 * Create a new PaginationItem
5094 * @param {Object} config The config object
5098 Roo.bootstrap.PaginationItem = function(config){
5099 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5104 * The raw click event for the entire grid.
5105 * @param {Roo.EventObject} e
5111 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5115 preventDefault: true,
5120 getAutoCreate : function(){
5126 href : this.href ? this.href : '#',
5127 html : this.html ? this.html : ''
5137 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5141 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5147 initEvents: function() {
5149 this.el.on('click', this.onClick, this);
5152 onClick : function(e)
5154 Roo.log('PaginationItem on click ');
5155 if(this.preventDefault){
5163 this.fireEvent('click', this, e);
5179 * @class Roo.bootstrap.Slider
5180 * @extends Roo.bootstrap.Component
5181 * Bootstrap Slider class
5184 * Create a new Slider
5185 * @param {Object} config The config object
5188 Roo.bootstrap.Slider = function(config){
5189 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5192 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5194 getAutoCreate : function(){
5198 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5202 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5214 * Ext JS Library 1.1.1
5215 * Copyright(c) 2006-2007, Ext JS, LLC.
5217 * Originally Released Under LGPL - original licence link has changed is not relivant.
5220 * <script type="text/javascript">
5225 * @class Roo.grid.ColumnModel
5226 * @extends Roo.util.Observable
5227 * This is the default implementation of a ColumnModel used by the Grid. It defines
5228 * the columns in the grid.
5231 var colModel = new Roo.grid.ColumnModel([
5232 {header: "Ticker", width: 60, sortable: true, locked: true},
5233 {header: "Company Name", width: 150, sortable: true},
5234 {header: "Market Cap.", width: 100, sortable: true},
5235 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5236 {header: "Employees", width: 100, sortable: true, resizable: false}
5241 * The config options listed for this class are options which may appear in each
5242 * individual column definition.
5243 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5245 * @param {Object} config An Array of column config objects. See this class's
5246 * config objects for details.
5248 Roo.grid.ColumnModel = function(config){
5250 * The config passed into the constructor
5252 this.config = config;
5255 // if no id, create one
5256 // if the column does not have a dataIndex mapping,
5257 // map it to the order it is in the config
5258 for(var i = 0, len = config.length; i < len; i++){
5260 if(typeof c.dataIndex == "undefined"){
5263 if(typeof c.renderer == "string"){
5264 c.renderer = Roo.util.Format[c.renderer];
5266 if(typeof c.id == "undefined"){
5269 if(c.editor && c.editor.xtype){
5270 c.editor = Roo.factory(c.editor, Roo.grid);
5272 if(c.editor && c.editor.isFormField){
5273 c.editor = new Roo.grid.GridEditor(c.editor);
5275 this.lookup[c.id] = c;
5279 * The width of columns which have no width specified (defaults to 100)
5282 this.defaultWidth = 100;
5285 * Default sortable of columns which have no sortable specified (defaults to false)
5288 this.defaultSortable = false;
5292 * @event widthchange
5293 * Fires when the width of a column changes.
5294 * @param {ColumnModel} this
5295 * @param {Number} columnIndex The column index
5296 * @param {Number} newWidth The new width
5298 "widthchange": true,
5300 * @event headerchange
5301 * Fires when the text of a header changes.
5302 * @param {ColumnModel} this
5303 * @param {Number} columnIndex The column index
5304 * @param {Number} newText The new header text
5306 "headerchange": true,
5308 * @event hiddenchange
5309 * Fires when a column is hidden or "unhidden".
5310 * @param {ColumnModel} this
5311 * @param {Number} columnIndex The column index
5312 * @param {Boolean} hidden true if hidden, false otherwise
5314 "hiddenchange": true,
5316 * @event columnmoved
5317 * Fires when a column is moved.
5318 * @param {ColumnModel} this
5319 * @param {Number} oldIndex
5320 * @param {Number} newIndex
5322 "columnmoved" : true,
5324 * @event columlockchange
5325 * Fires when a column's locked state is changed
5326 * @param {ColumnModel} this
5327 * @param {Number} colIndex
5328 * @param {Boolean} locked true if locked
5330 "columnlockchange" : true
5332 Roo.grid.ColumnModel.superclass.constructor.call(this);
5334 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5336 * @cfg {String} header The header text to display in the Grid view.
5339 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5340 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5341 * specified, the column's index is used as an index into the Record's data Array.
5344 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5345 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5348 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5349 * Defaults to the value of the {@link #defaultSortable} property.
5350 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5353 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5356 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5359 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5362 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5365 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5366 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5367 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5368 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5371 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5374 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5377 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5380 * @cfg {String} cursor (Optional)
5383 * @cfg {String} tooltip (Optional)
5386 * @cfg {Number} xs (Optional)
5389 * @cfg {Number} sm (Optional)
5392 * @cfg {Number} md (Optional)
5395 * @cfg {Number} lg (Optional)
5398 * Returns the id of the column at the specified index.
5399 * @param {Number} index The column index
5400 * @return {String} the id
5402 getColumnId : function(index){
5403 return this.config[index].id;
5407 * Returns the column for a specified id.
5408 * @param {String} id The column id
5409 * @return {Object} the column
5411 getColumnById : function(id){
5412 return this.lookup[id];
5417 * Returns the column for a specified dataIndex.
5418 * @param {String} dataIndex The column dataIndex
5419 * @return {Object|Boolean} the column or false if not found
5421 getColumnByDataIndex: function(dataIndex){
5422 var index = this.findColumnIndex(dataIndex);
5423 return index > -1 ? this.config[index] : false;
5427 * Returns the index for a specified column id.
5428 * @param {String} id The column id
5429 * @return {Number} the index, or -1 if not found
5431 getIndexById : function(id){
5432 for(var i = 0, len = this.config.length; i < len; i++){
5433 if(this.config[i].id == id){
5441 * Returns the index for a specified column dataIndex.
5442 * @param {String} dataIndex The column dataIndex
5443 * @return {Number} the index, or -1 if not found
5446 findColumnIndex : function(dataIndex){
5447 for(var i = 0, len = this.config.length; i < len; i++){
5448 if(this.config[i].dataIndex == dataIndex){
5456 moveColumn : function(oldIndex, newIndex){
5457 var c = this.config[oldIndex];
5458 this.config.splice(oldIndex, 1);
5459 this.config.splice(newIndex, 0, c);
5460 this.dataMap = null;
5461 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5464 isLocked : function(colIndex){
5465 return this.config[colIndex].locked === true;
5468 setLocked : function(colIndex, value, suppressEvent){
5469 if(this.isLocked(colIndex) == value){
5472 this.config[colIndex].locked = value;
5474 this.fireEvent("columnlockchange", this, colIndex, value);
5478 getTotalLockedWidth : function(){
5480 for(var i = 0; i < this.config.length; i++){
5481 if(this.isLocked(i) && !this.isHidden(i)){
5482 this.totalWidth += this.getColumnWidth(i);
5488 getLockedCount : function(){
5489 for(var i = 0, len = this.config.length; i < len; i++){
5490 if(!this.isLocked(i)){
5495 return this.config.length;
5499 * Returns the number of columns.
5502 getColumnCount : function(visibleOnly){
5503 if(visibleOnly === true){
5505 for(var i = 0, len = this.config.length; i < len; i++){
5506 if(!this.isHidden(i)){
5512 return this.config.length;
5516 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5517 * @param {Function} fn
5518 * @param {Object} scope (optional)
5519 * @return {Array} result
5521 getColumnsBy : function(fn, scope){
5523 for(var i = 0, len = this.config.length; i < len; i++){
5524 var c = this.config[i];
5525 if(fn.call(scope||this, c, i) === true){
5533 * Returns true if the specified column is sortable.
5534 * @param {Number} col The column index
5537 isSortable : function(col){
5538 if(typeof this.config[col].sortable == "undefined"){
5539 return this.defaultSortable;
5541 return this.config[col].sortable;
5545 * Returns the rendering (formatting) function defined for the column.
5546 * @param {Number} col The column index.
5547 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5549 getRenderer : function(col){
5550 if(!this.config[col].renderer){
5551 return Roo.grid.ColumnModel.defaultRenderer;
5553 return this.config[col].renderer;
5557 * Sets the rendering (formatting) function for a column.
5558 * @param {Number} col The column index
5559 * @param {Function} fn The function to use to process the cell's raw data
5560 * to return HTML markup for the grid view. The render function is called with
5561 * the following parameters:<ul>
5562 * <li>Data value.</li>
5563 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5564 * <li>css A CSS style string to apply to the table cell.</li>
5565 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5566 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5567 * <li>Row index</li>
5568 * <li>Column index</li>
5569 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5571 setRenderer : function(col, fn){
5572 this.config[col].renderer = fn;
5576 * Returns the width for the specified column.
5577 * @param {Number} col The column index
5580 getColumnWidth : function(col){
5581 return this.config[col].width * 1 || this.defaultWidth;
5585 * Sets the width for a column.
5586 * @param {Number} col The column index
5587 * @param {Number} width The new width
5589 setColumnWidth : function(col, width, suppressEvent){
5590 this.config[col].width = width;
5591 this.totalWidth = null;
5593 this.fireEvent("widthchange", this, col, width);
5598 * Returns the total width of all columns.
5599 * @param {Boolean} includeHidden True to include hidden column widths
5602 getTotalWidth : function(includeHidden){
5603 if(!this.totalWidth){
5604 this.totalWidth = 0;
5605 for(var i = 0, len = this.config.length; i < len; i++){
5606 if(includeHidden || !this.isHidden(i)){
5607 this.totalWidth += this.getColumnWidth(i);
5611 return this.totalWidth;
5615 * Returns the header for the specified column.
5616 * @param {Number} col The column index
5619 getColumnHeader : function(col){
5620 return this.config[col].header;
5624 * Sets the header for a column.
5625 * @param {Number} col The column index
5626 * @param {String} header The new header
5628 setColumnHeader : function(col, header){
5629 this.config[col].header = header;
5630 this.fireEvent("headerchange", this, col, header);
5634 * Returns the tooltip for the specified column.
5635 * @param {Number} col The column index
5638 getColumnTooltip : function(col){
5639 return this.config[col].tooltip;
5642 * Sets the tooltip for a column.
5643 * @param {Number} col The column index
5644 * @param {String} tooltip The new tooltip
5646 setColumnTooltip : function(col, tooltip){
5647 this.config[col].tooltip = tooltip;
5651 * Returns the dataIndex for the specified column.
5652 * @param {Number} col The column index
5655 getDataIndex : function(col){
5656 return this.config[col].dataIndex;
5660 * Sets the dataIndex for a column.
5661 * @param {Number} col The column index
5662 * @param {Number} dataIndex The new dataIndex
5664 setDataIndex : function(col, dataIndex){
5665 this.config[col].dataIndex = dataIndex;
5671 * Returns true if the cell is editable.
5672 * @param {Number} colIndex The column index
5673 * @param {Number} rowIndex The row index - this is nto actually used..?
5676 isCellEditable : function(colIndex, rowIndex){
5677 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5681 * Returns the editor defined for the cell/column.
5682 * return false or null to disable editing.
5683 * @param {Number} colIndex The column index
5684 * @param {Number} rowIndex The row index
5687 getCellEditor : function(colIndex, rowIndex){
5688 return this.config[colIndex].editor;
5692 * Sets if a column is editable.
5693 * @param {Number} col The column index
5694 * @param {Boolean} editable True if the column is editable
5696 setEditable : function(col, editable){
5697 this.config[col].editable = editable;
5702 * Returns true if the column is hidden.
5703 * @param {Number} colIndex The column index
5706 isHidden : function(colIndex){
5707 return this.config[colIndex].hidden;
5712 * Returns true if the column width cannot be changed
5714 isFixed : function(colIndex){
5715 return this.config[colIndex].fixed;
5719 * Returns true if the column can be resized
5722 isResizable : function(colIndex){
5723 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5726 * Sets if a column is hidden.
5727 * @param {Number} colIndex The column index
5728 * @param {Boolean} hidden True if the column is hidden
5730 setHidden : function(colIndex, hidden){
5731 this.config[colIndex].hidden = hidden;
5732 this.totalWidth = null;
5733 this.fireEvent("hiddenchange", this, colIndex, hidden);
5737 * Sets the editor for a column.
5738 * @param {Number} col The column index
5739 * @param {Object} editor The editor object
5741 setEditor : function(col, editor){
5742 this.config[col].editor = editor;
5746 Roo.grid.ColumnModel.defaultRenderer = function(value)
5748 if(typeof value == "object") {
5751 if(typeof value == "string" && value.length < 1){
5755 return String.format("{0}", value);
5758 // Alias for backwards compatibility
5759 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5762 * Ext JS Library 1.1.1
5763 * Copyright(c) 2006-2007, Ext JS, LLC.
5765 * Originally Released Under LGPL - original licence link has changed is not relivant.
5768 * <script type="text/javascript">
5772 * @class Roo.LoadMask
5773 * A simple utility class for generically masking elements while loading data. If the element being masked has
5774 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5775 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5776 * element's UpdateManager load indicator and will be destroyed after the initial load.
5778 * Create a new LoadMask
5779 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5780 * @param {Object} config The config object
5782 Roo.LoadMask = function(el, config){
5783 this.el = Roo.get(el);
5784 Roo.apply(this, config);
5786 this.store.on('beforeload', this.onBeforeLoad, this);
5787 this.store.on('load', this.onLoad, this);
5788 this.store.on('loadexception', this.onLoadException, this);
5789 this.removeMask = false;
5791 var um = this.el.getUpdateManager();
5792 um.showLoadIndicator = false; // disable the default indicator
5793 um.on('beforeupdate', this.onBeforeLoad, this);
5794 um.on('update', this.onLoad, this);
5795 um.on('failure', this.onLoad, this);
5796 this.removeMask = true;
5800 Roo.LoadMask.prototype = {
5802 * @cfg {Boolean} removeMask
5803 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5804 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5808 * The text to display in a centered loading message box (defaults to 'Loading...')
5812 * @cfg {String} msgCls
5813 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5815 msgCls : 'x-mask-loading',
5818 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5824 * Disables the mask to prevent it from being displayed
5826 disable : function(){
5827 this.disabled = true;
5831 * Enables the mask so that it can be displayed
5833 enable : function(){
5834 this.disabled = false;
5837 onLoadException : function()
5841 if (typeof(arguments[3]) != 'undefined') {
5842 Roo.MessageBox.alert("Error loading",arguments[3]);
5846 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5847 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5854 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5859 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5863 onBeforeLoad : function(){
5865 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5870 destroy : function(){
5872 this.store.un('beforeload', this.onBeforeLoad, this);
5873 this.store.un('load', this.onLoad, this);
5874 this.store.un('loadexception', this.onLoadException, this);
5876 var um = this.el.getUpdateManager();
5877 um.un('beforeupdate', this.onBeforeLoad, this);
5878 um.un('update', this.onLoad, this);
5879 um.un('failure', this.onLoad, this);
5890 * @class Roo.bootstrap.Table
5891 * @extends Roo.bootstrap.Component
5892 * Bootstrap Table class
5893 * @cfg {String} cls table class
5894 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5895 * @cfg {String} bgcolor Specifies the background color for a table
5896 * @cfg {Number} border Specifies whether the table cells should have borders or not
5897 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5898 * @cfg {Number} cellspacing Specifies the space between cells
5899 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5900 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5901 * @cfg {String} sortable Specifies that the table should be sortable
5902 * @cfg {String} summary Specifies a summary of the content of a table
5903 * @cfg {Number} width Specifies the width of a table
5904 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5906 * @cfg {boolean} striped Should the rows be alternative striped
5907 * @cfg {boolean} bordered Add borders to the table
5908 * @cfg {boolean} hover Add hover highlighting
5909 * @cfg {boolean} condensed Format condensed
5910 * @cfg {boolean} responsive Format condensed
5911 * @cfg {Boolean} loadMask (true|false) default false
5912 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5913 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5914 * @cfg {Boolean} rowSelection (true|false) default false
5915 * @cfg {Boolean} cellSelection (true|false) default false
5916 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5917 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5918 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5919 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5923 * Create a new Table
5924 * @param {Object} config The config object
5927 Roo.bootstrap.Table = function(config){
5928 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5933 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5934 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5935 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5936 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5938 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5940 this.sm.grid = this;
5941 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5942 this.sm = this.selModel;
5943 this.sm.xmodule = this.xmodule || false;
5946 if (this.cm && typeof(this.cm.config) == 'undefined') {
5947 this.colModel = new Roo.grid.ColumnModel(this.cm);
5948 this.cm = this.colModel;
5949 this.cm.xmodule = this.xmodule || false;
5952 this.store= Roo.factory(this.store, Roo.data);
5953 this.ds = this.store;
5954 this.ds.xmodule = this.xmodule || false;
5957 if (this.footer && this.store) {
5958 this.footer.dataSource = this.ds;
5959 this.footer = Roo.factory(this.footer);
5966 * Fires when a cell is clicked
5967 * @param {Roo.bootstrap.Table} this
5968 * @param {Roo.Element} el
5969 * @param {Number} rowIndex
5970 * @param {Number} columnIndex
5971 * @param {Roo.EventObject} e
5975 * @event celldblclick
5976 * Fires when a cell is double clicked
5977 * @param {Roo.bootstrap.Table} this
5978 * @param {Roo.Element} el
5979 * @param {Number} rowIndex
5980 * @param {Number} columnIndex
5981 * @param {Roo.EventObject} e
5983 "celldblclick" : true,
5986 * Fires when a row is clicked
5987 * @param {Roo.bootstrap.Table} this
5988 * @param {Roo.Element} el
5989 * @param {Number} rowIndex
5990 * @param {Roo.EventObject} e
5994 * @event rowdblclick
5995 * Fires when a row is double clicked
5996 * @param {Roo.bootstrap.Table} this
5997 * @param {Roo.Element} el
5998 * @param {Number} rowIndex
5999 * @param {Roo.EventObject} e
6001 "rowdblclick" : true,
6004 * Fires when a mouseover occur
6005 * @param {Roo.bootstrap.Table} this
6006 * @param {Roo.Element} el
6007 * @param {Number} rowIndex
6008 * @param {Number} columnIndex
6009 * @param {Roo.EventObject} e
6014 * Fires when a mouseout occur
6015 * @param {Roo.bootstrap.Table} this
6016 * @param {Roo.Element} el
6017 * @param {Number} rowIndex
6018 * @param {Number} columnIndex
6019 * @param {Roo.EventObject} e
6024 * Fires when a row is rendered, so you can change add a style to it.
6025 * @param {Roo.bootstrap.Table} this
6026 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6030 * @event rowsrendered
6031 * Fires when all the rows have been rendered
6032 * @param {Roo.bootstrap.Table} this
6034 'rowsrendered' : true,
6036 * @event contextmenu
6037 * The raw contextmenu event for the entire grid.
6038 * @param {Roo.EventObject} e
6040 "contextmenu" : true,
6042 * @event rowcontextmenu
6043 * Fires when a row is right clicked
6044 * @param {Roo.bootstrap.Table} this
6045 * @param {Number} rowIndex
6046 * @param {Roo.EventObject} e
6048 "rowcontextmenu" : true,
6050 * @event cellcontextmenu
6051 * Fires when a cell is right clicked
6052 * @param {Roo.bootstrap.Table} this
6053 * @param {Number} rowIndex
6054 * @param {Number} cellIndex
6055 * @param {Roo.EventObject} e
6057 "cellcontextmenu" : true,
6059 * @event headercontextmenu
6060 * Fires when a header is right clicked
6061 * @param {Roo.bootstrap.Table} this
6062 * @param {Number} columnIndex
6063 * @param {Roo.EventObject} e
6065 "headercontextmenu" : true
6069 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6095 rowSelection : false,
6096 cellSelection : false,
6099 // Roo.Element - the tbody
6101 // Roo.Element - thead element
6104 container: false, // used by gridpanel...
6110 auto_hide_footer : false,
6112 getAutoCreate : function()
6114 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6121 if (this.scrollBody) {
6122 cfg.cls += ' table-body-fixed';
6125 cfg.cls += ' table-striped';
6129 cfg.cls += ' table-hover';
6131 if (this.bordered) {
6132 cfg.cls += ' table-bordered';
6134 if (this.condensed) {
6135 cfg.cls += ' table-condensed';
6137 if (this.responsive) {
6138 cfg.cls += ' table-responsive';
6142 cfg.cls+= ' ' +this.cls;
6145 // this lot should be simplifed...
6158 ].forEach(function(k) {
6166 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6169 if(this.store || this.cm){
6170 if(this.headerShow){
6171 cfg.cn.push(this.renderHeader());
6174 cfg.cn.push(this.renderBody());
6176 if(this.footerShow){
6177 cfg.cn.push(this.renderFooter());
6179 // where does this come from?
6180 //cfg.cls+= ' TableGrid';
6183 return { cn : [ cfg ] };
6186 initEvents : function()
6188 if(!this.store || !this.cm){
6191 if (this.selModel) {
6192 this.selModel.initEvents();
6196 //Roo.log('initEvents with ds!!!!');
6198 this.mainBody = this.el.select('tbody', true).first();
6199 this.mainHead = this.el.select('thead', true).first();
6200 this.mainFoot = this.el.select('tfoot', true).first();
6206 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6207 e.on('click', _this.sort, _this);
6210 this.mainBody.on("click", this.onClick, this);
6211 this.mainBody.on("dblclick", this.onDblClick, this);
6213 // why is this done????? = it breaks dialogs??
6214 //this.parent().el.setStyle('position', 'relative');
6218 this.footer.parentId = this.id;
6219 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6222 this.el.select('tfoot tr td').first().addClass('hide');
6227 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6230 this.store.on('load', this.onLoad, this);
6231 this.store.on('beforeload', this.onBeforeLoad, this);
6232 this.store.on('update', this.onUpdate, this);
6233 this.store.on('add', this.onAdd, this);
6234 this.store.on("clear", this.clear, this);
6236 this.el.on("contextmenu", this.onContextMenu, this);
6238 this.mainBody.on('scroll', this.onBodyScroll, this);
6240 this.cm.on("headerchange", this.onHeaderChange, this);
6242 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6246 onContextMenu : function(e, t)
6248 this.processEvent("contextmenu", e);
6251 processEvent : function(name, e)
6253 if (name != 'touchstart' ) {
6254 this.fireEvent(name, e);
6257 var t = e.getTarget();
6259 var cell = Roo.get(t);
6265 if(cell.findParent('tfoot', false, true)){
6269 if(cell.findParent('thead', false, true)){
6271 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6272 cell = Roo.get(t).findParent('th', false, true);
6274 Roo.log("failed to find th in thead?");
6275 Roo.log(e.getTarget());
6280 var cellIndex = cell.dom.cellIndex;
6282 var ename = name == 'touchstart' ? 'click' : name;
6283 this.fireEvent("header" + ename, this, cellIndex, e);
6288 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6289 cell = Roo.get(t).findParent('td', false, true);
6291 Roo.log("failed to find th in tbody?");
6292 Roo.log(e.getTarget());
6297 var row = cell.findParent('tr', false, true);
6298 var cellIndex = cell.dom.cellIndex;
6299 var rowIndex = row.dom.rowIndex - 1;
6303 this.fireEvent("row" + name, this, rowIndex, e);
6307 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6313 onMouseover : function(e, el)
6315 var cell = Roo.get(el);
6321 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6322 cell = cell.findParent('td', false, true);
6325 var row = cell.findParent('tr', false, true);
6326 var cellIndex = cell.dom.cellIndex;
6327 var rowIndex = row.dom.rowIndex - 1; // start from 0
6329 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6333 onMouseout : function(e, el)
6335 var cell = Roo.get(el);
6341 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6342 cell = cell.findParent('td', false, true);
6345 var row = cell.findParent('tr', false, true);
6346 var cellIndex = cell.dom.cellIndex;
6347 var rowIndex = row.dom.rowIndex - 1; // start from 0
6349 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6353 onClick : function(e, el)
6355 var cell = Roo.get(el);
6357 if(!cell || (!this.cellSelection && !this.rowSelection)){
6361 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6362 cell = cell.findParent('td', false, true);
6365 if(!cell || typeof(cell) == 'undefined'){
6369 var row = cell.findParent('tr', false, true);
6371 if(!row || typeof(row) == 'undefined'){
6375 var cellIndex = cell.dom.cellIndex;
6376 var rowIndex = this.getRowIndex(row);
6378 // why??? - should these not be based on SelectionModel?
6379 if(this.cellSelection){
6380 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6383 if(this.rowSelection){
6384 this.fireEvent('rowclick', this, row, rowIndex, e);
6390 onDblClick : function(e,el)
6392 var cell = Roo.get(el);
6394 if(!cell || (!this.cellSelection && !this.rowSelection)){
6398 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6399 cell = cell.findParent('td', false, true);
6402 if(!cell || typeof(cell) == 'undefined'){
6406 var row = cell.findParent('tr', false, true);
6408 if(!row || typeof(row) == 'undefined'){
6412 var cellIndex = cell.dom.cellIndex;
6413 var rowIndex = this.getRowIndex(row);
6415 if(this.cellSelection){
6416 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6419 if(this.rowSelection){
6420 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6424 sort : function(e,el)
6426 var col = Roo.get(el);
6428 if(!col.hasClass('sortable')){
6432 var sort = col.attr('sort');
6435 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6439 this.store.sortInfo = {field : sort, direction : dir};
6442 Roo.log("calling footer first");
6443 this.footer.onClick('first');
6446 this.store.load({ params : { start : 0 } });
6450 renderHeader : function()
6458 this.totalWidth = 0;
6460 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6462 var config = cm.config[i];
6466 cls : 'x-hcol-' + i,
6468 html: cm.getColumnHeader(i)
6473 if(typeof(config.sortable) != 'undefined' && config.sortable){
6475 c.html = '<i class="glyphicon"></i>' + c.html;
6478 if(typeof(config.lgHeader) != 'undefined'){
6479 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6482 if(typeof(config.mdHeader) != 'undefined'){
6483 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6486 if(typeof(config.smHeader) != 'undefined'){
6487 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6490 if(typeof(config.xsHeader) != 'undefined'){
6491 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6498 if(typeof(config.tooltip) != 'undefined'){
6499 c.tooltip = config.tooltip;
6502 if(typeof(config.colspan) != 'undefined'){
6503 c.colspan = config.colspan;
6506 if(typeof(config.hidden) != 'undefined' && config.hidden){
6507 c.style += ' display:none;';
6510 if(typeof(config.dataIndex) != 'undefined'){
6511 c.sort = config.dataIndex;
6516 if(typeof(config.align) != 'undefined' && config.align.length){
6517 c.style += ' text-align:' + config.align + ';';
6520 if(typeof(config.width) != 'undefined'){
6521 c.style += ' width:' + config.width + 'px;';
6522 this.totalWidth += config.width;
6524 this.totalWidth += 100; // assume minimum of 100 per column?
6527 if(typeof(config.cls) != 'undefined'){
6528 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6531 ['xs','sm','md','lg'].map(function(size){
6533 if(typeof(config[size]) == 'undefined'){
6537 if (!config[size]) { // 0 = hidden
6538 c.cls += ' hidden-' + size;
6542 c.cls += ' col-' + size + '-' + config[size];
6552 renderBody : function()
6562 colspan : this.cm.getColumnCount()
6572 renderFooter : function()
6582 colspan : this.cm.getColumnCount()
6596 // Roo.log('ds onload');
6601 var ds = this.store;
6603 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6604 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6605 if (_this.store.sortInfo) {
6607 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6608 e.select('i', true).addClass(['glyphicon-arrow-up']);
6611 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6612 e.select('i', true).addClass(['glyphicon-arrow-down']);
6617 var tbody = this.mainBody;
6619 if(ds.getCount() > 0){
6620 ds.data.each(function(d,rowIndex){
6621 var row = this.renderRow(cm, ds, rowIndex);
6623 tbody.createChild(row);
6627 if(row.cellObjects.length){
6628 Roo.each(row.cellObjects, function(r){
6629 _this.renderCellObject(r);
6636 var tfoot = this.el.select('tfoot', true).first();
6638 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6640 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6642 var total = this.ds.getTotalCount();
6644 if(this.footer.pageSize < total){
6645 this.mainFoot.show();
6649 Roo.each(this.el.select('tbody td', true).elements, function(e){
6650 e.on('mouseover', _this.onMouseover, _this);
6653 Roo.each(this.el.select('tbody td', true).elements, function(e){
6654 e.on('mouseout', _this.onMouseout, _this);
6656 this.fireEvent('rowsrendered', this);
6662 onUpdate : function(ds,record)
6664 this.refreshRow(record);
6668 onRemove : function(ds, record, index, isUpdate){
6669 if(isUpdate !== true){
6670 this.fireEvent("beforerowremoved", this, index, record);
6672 var bt = this.mainBody.dom;
6674 var rows = this.el.select('tbody > tr', true).elements;
6676 if(typeof(rows[index]) != 'undefined'){
6677 bt.removeChild(rows[index].dom);
6680 // if(bt.rows[index]){
6681 // bt.removeChild(bt.rows[index]);
6684 if(isUpdate !== true){
6685 //this.stripeRows(index);
6686 //this.syncRowHeights(index, index);
6688 this.fireEvent("rowremoved", this, index, record);
6692 onAdd : function(ds, records, rowIndex)
6694 //Roo.log('on Add called');
6695 // - note this does not handle multiple adding very well..
6696 var bt = this.mainBody.dom;
6697 for (var i =0 ; i < records.length;i++) {
6698 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6699 //Roo.log(records[i]);
6700 //Roo.log(this.store.getAt(rowIndex+i));
6701 this.insertRow(this.store, rowIndex + i, false);
6708 refreshRow : function(record){
6709 var ds = this.store, index;
6710 if(typeof record == 'number'){
6712 record = ds.getAt(index);
6714 index = ds.indexOf(record);
6716 this.insertRow(ds, index, true);
6718 this.onRemove(ds, record, index+1, true);
6720 //this.syncRowHeights(index, index);
6722 this.fireEvent("rowupdated", this, index, record);
6725 insertRow : function(dm, rowIndex, isUpdate){
6728 this.fireEvent("beforerowsinserted", this, rowIndex);
6730 //var s = this.getScrollState();
6731 var row = this.renderRow(this.cm, this.store, rowIndex);
6732 // insert before rowIndex..
6733 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6737 if(row.cellObjects.length){
6738 Roo.each(row.cellObjects, function(r){
6739 _this.renderCellObject(r);
6744 this.fireEvent("rowsinserted", this, rowIndex);
6745 //this.syncRowHeights(firstRow, lastRow);
6746 //this.stripeRows(firstRow);
6753 getRowDom : function(rowIndex)
6755 var rows = this.el.select('tbody > tr', true).elements;
6757 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6760 // returns the object tree for a tr..
6763 renderRow : function(cm, ds, rowIndex)
6765 var d = ds.getAt(rowIndex);
6769 cls : 'x-row-' + rowIndex,
6773 var cellObjects = [];
6775 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6776 var config = cm.config[i];
6778 var renderer = cm.getRenderer(i);
6782 if(typeof(renderer) !== 'undefined'){
6783 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6785 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6786 // and are rendered into the cells after the row is rendered - using the id for the element.
6788 if(typeof(value) === 'object'){
6798 rowIndex : rowIndex,
6803 this.fireEvent('rowclass', this, rowcfg);
6807 cls : rowcfg.rowClass + ' x-col-' + i,
6809 html: (typeof(value) === 'object') ? '' : value
6816 if(typeof(config.colspan) != 'undefined'){
6817 td.colspan = config.colspan;
6820 if(typeof(config.hidden) != 'undefined' && config.hidden){
6821 td.style += ' display:none;';
6824 if(typeof(config.align) != 'undefined' && config.align.length){
6825 td.style += ' text-align:' + config.align + ';';
6827 if(typeof(config.valign) != 'undefined' && config.valign.length){
6828 td.style += ' vertical-align:' + config.valign + ';';
6831 if(typeof(config.width) != 'undefined'){
6832 td.style += ' width:' + config.width + 'px;';
6835 if(typeof(config.cursor) != 'undefined'){
6836 td.style += ' cursor:' + config.cursor + ';';
6839 if(typeof(config.cls) != 'undefined'){
6840 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6843 ['xs','sm','md','lg'].map(function(size){
6845 if(typeof(config[size]) == 'undefined'){
6849 if (!config[size]) { // 0 = hidden
6850 td.cls += ' hidden-' + size;
6854 td.cls += ' col-' + size + '-' + config[size];
6862 row.cellObjects = cellObjects;
6870 onBeforeLoad : function()
6879 this.el.select('tbody', true).first().dom.innerHTML = '';
6882 * Show or hide a row.
6883 * @param {Number} rowIndex to show or hide
6884 * @param {Boolean} state hide
6886 setRowVisibility : function(rowIndex, state)
6888 var bt = this.mainBody.dom;
6890 var rows = this.el.select('tbody > tr', true).elements;
6892 if(typeof(rows[rowIndex]) == 'undefined'){
6895 rows[rowIndex].dom.style.display = state ? '' : 'none';
6899 getSelectionModel : function(){
6901 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6903 return this.selModel;
6906 * Render the Roo.bootstrap object from renderder
6908 renderCellObject : function(r)
6912 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6914 var t = r.cfg.render(r.container);
6917 Roo.each(r.cfg.cn, function(c){
6919 container: t.getChildContainer(),
6922 _this.renderCellObject(child);
6927 getRowIndex : function(row)
6931 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6942 * Returns the grid's underlying element = used by panel.Grid
6943 * @return {Element} The element
6945 getGridEl : function(){
6949 * Forces a resize - used by panel.Grid
6950 * @return {Element} The element
6952 autoSize : function()
6954 //var ctr = Roo.get(this.container.dom.parentElement);
6955 var ctr = Roo.get(this.el.dom);
6957 var thd = this.getGridEl().select('thead',true).first();
6958 var tbd = this.getGridEl().select('tbody', true).first();
6959 var tfd = this.getGridEl().select('tfoot', true).first();
6961 var cw = ctr.getWidth();
6965 tbd.setSize(ctr.getWidth(),
6966 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6968 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6971 cw = Math.max(cw, this.totalWidth);
6972 this.getGridEl().select('tr',true).setWidth(cw);
6973 // resize 'expandable coloumn?
6975 return; // we doe not have a view in this design..
6978 onBodyScroll: function()
6980 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6982 this.mainHead.setStyle({
6983 'position' : 'relative',
6984 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6990 var scrollHeight = this.mainBody.dom.scrollHeight;
6992 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6994 var height = this.mainBody.getHeight();
6996 if(scrollHeight - height == scrollTop) {
6998 var total = this.ds.getTotalCount();
7000 if(this.footer.cursor + this.footer.pageSize < total){
7002 this.footer.ds.load({
7004 start : this.footer.cursor + this.footer.pageSize,
7005 limit : this.footer.pageSize
7015 onHeaderChange : function()
7017 var header = this.renderHeader();
7018 var table = this.el.select('table', true).first();
7020 this.mainHead.remove();
7021 this.mainHead = table.createChild(header, this.mainBody, false);
7024 onHiddenChange : function(colModel, colIndex, hidden)
7026 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7027 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7029 this.CSS.updateRule(thSelector, "display", "");
7030 this.CSS.updateRule(tdSelector, "display", "");
7033 this.CSS.updateRule(thSelector, "display", "none");
7034 this.CSS.updateRule(tdSelector, "display", "none");
7037 this.onHeaderChange();
7054 * @class Roo.bootstrap.TableCell
7055 * @extends Roo.bootstrap.Component
7056 * Bootstrap TableCell class
7057 * @cfg {String} html cell contain text
7058 * @cfg {String} cls cell class
7059 * @cfg {String} tag cell tag (td|th) default td
7060 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7061 * @cfg {String} align Aligns the content in a cell
7062 * @cfg {String} axis Categorizes cells
7063 * @cfg {String} bgcolor Specifies the background color of a cell
7064 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7065 * @cfg {Number} colspan Specifies the number of columns a cell should span
7066 * @cfg {String} headers Specifies one or more header cells a cell is related to
7067 * @cfg {Number} height Sets the height of a cell
7068 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7069 * @cfg {Number} rowspan Sets the number of rows a cell should span
7070 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7071 * @cfg {String} valign Vertical aligns the content in a cell
7072 * @cfg {Number} width Specifies the width of a cell
7075 * Create a new TableCell
7076 * @param {Object} config The config object
7079 Roo.bootstrap.TableCell = function(config){
7080 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7083 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7103 getAutoCreate : function(){
7104 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7124 cfg.align=this.align
7130 cfg.bgcolor=this.bgcolor
7133 cfg.charoff=this.charoff
7136 cfg.colspan=this.colspan
7139 cfg.headers=this.headers
7142 cfg.height=this.height
7145 cfg.nowrap=this.nowrap
7148 cfg.rowspan=this.rowspan
7151 cfg.scope=this.scope
7154 cfg.valign=this.valign
7157 cfg.width=this.width
7176 * @class Roo.bootstrap.TableRow
7177 * @extends Roo.bootstrap.Component
7178 * Bootstrap TableRow class
7179 * @cfg {String} cls row class
7180 * @cfg {String} align Aligns the content in a table row
7181 * @cfg {String} bgcolor Specifies a background color for a table row
7182 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7183 * @cfg {String} valign Vertical aligns the content in a table row
7186 * Create a new TableRow
7187 * @param {Object} config The config object
7190 Roo.bootstrap.TableRow = function(config){
7191 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7194 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7202 getAutoCreate : function(){
7203 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7213 cfg.align = this.align;
7216 cfg.bgcolor = this.bgcolor;
7219 cfg.charoff = this.charoff;
7222 cfg.valign = this.valign;
7240 * @class Roo.bootstrap.TableBody
7241 * @extends Roo.bootstrap.Component
7242 * Bootstrap TableBody class
7243 * @cfg {String} cls element class
7244 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7245 * @cfg {String} align Aligns the content inside the element
7246 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7247 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7250 * Create a new TableBody
7251 * @param {Object} config The config object
7254 Roo.bootstrap.TableBody = function(config){
7255 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7258 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7266 getAutoCreate : function(){
7267 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7281 cfg.align = this.align;
7284 cfg.charoff = this.charoff;
7287 cfg.valign = this.valign;
7294 // initEvents : function()
7301 // this.store = Roo.factory(this.store, Roo.data);
7302 // this.store.on('load', this.onLoad, this);
7304 // this.store.load();
7308 // onLoad: function ()
7310 // this.fireEvent('load', this);
7320 * Ext JS Library 1.1.1
7321 * Copyright(c) 2006-2007, Ext JS, LLC.
7323 * Originally Released Under LGPL - original licence link has changed is not relivant.
7326 * <script type="text/javascript">
7329 // as we use this in bootstrap.
7330 Roo.namespace('Roo.form');
7332 * @class Roo.form.Action
7333 * Internal Class used to handle form actions
7335 * @param {Roo.form.BasicForm} el The form element or its id
7336 * @param {Object} config Configuration options
7341 // define the action interface
7342 Roo.form.Action = function(form, options){
7344 this.options = options || {};
7347 * Client Validation Failed
7350 Roo.form.Action.CLIENT_INVALID = 'client';
7352 * Server Validation Failed
7355 Roo.form.Action.SERVER_INVALID = 'server';
7357 * Connect to Server Failed
7360 Roo.form.Action.CONNECT_FAILURE = 'connect';
7362 * Reading Data from Server Failed
7365 Roo.form.Action.LOAD_FAILURE = 'load';
7367 Roo.form.Action.prototype = {
7369 failureType : undefined,
7370 response : undefined,
7374 run : function(options){
7379 success : function(response){
7384 handleResponse : function(response){
7388 // default connection failure
7389 failure : function(response){
7391 this.response = response;
7392 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7393 this.form.afterAction(this, false);
7396 processResponse : function(response){
7397 this.response = response;
7398 if(!response.responseText){
7401 this.result = this.handleResponse(response);
7405 // utility functions used internally
7406 getUrl : function(appendParams){
7407 var url = this.options.url || this.form.url || this.form.el.dom.action;
7409 var p = this.getParams();
7411 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7417 getMethod : function(){
7418 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7421 getParams : function(){
7422 var bp = this.form.baseParams;
7423 var p = this.options.params;
7425 if(typeof p == "object"){
7426 p = Roo.urlEncode(Roo.applyIf(p, bp));
7427 }else if(typeof p == 'string' && bp){
7428 p += '&' + Roo.urlEncode(bp);
7431 p = Roo.urlEncode(bp);
7436 createCallback : function(){
7438 success: this.success,
7439 failure: this.failure,
7441 timeout: (this.form.timeout*1000),
7442 upload: this.form.fileUpload ? this.success : undefined
7447 Roo.form.Action.Submit = function(form, options){
7448 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7451 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7454 haveProgress : false,
7455 uploadComplete : false,
7457 // uploadProgress indicator.
7458 uploadProgress : function()
7460 if (!this.form.progressUrl) {
7464 if (!this.haveProgress) {
7465 Roo.MessageBox.progress("Uploading", "Uploading");
7467 if (this.uploadComplete) {
7468 Roo.MessageBox.hide();
7472 this.haveProgress = true;
7474 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7476 var c = new Roo.data.Connection();
7478 url : this.form.progressUrl,
7483 success : function(req){
7484 //console.log(data);
7488 rdata = Roo.decode(req.responseText)
7490 Roo.log("Invalid data from server..");
7494 if (!rdata || !rdata.success) {
7496 Roo.MessageBox.alert(Roo.encode(rdata));
7499 var data = rdata.data;
7501 if (this.uploadComplete) {
7502 Roo.MessageBox.hide();
7507 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7508 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7511 this.uploadProgress.defer(2000,this);
7514 failure: function(data) {
7515 Roo.log('progress url failed ');
7526 // run get Values on the form, so it syncs any secondary forms.
7527 this.form.getValues();
7529 var o = this.options;
7530 var method = this.getMethod();
7531 var isPost = method == 'POST';
7532 if(o.clientValidation === false || this.form.isValid()){
7534 if (this.form.progressUrl) {
7535 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7536 (new Date() * 1) + '' + Math.random());
7541 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7542 form:this.form.el.dom,
7543 url:this.getUrl(!isPost),
7545 params:isPost ? this.getParams() : null,
7546 isUpload: this.form.fileUpload
7549 this.uploadProgress();
7551 }else if (o.clientValidation !== false){ // client validation failed
7552 this.failureType = Roo.form.Action.CLIENT_INVALID;
7553 this.form.afterAction(this, false);
7557 success : function(response)
7559 this.uploadComplete= true;
7560 if (this.haveProgress) {
7561 Roo.MessageBox.hide();
7565 var result = this.processResponse(response);
7566 if(result === true || result.success){
7567 this.form.afterAction(this, true);
7571 this.form.markInvalid(result.errors);
7572 this.failureType = Roo.form.Action.SERVER_INVALID;
7574 this.form.afterAction(this, false);
7576 failure : function(response)
7578 this.uploadComplete= true;
7579 if (this.haveProgress) {
7580 Roo.MessageBox.hide();
7583 this.response = response;
7584 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7585 this.form.afterAction(this, false);
7588 handleResponse : function(response){
7589 if(this.form.errorReader){
7590 var rs = this.form.errorReader.read(response);
7593 for(var i = 0, len = rs.records.length; i < len; i++) {
7594 var r = rs.records[i];
7598 if(errors.length < 1){
7602 success : rs.success,
7608 ret = Roo.decode(response.responseText);
7612 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7622 Roo.form.Action.Load = function(form, options){
7623 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7624 this.reader = this.form.reader;
7627 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7632 Roo.Ajax.request(Roo.apply(
7633 this.createCallback(), {
7634 method:this.getMethod(),
7635 url:this.getUrl(false),
7636 params:this.getParams()
7640 success : function(response){
7642 var result = this.processResponse(response);
7643 if(result === true || !result.success || !result.data){
7644 this.failureType = Roo.form.Action.LOAD_FAILURE;
7645 this.form.afterAction(this, false);
7648 this.form.clearInvalid();
7649 this.form.setValues(result.data);
7650 this.form.afterAction(this, true);
7653 handleResponse : function(response){
7654 if(this.form.reader){
7655 var rs = this.form.reader.read(response);
7656 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7658 success : rs.success,
7662 return Roo.decode(response.responseText);
7666 Roo.form.Action.ACTION_TYPES = {
7667 'load' : Roo.form.Action.Load,
7668 'submit' : Roo.form.Action.Submit
7677 * @class Roo.bootstrap.Form
7678 * @extends Roo.bootstrap.Component
7679 * Bootstrap Form class
7680 * @cfg {String} method GET | POST (default POST)
7681 * @cfg {String} labelAlign top | left (default top)
7682 * @cfg {String} align left | right - for navbars
7683 * @cfg {Boolean} loadMask load mask when submit (default true)
7688 * @param {Object} config The config object
7692 Roo.bootstrap.Form = function(config){
7694 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7696 Roo.bootstrap.Form.popover.apply();
7700 * @event clientvalidation
7701 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7702 * @param {Form} this
7703 * @param {Boolean} valid true if the form has passed client-side validation
7705 clientvalidation: true,
7707 * @event beforeaction
7708 * Fires before any action is performed. Return false to cancel the action.
7709 * @param {Form} this
7710 * @param {Action} action The action to be performed
7714 * @event actionfailed
7715 * Fires when an action fails.
7716 * @param {Form} this
7717 * @param {Action} action The action that failed
7719 actionfailed : true,
7721 * @event actioncomplete
7722 * Fires when an action is completed.
7723 * @param {Form} this
7724 * @param {Action} action The action that completed
7726 actioncomplete : true
7730 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7733 * @cfg {String} method
7734 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7739 * The URL to use for form actions if one isn't supplied in the action options.
7742 * @cfg {Boolean} fileUpload
7743 * Set to true if this form is a file upload.
7747 * @cfg {Object} baseParams
7748 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7752 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7756 * @cfg {Sting} align (left|right) for navbar forms
7761 activeAction : null,
7764 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7765 * element by passing it or its id or mask the form itself by passing in true.
7768 waitMsgTarget : false,
7773 * @cfg {Boolean} errorMask (true|false) default false
7778 * @cfg {Number} maskOffset Default 100
7783 * @cfg {Boolean} maskBody
7787 getAutoCreate : function(){
7791 method : this.method || 'POST',
7792 id : this.id || Roo.id(),
7795 if (this.parent().xtype.match(/^Nav/)) {
7796 cfg.cls = 'navbar-form navbar-' + this.align;
7800 if (this.labelAlign == 'left' ) {
7801 cfg.cls += ' form-horizontal';
7807 initEvents : function()
7809 this.el.on('submit', this.onSubmit, this);
7810 // this was added as random key presses on the form where triggering form submit.
7811 this.el.on('keypress', function(e) {
7812 if (e.getCharCode() != 13) {
7815 // we might need to allow it for textareas.. and some other items.
7816 // check e.getTarget().
7818 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7822 Roo.log("keypress blocked");
7830 onSubmit : function(e){
7835 * Returns true if client-side validation on the form is successful.
7838 isValid : function(){
7839 var items = this.getItems();
7843 items.each(function(f){
7849 Roo.log('invalid field: ' + f.name);
7853 if(!target && f.el.isVisible(true)){
7859 if(this.errorMask && !valid){
7860 Roo.bootstrap.Form.popover.mask(this, target);
7867 * Returns true if any fields in this form have changed since their original load.
7870 isDirty : function(){
7872 var items = this.getItems();
7873 items.each(function(f){
7883 * Performs a predefined action (submit or load) or custom actions you define on this form.
7884 * @param {String} actionName The name of the action type
7885 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7886 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7887 * accept other config options):
7889 Property Type Description
7890 ---------------- --------------- ----------------------------------------------------------------------------------
7891 url String The url for the action (defaults to the form's url)
7892 method String The form method to use (defaults to the form's method, or POST if not defined)
7893 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7894 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7895 validate the form on the client (defaults to false)
7897 * @return {BasicForm} this
7899 doAction : function(action, options){
7900 if(typeof action == 'string'){
7901 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7903 if(this.fireEvent('beforeaction', this, action) !== false){
7904 this.beforeAction(action);
7905 action.run.defer(100, action);
7911 beforeAction : function(action){
7912 var o = action.options;
7917 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7919 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7922 // not really supported yet.. ??
7924 //if(this.waitMsgTarget === true){
7925 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7926 //}else if(this.waitMsgTarget){
7927 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7928 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7930 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7936 afterAction : function(action, success){
7937 this.activeAction = null;
7938 var o = action.options;
7943 Roo.get(document.body).unmask();
7949 //if(this.waitMsgTarget === true){
7950 // this.el.unmask();
7951 //}else if(this.waitMsgTarget){
7952 // this.waitMsgTarget.unmask();
7954 // Roo.MessageBox.updateProgress(1);
7955 // Roo.MessageBox.hide();
7962 Roo.callback(o.success, o.scope, [this, action]);
7963 this.fireEvent('actioncomplete', this, action);
7967 // failure condition..
7968 // we have a scenario where updates need confirming.
7969 // eg. if a locking scenario exists..
7970 // we look for { errors : { needs_confirm : true }} in the response.
7972 (typeof(action.result) != 'undefined') &&
7973 (typeof(action.result.errors) != 'undefined') &&
7974 (typeof(action.result.errors.needs_confirm) != 'undefined')
7977 Roo.log("not supported yet");
7980 Roo.MessageBox.confirm(
7981 "Change requires confirmation",
7982 action.result.errorMsg,
7987 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7997 Roo.callback(o.failure, o.scope, [this, action]);
7998 // show an error message if no failed handler is set..
7999 if (!this.hasListener('actionfailed')) {
8000 Roo.log("need to add dialog support");
8002 Roo.MessageBox.alert("Error",
8003 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8004 action.result.errorMsg :
8005 "Saving Failed, please check your entries or try again"
8010 this.fireEvent('actionfailed', this, action);
8015 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8016 * @param {String} id The value to search for
8019 findField : function(id){
8020 var items = this.getItems();
8021 var field = items.get(id);
8023 items.each(function(f){
8024 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8031 return field || null;
8034 * Mark fields in this form invalid in bulk.
8035 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8036 * @return {BasicForm} this
8038 markInvalid : function(errors){
8039 if(errors instanceof Array){
8040 for(var i = 0, len = errors.length; i < len; i++){
8041 var fieldError = errors[i];
8042 var f = this.findField(fieldError.id);
8044 f.markInvalid(fieldError.msg);
8050 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8051 field.markInvalid(errors[id]);
8055 //Roo.each(this.childForms || [], function (f) {
8056 // f.markInvalid(errors);
8063 * Set values for fields in this form in bulk.
8064 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8065 * @return {BasicForm} this
8067 setValues : function(values){
8068 if(values instanceof Array){ // array of objects
8069 for(var i = 0, len = values.length; i < len; i++){
8071 var f = this.findField(v.id);
8073 f.setValue(v.value);
8074 if(this.trackResetOnLoad){
8075 f.originalValue = f.getValue();
8079 }else{ // object hash
8082 if(typeof values[id] != 'function' && (field = this.findField(id))){
8084 if (field.setFromData &&
8086 field.displayField &&
8087 // combos' with local stores can
8088 // be queried via setValue()
8089 // to set their value..
8090 (field.store && !field.store.isLocal)
8094 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8095 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8096 field.setFromData(sd);
8098 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8100 field.setFromData(values);
8103 field.setValue(values[id]);
8107 if(this.trackResetOnLoad){
8108 field.originalValue = field.getValue();
8114 //Roo.each(this.childForms || [], function (f) {
8115 // f.setValues(values);
8122 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8123 * they are returned as an array.
8124 * @param {Boolean} asString
8127 getValues : function(asString){
8128 //if (this.childForms) {
8129 // copy values from the child forms
8130 // Roo.each(this.childForms, function (f) {
8131 // this.setValues(f.getValues());
8137 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8138 if(asString === true){
8141 return Roo.urlDecode(fs);
8145 * Returns the fields in this form as an object with key/value pairs.
8146 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8149 getFieldValues : function(with_hidden)
8151 var items = this.getItems();
8153 items.each(function(f){
8159 var v = f.getValue();
8161 if (f.inputType =='radio') {
8162 if (typeof(ret[f.getName()]) == 'undefined') {
8163 ret[f.getName()] = ''; // empty..
8166 if (!f.el.dom.checked) {
8174 if(f.xtype == 'MoneyField'){
8175 ret[f.currencyName] = f.getCurrency();
8178 // not sure if this supported any more..
8179 if ((typeof(v) == 'object') && f.getRawValue) {
8180 v = f.getRawValue() ; // dates..
8182 // combo boxes where name != hiddenName...
8183 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8184 ret[f.name] = f.getRawValue();
8186 ret[f.getName()] = v;
8193 * Clears all invalid messages in this form.
8194 * @return {BasicForm} this
8196 clearInvalid : function(){
8197 var items = this.getItems();
8199 items.each(function(f){
8208 * @return {BasicForm} this
8211 var items = this.getItems();
8212 items.each(function(f){
8216 Roo.each(this.childForms || [], function (f) {
8224 getItems : function()
8226 var r=new Roo.util.MixedCollection(false, function(o){
8227 return o.id || (o.id = Roo.id());
8229 var iter = function(el) {
8236 Roo.each(el.items,function(e) {
8245 hideFields : function(items)
8247 Roo.each(items, function(i){
8249 var f = this.findField(i);
8255 if(f.xtype == 'DateField'){
8256 f.setVisible(false);
8265 showFields : function(items)
8267 Roo.each(items, function(i){
8269 var f = this.findField(i);
8275 if(f.xtype == 'DateField'){
8287 Roo.apply(Roo.bootstrap.Form, {
8314 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8315 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8316 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8317 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8320 this.maskEl.top.enableDisplayMode("block");
8321 this.maskEl.left.enableDisplayMode("block");
8322 this.maskEl.bottom.enableDisplayMode("block");
8323 this.maskEl.right.enableDisplayMode("block");
8325 this.toolTip = new Roo.bootstrap.Tooltip({
8326 cls : 'roo-form-error-popover',
8328 'left' : ['r-l', [-2,0], 'right'],
8329 'right' : ['l-r', [2,0], 'left'],
8330 'bottom' : ['tl-bl', [0,2], 'top'],
8331 'top' : [ 'bl-tl', [0,-2], 'bottom']
8335 this.toolTip.render(Roo.get(document.body));
8337 this.toolTip.el.enableDisplayMode("block");
8339 Roo.get(document.body).on('click', function(){
8343 Roo.get(document.body).on('touchstart', function(){
8347 this.isApplied = true
8350 mask : function(form, target)
8354 this.target = target;
8356 if(!this.form.errorMask || !target.el){
8360 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8362 Roo.log(scrollable);
8364 var ot = this.target.el.calcOffsetsTo(scrollable);
8366 var scrollTo = ot[1] - this.form.maskOffset;
8368 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8370 scrollable.scrollTo('top', scrollTo);
8372 var box = this.target.el.getBox();
8374 var zIndex = Roo.bootstrap.Modal.zIndex++;
8377 this.maskEl.top.setStyle('position', 'absolute');
8378 this.maskEl.top.setStyle('z-index', zIndex);
8379 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8380 this.maskEl.top.setLeft(0);
8381 this.maskEl.top.setTop(0);
8382 this.maskEl.top.show();
8384 this.maskEl.left.setStyle('position', 'absolute');
8385 this.maskEl.left.setStyle('z-index', zIndex);
8386 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8387 this.maskEl.left.setLeft(0);
8388 this.maskEl.left.setTop(box.y - this.padding);
8389 this.maskEl.left.show();
8391 this.maskEl.bottom.setStyle('position', 'absolute');
8392 this.maskEl.bottom.setStyle('z-index', zIndex);
8393 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8394 this.maskEl.bottom.setLeft(0);
8395 this.maskEl.bottom.setTop(box.bottom + this.padding);
8396 this.maskEl.bottom.show();
8398 this.maskEl.right.setStyle('position', 'absolute');
8399 this.maskEl.right.setStyle('z-index', zIndex);
8400 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8401 this.maskEl.right.setLeft(box.right + this.padding);
8402 this.maskEl.right.setTop(box.y - this.padding);
8403 this.maskEl.right.show();
8405 this.toolTip.bindEl = this.target.el;
8407 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8409 var tip = this.target.blankText;
8411 if(this.target.getValue() !== '' ) {
8413 if (this.target.invalidText.length) {
8414 tip = this.target.invalidText;
8415 } else if (this.target.regexText.length){
8416 tip = this.target.regexText;
8420 this.toolTip.show(tip);
8422 this.intervalID = window.setInterval(function() {
8423 Roo.bootstrap.Form.popover.unmask();
8426 window.onwheel = function(){ return false;};
8428 (function(){ this.isMasked = true; }).defer(500, this);
8434 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8438 this.maskEl.top.setStyle('position', 'absolute');
8439 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8440 this.maskEl.top.hide();
8442 this.maskEl.left.setStyle('position', 'absolute');
8443 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8444 this.maskEl.left.hide();
8446 this.maskEl.bottom.setStyle('position', 'absolute');
8447 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8448 this.maskEl.bottom.hide();
8450 this.maskEl.right.setStyle('position', 'absolute');
8451 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8452 this.maskEl.right.hide();
8454 this.toolTip.hide();
8456 this.toolTip.el.hide();
8458 window.onwheel = function(){ return true;};
8460 if(this.intervalID){
8461 window.clearInterval(this.intervalID);
8462 this.intervalID = false;
8465 this.isMasked = false;
8475 * Ext JS Library 1.1.1
8476 * Copyright(c) 2006-2007, Ext JS, LLC.
8478 * Originally Released Under LGPL - original licence link has changed is not relivant.
8481 * <script type="text/javascript">
8484 * @class Roo.form.VTypes
8485 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8488 Roo.form.VTypes = function(){
8489 // closure these in so they are only created once.
8490 var alpha = /^[a-zA-Z_]+$/;
8491 var alphanum = /^[a-zA-Z0-9_]+$/;
8492 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8493 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8495 // All these messages and functions are configurable
8498 * The function used to validate email addresses
8499 * @param {String} value The email address
8501 'email' : function(v){
8502 return email.test(v);
8505 * The error text to display when the email validation function returns false
8508 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8510 * The keystroke filter mask to be applied on email input
8513 'emailMask' : /[a-z0-9_\.\-@]/i,
8516 * The function used to validate URLs
8517 * @param {String} value The URL
8519 'url' : function(v){
8523 * The error text to display when the url validation function returns false
8526 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8529 * The function used to validate alpha values
8530 * @param {String} value The value
8532 'alpha' : function(v){
8533 return alpha.test(v);
8536 * The error text to display when the alpha validation function returns false
8539 'alphaText' : 'This field should only contain letters and _',
8541 * The keystroke filter mask to be applied on alpha input
8544 'alphaMask' : /[a-z_]/i,
8547 * The function used to validate alphanumeric values
8548 * @param {String} value The value
8550 'alphanum' : function(v){
8551 return alphanum.test(v);
8554 * The error text to display when the alphanumeric validation function returns false
8557 'alphanumText' : 'This field should only contain letters, numbers and _',
8559 * The keystroke filter mask to be applied on alphanumeric input
8562 'alphanumMask' : /[a-z0-9_]/i
8572 * @class Roo.bootstrap.Input
8573 * @extends Roo.bootstrap.Component
8574 * Bootstrap Input class
8575 * @cfg {Boolean} disabled is it disabled
8576 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8577 * @cfg {String} name name of the input
8578 * @cfg {string} fieldLabel - the label associated
8579 * @cfg {string} placeholder - placeholder to put in text.
8580 * @cfg {string} before - input group add on before
8581 * @cfg {string} after - input group add on after
8582 * @cfg {string} size - (lg|sm) or leave empty..
8583 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8584 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8585 * @cfg {Number} md colspan out of 12 for computer-sized screens
8586 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8587 * @cfg {string} value default value of the input
8588 * @cfg {Number} labelWidth set the width of label
8589 * @cfg {Number} labellg set the width of label (1-12)
8590 * @cfg {Number} labelmd set the width of label (1-12)
8591 * @cfg {Number} labelsm set the width of label (1-12)
8592 * @cfg {Number} labelxs set the width of label (1-12)
8593 * @cfg {String} labelAlign (top|left)
8594 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8595 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8596 * @cfg {String} indicatorpos (left|right) default left
8597 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8598 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8600 * @cfg {String} align (left|center|right) Default left
8601 * @cfg {Boolean} forceFeedback (true|false) Default false
8604 * Create a new Input
8605 * @param {Object} config The config object
8608 Roo.bootstrap.Input = function(config){
8610 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8615 * Fires when this field receives input focus.
8616 * @param {Roo.form.Field} this
8621 * Fires when this field loses input focus.
8622 * @param {Roo.form.Field} this
8627 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8628 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8629 * @param {Roo.form.Field} this
8630 * @param {Roo.EventObject} e The event object
8635 * Fires just before the field blurs if the field value has changed.
8636 * @param {Roo.form.Field} this
8637 * @param {Mixed} newValue The new value
8638 * @param {Mixed} oldValue The original value
8643 * Fires after the field has been marked as invalid.
8644 * @param {Roo.form.Field} this
8645 * @param {String} msg The validation message
8650 * Fires after the field has been validated with no errors.
8651 * @param {Roo.form.Field} this
8656 * Fires after the key up
8657 * @param {Roo.form.Field} this
8658 * @param {Roo.EventObject} e The event Object
8664 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8666 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8667 automatic validation (defaults to "keyup").
8669 validationEvent : "keyup",
8671 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8673 validateOnBlur : true,
8675 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8677 validationDelay : 250,
8679 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8681 focusClass : "x-form-focus", // not needed???
8685 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8687 invalidClass : "has-warning",
8690 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8692 validClass : "has-success",
8695 * @cfg {Boolean} hasFeedback (true|false) default true
8700 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8702 invalidFeedbackClass : "glyphicon-warning-sign",
8705 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8707 validFeedbackClass : "glyphicon-ok",
8710 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8712 selectOnFocus : false,
8715 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8719 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8724 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8726 disableKeyFilter : false,
8729 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8733 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8737 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8739 blankText : "Please complete this mandatory field",
8742 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8746 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8748 maxLength : Number.MAX_VALUE,
8750 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8752 minLengthText : "The minimum length for this field is {0}",
8754 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8756 maxLengthText : "The maximum length for this field is {0}",
8760 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8761 * If available, this function will be called only after the basic validators all return true, and will be passed the
8762 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8766 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8767 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8768 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8772 * @cfg {String} regexText -- Depricated - use Invalid Text
8777 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8783 autocomplete: false,
8802 formatedValue : false,
8803 forceFeedback : false,
8805 indicatorpos : 'left',
8815 parentLabelAlign : function()
8818 while (parent.parent()) {
8819 parent = parent.parent();
8820 if (typeof(parent.labelAlign) !='undefined') {
8821 return parent.labelAlign;
8828 getAutoCreate : function()
8830 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8836 if(this.inputType != 'hidden'){
8837 cfg.cls = 'form-group' //input-group
8843 type : this.inputType,
8845 cls : 'form-control',
8846 placeholder : this.placeholder || '',
8847 autocomplete : this.autocomplete || 'new-password'
8850 if(this.capture.length){
8851 input.capture = this.capture;
8854 if(this.accept.length){
8855 input.accept = this.accept + "/*";
8859 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8862 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8863 input.maxLength = this.maxLength;
8866 if (this.disabled) {
8867 input.disabled=true;
8870 if (this.readOnly) {
8871 input.readonly=true;
8875 input.name = this.name;
8879 input.cls += ' input-' + this.size;
8883 ['xs','sm','md','lg'].map(function(size){
8884 if (settings[size]) {
8885 cfg.cls += ' col-' + size + '-' + settings[size];
8889 var inputblock = input;
8893 cls: 'glyphicon form-control-feedback'
8896 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8899 cls : 'has-feedback',
8907 if (this.before || this.after) {
8910 cls : 'input-group',
8914 if (this.before && typeof(this.before) == 'string') {
8916 inputblock.cn.push({
8918 cls : 'roo-input-before input-group-addon',
8922 if (this.before && typeof(this.before) == 'object') {
8923 this.before = Roo.factory(this.before);
8925 inputblock.cn.push({
8927 cls : 'roo-input-before input-group-' +
8928 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8932 inputblock.cn.push(input);
8934 if (this.after && typeof(this.after) == 'string') {
8935 inputblock.cn.push({
8937 cls : 'roo-input-after input-group-addon',
8941 if (this.after && typeof(this.after) == 'object') {
8942 this.after = Roo.factory(this.after);
8944 inputblock.cn.push({
8946 cls : 'roo-input-after input-group-' +
8947 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8951 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8952 inputblock.cls += ' has-feedback';
8953 inputblock.cn.push(feedback);
8957 if (align ==='left' && this.fieldLabel.length) {
8959 cfg.cls += ' roo-form-group-label-left';
8964 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8965 tooltip : 'This field is required'
8970 cls : 'control-label',
8971 html : this.fieldLabel
8982 var labelCfg = cfg.cn[1];
8983 var contentCfg = cfg.cn[2];
8985 if(this.indicatorpos == 'right'){
8990 cls : 'control-label',
8994 html : this.fieldLabel
8998 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8999 tooltip : 'This field is required'
9012 labelCfg = cfg.cn[0];
9013 contentCfg = cfg.cn[1];
9017 if(this.labelWidth > 12){
9018 labelCfg.style = "width: " + this.labelWidth + 'px';
9021 if(this.labelWidth < 13 && this.labelmd == 0){
9022 this.labelmd = this.labelWidth;
9025 if(this.labellg > 0){
9026 labelCfg.cls += ' col-lg-' + this.labellg;
9027 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9030 if(this.labelmd > 0){
9031 labelCfg.cls += ' col-md-' + this.labelmd;
9032 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9035 if(this.labelsm > 0){
9036 labelCfg.cls += ' col-sm-' + this.labelsm;
9037 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9040 if(this.labelxs > 0){
9041 labelCfg.cls += ' col-xs-' + this.labelxs;
9042 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9046 } else if ( this.fieldLabel.length) {
9051 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9052 tooltip : 'This field is required'
9056 //cls : 'input-group-addon',
9057 html : this.fieldLabel
9065 if(this.indicatorpos == 'right'){
9070 //cls : 'input-group-addon',
9071 html : this.fieldLabel
9076 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9077 tooltip : 'This field is required'
9097 if (this.parentType === 'Navbar' && this.parent().bar) {
9098 cfg.cls += ' navbar-form';
9101 if (this.parentType === 'NavGroup') {
9102 cfg.cls += ' navbar-form';
9110 * return the real input element.
9112 inputEl: function ()
9114 return this.el.select('input.form-control',true).first();
9117 tooltipEl : function()
9119 return this.inputEl();
9122 indicatorEl : function()
9124 var indicator = this.el.select('i.roo-required-indicator',true).first();
9134 setDisabled : function(v)
9136 var i = this.inputEl().dom;
9138 i.removeAttribute('disabled');
9142 i.setAttribute('disabled','true');
9144 initEvents : function()
9147 this.inputEl().on("keydown" , this.fireKey, this);
9148 this.inputEl().on("focus", this.onFocus, this);
9149 this.inputEl().on("blur", this.onBlur, this);
9151 this.inputEl().relayEvent('keyup', this);
9153 this.indicator = this.indicatorEl();
9156 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9159 // reference to original value for reset
9160 this.originalValue = this.getValue();
9161 //Roo.form.TextField.superclass.initEvents.call(this);
9162 if(this.validationEvent == 'keyup'){
9163 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9164 this.inputEl().on('keyup', this.filterValidation, this);
9166 else if(this.validationEvent !== false){
9167 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9170 if(this.selectOnFocus){
9171 this.on("focus", this.preFocus, this);
9174 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9175 this.inputEl().on("keypress", this.filterKeys, this);
9177 this.inputEl().relayEvent('keypress', this);
9180 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9181 this.el.on("click", this.autoSize, this);
9184 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9185 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9188 if (typeof(this.before) == 'object') {
9189 this.before.render(this.el.select('.roo-input-before',true).first());
9191 if (typeof(this.after) == 'object') {
9192 this.after.render(this.el.select('.roo-input-after',true).first());
9195 this.inputEl().on('change', this.onChange, this);
9198 filterValidation : function(e){
9199 if(!e.isNavKeyPress()){
9200 this.validationTask.delay(this.validationDelay);
9204 * Validates the field value
9205 * @return {Boolean} True if the value is valid, else false
9207 validate : function(){
9208 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9209 if(this.disabled || this.validateValue(this.getRawValue())){
9220 * Validates a value according to the field's validation rules and marks the field as invalid
9221 * if the validation fails
9222 * @param {Mixed} value The value to validate
9223 * @return {Boolean} True if the value is valid, else false
9225 validateValue : function(value)
9227 if(this.getVisibilityEl().hasClass('hidden')){
9231 if(value.length < 1) { // if it's blank
9232 if(this.allowBlank){
9238 if(value.length < this.minLength){
9241 if(value.length > this.maxLength){
9245 var vt = Roo.form.VTypes;
9246 if(!vt[this.vtype](value, this)){
9250 if(typeof this.validator == "function"){
9251 var msg = this.validator(value);
9255 if (typeof(msg) == 'string') {
9256 this.invalidText = msg;
9260 if(this.regex && !this.regex.test(value)){
9268 fireKey : function(e){
9269 //Roo.log('field ' + e.getKey());
9270 if(e.isNavKeyPress()){
9271 this.fireEvent("specialkey", this, e);
9274 focus : function (selectText){
9276 this.inputEl().focus();
9277 if(selectText === true){
9278 this.inputEl().dom.select();
9284 onFocus : function(){
9285 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9286 // this.el.addClass(this.focusClass);
9289 this.hasFocus = true;
9290 this.startValue = this.getValue();
9291 this.fireEvent("focus", this);
9295 beforeBlur : Roo.emptyFn,
9299 onBlur : function(){
9301 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9302 //this.el.removeClass(this.focusClass);
9304 this.hasFocus = false;
9305 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9308 var v = this.getValue();
9309 if(String(v) !== String(this.startValue)){
9310 this.fireEvent('change', this, v, this.startValue);
9312 this.fireEvent("blur", this);
9315 onChange : function(e)
9317 var v = this.getValue();
9318 if(String(v) !== String(this.startValue)){
9319 this.fireEvent('change', this, v, this.startValue);
9325 * Resets the current field value to the originally loaded value and clears any validation messages
9328 this.setValue(this.originalValue);
9332 * Returns the name of the field
9333 * @return {Mixed} name The name field
9335 getName: function(){
9339 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9340 * @return {Mixed} value The field value
9342 getValue : function(){
9344 var v = this.inputEl().getValue();
9349 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9350 * @return {Mixed} value The field value
9352 getRawValue : function(){
9353 var v = this.inputEl().getValue();
9359 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9360 * @param {Mixed} value The value to set
9362 setRawValue : function(v){
9363 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9366 selectText : function(start, end){
9367 var v = this.getRawValue();
9369 start = start === undefined ? 0 : start;
9370 end = end === undefined ? v.length : end;
9371 var d = this.inputEl().dom;
9372 if(d.setSelectionRange){
9373 d.setSelectionRange(start, end);
9374 }else if(d.createTextRange){
9375 var range = d.createTextRange();
9376 range.moveStart("character", start);
9377 range.moveEnd("character", v.length-end);
9384 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9385 * @param {Mixed} value The value to set
9387 setValue : function(v){
9390 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9396 processValue : function(value){
9397 if(this.stripCharsRe){
9398 var newValue = value.replace(this.stripCharsRe, '');
9399 if(newValue !== value){
9400 this.setRawValue(newValue);
9407 preFocus : function(){
9409 if(this.selectOnFocus){
9410 this.inputEl().dom.select();
9413 filterKeys : function(e){
9415 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9418 var c = e.getCharCode(), cc = String.fromCharCode(c);
9419 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9422 if(!this.maskRe.test(cc)){
9427 * Clear any invalid styles/messages for this field
9429 clearInvalid : function(){
9431 if(!this.el || this.preventMark){ // not rendered
9436 this.el.removeClass(this.invalidClass);
9438 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9440 var feedback = this.el.select('.form-control-feedback', true).first();
9443 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9449 this.indicator.removeClass('visible');
9450 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9453 this.fireEvent('valid', this);
9457 * Mark this field as valid
9459 markValid : function()
9461 if(!this.el || this.preventMark){ // not rendered...
9465 this.el.removeClass([this.invalidClass, this.validClass]);
9467 var feedback = this.el.select('.form-control-feedback', true).first();
9470 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9474 this.indicator.removeClass('visible');
9475 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9482 if(this.allowBlank && !this.getRawValue().length){
9486 this.el.addClass(this.validClass);
9488 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9490 var feedback = this.el.select('.form-control-feedback', true).first();
9493 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9494 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9499 this.fireEvent('valid', this);
9503 * Mark this field as invalid
9504 * @param {String} msg The validation message
9506 markInvalid : function(msg)
9508 if(!this.el || this.preventMark){ // not rendered
9512 this.el.removeClass([this.invalidClass, this.validClass]);
9514 var feedback = this.el.select('.form-control-feedback', true).first();
9517 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9524 if(this.allowBlank && !this.getRawValue().length){
9529 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9530 this.indicator.addClass('visible');
9533 this.el.addClass(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, this.validFeedbackClass]);
9542 if(this.getValue().length || this.forceFeedback){
9543 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9550 this.fireEvent('invalid', this, msg);
9553 SafariOnKeyDown : function(event)
9555 // this is a workaround for a password hang bug on chrome/ webkit.
9556 if (this.inputEl().dom.type != 'password') {
9560 var isSelectAll = false;
9562 if(this.inputEl().dom.selectionEnd > 0){
9563 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9565 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9566 event.preventDefault();
9571 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9573 event.preventDefault();
9574 // this is very hacky as keydown always get's upper case.
9576 var cc = String.fromCharCode(event.getCharCode());
9577 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9581 adjustWidth : function(tag, w){
9582 tag = tag.toLowerCase();
9583 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9584 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9588 if(tag == 'textarea'){
9591 }else if(Roo.isOpera){
9595 if(tag == 'textarea'){
9603 setFieldLabel : function(v)
9610 var ar = this.el.select('label > span',true);
9612 if (ar.elements.length) {
9613 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9614 this.fieldLabel = v;
9618 var br = this.el.select('label',true);
9620 if(br.elements.length) {
9621 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9622 this.fieldLabel = v;
9626 Roo.log('Cannot Found any of label > span || label in input');
9630 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9631 this.fieldLabel = v;
9646 * @class Roo.bootstrap.TextArea
9647 * @extends Roo.bootstrap.Input
9648 * Bootstrap TextArea class
9649 * @cfg {Number} cols Specifies the visible width of a text area
9650 * @cfg {Number} rows Specifies the visible number of lines in a text area
9651 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9652 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9653 * @cfg {string} html text
9656 * Create a new TextArea
9657 * @param {Object} config The config object
9660 Roo.bootstrap.TextArea = function(config){
9661 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9665 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9675 getAutoCreate : function(){
9677 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9683 if(this.inputType != 'hidden'){
9684 cfg.cls = 'form-group' //input-group
9692 value : this.value || '',
9693 html: this.html || '',
9694 cls : 'form-control',
9695 placeholder : this.placeholder || ''
9699 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9700 input.maxLength = this.maxLength;
9704 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9708 input.cols = this.cols;
9711 if (this.readOnly) {
9712 input.readonly = true;
9716 input.name = this.name;
9720 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9724 ['xs','sm','md','lg'].map(function(size){
9725 if (settings[size]) {
9726 cfg.cls += ' col-' + size + '-' + settings[size];
9730 var inputblock = input;
9732 if(this.hasFeedback && !this.allowBlank){
9736 cls: 'glyphicon form-control-feedback'
9740 cls : 'has-feedback',
9749 if (this.before || this.after) {
9752 cls : 'input-group',
9756 inputblock.cn.push({
9758 cls : 'input-group-addon',
9763 inputblock.cn.push(input);
9765 if(this.hasFeedback && !this.allowBlank){
9766 inputblock.cls += ' has-feedback';
9767 inputblock.cn.push(feedback);
9771 inputblock.cn.push({
9773 cls : 'input-group-addon',
9780 if (align ==='left' && this.fieldLabel.length) {
9785 cls : 'control-label',
9786 html : this.fieldLabel
9797 if(this.labelWidth > 12){
9798 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9801 if(this.labelWidth < 13 && this.labelmd == 0){
9802 this.labelmd = this.labelWidth;
9805 if(this.labellg > 0){
9806 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9807 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9810 if(this.labelmd > 0){
9811 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9812 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9815 if(this.labelsm > 0){
9816 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9817 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9820 if(this.labelxs > 0){
9821 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9822 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9825 } else if ( this.fieldLabel.length) {
9830 //cls : 'input-group-addon',
9831 html : this.fieldLabel
9849 if (this.disabled) {
9850 input.disabled=true;
9857 * return the real textarea element.
9859 inputEl: function ()
9861 return this.el.select('textarea.form-control',true).first();
9865 * Clear any invalid styles/messages for this field
9867 clearInvalid : function()
9870 if(!this.el || this.preventMark){ // not rendered
9874 var label = this.el.select('label', true).first();
9875 var icon = this.el.select('i.fa-star', true).first();
9881 this.el.removeClass(this.invalidClass);
9883 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9885 var feedback = this.el.select('.form-control-feedback', true).first();
9888 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9893 this.fireEvent('valid', this);
9897 * Mark this field as valid
9899 markValid : function()
9901 if(!this.el || this.preventMark){ // not rendered
9905 this.el.removeClass([this.invalidClass, this.validClass]);
9907 var feedback = this.el.select('.form-control-feedback', true).first();
9910 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9913 if(this.disabled || this.allowBlank){
9917 var label = this.el.select('label', true).first();
9918 var icon = this.el.select('i.fa-star', true).first();
9924 this.el.addClass(this.validClass);
9926 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9928 var feedback = this.el.select('.form-control-feedback', true).first();
9931 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9932 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9937 this.fireEvent('valid', this);
9941 * Mark this field as invalid
9942 * @param {String} msg The validation message
9944 markInvalid : function(msg)
9946 if(!this.el || this.preventMark){ // not rendered
9950 this.el.removeClass([this.invalidClass, this.validClass]);
9952 var feedback = this.el.select('.form-control-feedback', true).first();
9955 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9958 if(this.disabled || this.allowBlank){
9962 var label = this.el.select('label', true).first();
9963 var icon = this.el.select('i.fa-star', true).first();
9965 if(!this.getValue().length && label && !icon){
9966 this.el.createChild({
9968 cls : 'text-danger fa fa-lg fa-star',
9969 tooltip : 'This field is required',
9970 style : 'margin-right:5px;'
9974 this.el.addClass(this.invalidClass);
9976 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9978 var feedback = this.el.select('.form-control-feedback', true).first();
9981 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9983 if(this.getValue().length || this.forceFeedback){
9984 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9991 this.fireEvent('invalid', this, msg);
9999 * trigger field - base class for combo..
10004 * @class Roo.bootstrap.TriggerField
10005 * @extends Roo.bootstrap.Input
10006 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10007 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10008 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10009 * for which you can provide a custom implementation. For example:
10011 var trigger = new Roo.bootstrap.TriggerField();
10012 trigger.onTriggerClick = myTriggerFn;
10013 trigger.applyTo('my-field');
10016 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10017 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10018 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10019 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10020 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10023 * Create a new TriggerField.
10024 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10025 * to the base TextField)
10027 Roo.bootstrap.TriggerField = function(config){
10028 this.mimicing = false;
10029 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10032 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10034 * @cfg {String} triggerClass A CSS class to apply to the trigger
10037 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10042 * @cfg {Boolean} removable (true|false) special filter default false
10046 /** @cfg {Boolean} grow @hide */
10047 /** @cfg {Number} growMin @hide */
10048 /** @cfg {Number} growMax @hide */
10054 autoSize: Roo.emptyFn,
10058 deferHeight : true,
10061 actionMode : 'wrap',
10066 getAutoCreate : function(){
10068 var align = this.labelAlign || this.parentLabelAlign();
10073 cls: 'form-group' //input-group
10080 type : this.inputType,
10081 cls : 'form-control',
10082 autocomplete: 'new-password',
10083 placeholder : this.placeholder || ''
10087 input.name = this.name;
10090 input.cls += ' input-' + this.size;
10093 if (this.disabled) {
10094 input.disabled=true;
10097 var inputblock = input;
10099 if(this.hasFeedback && !this.allowBlank){
10103 cls: 'glyphicon form-control-feedback'
10106 if(this.removable && !this.editable && !this.tickable){
10108 cls : 'has-feedback',
10114 cls : 'roo-combo-removable-btn close'
10121 cls : 'has-feedback',
10130 if(this.removable && !this.editable && !this.tickable){
10132 cls : 'roo-removable',
10138 cls : 'roo-combo-removable-btn close'
10145 if (this.before || this.after) {
10148 cls : 'input-group',
10152 inputblock.cn.push({
10154 cls : 'input-group-addon',
10159 inputblock.cn.push(input);
10161 if(this.hasFeedback && !this.allowBlank){
10162 inputblock.cls += ' has-feedback';
10163 inputblock.cn.push(feedback);
10167 inputblock.cn.push({
10169 cls : 'input-group-addon',
10182 cls: 'form-hidden-field'
10196 cls: 'form-hidden-field'
10200 cls: 'roo-select2-choices',
10204 cls: 'roo-select2-search-field',
10217 cls: 'roo-select2-container input-group',
10222 // cls: 'typeahead typeahead-long dropdown-menu',
10223 // style: 'display:none'
10228 if(!this.multiple && this.showToggleBtn){
10234 if (this.caret != false) {
10237 cls: 'fa fa-' + this.caret
10244 cls : 'input-group-addon btn dropdown-toggle',
10249 cls: 'combobox-clear',
10263 combobox.cls += ' roo-select2-container-multi';
10266 if (align ==='left' && this.fieldLabel.length) {
10268 cfg.cls += ' roo-form-group-label-left';
10273 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10274 tooltip : 'This field is required'
10279 cls : 'control-label',
10280 html : this.fieldLabel
10292 var labelCfg = cfg.cn[1];
10293 var contentCfg = cfg.cn[2];
10295 if(this.indicatorpos == 'right'){
10300 cls : 'control-label',
10304 html : this.fieldLabel
10308 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10309 tooltip : 'This field is required'
10322 labelCfg = cfg.cn[0];
10323 contentCfg = cfg.cn[1];
10326 if(this.labelWidth > 12){
10327 labelCfg.style = "width: " + this.labelWidth + 'px';
10330 if(this.labelWidth < 13 && this.labelmd == 0){
10331 this.labelmd = this.labelWidth;
10334 if(this.labellg > 0){
10335 labelCfg.cls += ' col-lg-' + this.labellg;
10336 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10339 if(this.labelmd > 0){
10340 labelCfg.cls += ' col-md-' + this.labelmd;
10341 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10344 if(this.labelsm > 0){
10345 labelCfg.cls += ' col-sm-' + this.labelsm;
10346 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10349 if(this.labelxs > 0){
10350 labelCfg.cls += ' col-xs-' + this.labelxs;
10351 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10354 } else if ( this.fieldLabel.length) {
10355 // Roo.log(" label");
10359 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10360 tooltip : 'This field is required'
10364 //cls : 'input-group-addon',
10365 html : this.fieldLabel
10373 if(this.indicatorpos == 'right'){
10381 html : this.fieldLabel
10385 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10386 tooltip : 'This field is required'
10399 // Roo.log(" no label && no align");
10406 ['xs','sm','md','lg'].map(function(size){
10407 if (settings[size]) {
10408 cfg.cls += ' col-' + size + '-' + settings[size];
10419 onResize : function(w, h){
10420 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10421 // if(typeof w == 'number'){
10422 // var x = w - this.trigger.getWidth();
10423 // this.inputEl().setWidth(this.adjustWidth('input', x));
10424 // this.trigger.setStyle('left', x+'px');
10429 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10432 getResizeEl : function(){
10433 return this.inputEl();
10437 getPositionEl : function(){
10438 return this.inputEl();
10442 alignErrorIcon : function(){
10443 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10447 initEvents : function(){
10451 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10452 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10453 if(!this.multiple && this.showToggleBtn){
10454 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10455 if(this.hideTrigger){
10456 this.trigger.setDisplayed(false);
10458 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10462 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10465 if(this.removable && !this.editable && !this.tickable){
10466 var close = this.closeTriggerEl();
10469 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10470 close.on('click', this.removeBtnClick, this, close);
10474 //this.trigger.addClassOnOver('x-form-trigger-over');
10475 //this.trigger.addClassOnClick('x-form-trigger-click');
10478 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10482 closeTriggerEl : function()
10484 var close = this.el.select('.roo-combo-removable-btn', true).first();
10485 return close ? close : false;
10488 removeBtnClick : function(e, h, el)
10490 e.preventDefault();
10492 if(this.fireEvent("remove", this) !== false){
10494 this.fireEvent("afterremove", this)
10498 createList : function()
10500 this.list = Roo.get(document.body).createChild({
10502 cls: 'typeahead typeahead-long dropdown-menu',
10503 style: 'display:none'
10506 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10511 initTrigger : function(){
10516 onDestroy : function(){
10518 this.trigger.removeAllListeners();
10519 // this.trigger.remove();
10522 // this.wrap.remove();
10524 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10528 onFocus : function(){
10529 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10531 if(!this.mimicing){
10532 this.wrap.addClass('x-trigger-wrap-focus');
10533 this.mimicing = true;
10534 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10535 if(this.monitorTab){
10536 this.el.on("keydown", this.checkTab, this);
10543 checkTab : function(e){
10544 if(e.getKey() == e.TAB){
10545 this.triggerBlur();
10550 onBlur : function(){
10555 mimicBlur : function(e, t){
10557 if(!this.wrap.contains(t) && this.validateBlur()){
10558 this.triggerBlur();
10564 triggerBlur : function(){
10565 this.mimicing = false;
10566 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10567 if(this.monitorTab){
10568 this.el.un("keydown", this.checkTab, this);
10570 //this.wrap.removeClass('x-trigger-wrap-focus');
10571 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10575 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10576 validateBlur : function(e, t){
10581 onDisable : function(){
10582 this.inputEl().dom.disabled = true;
10583 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10585 // this.wrap.addClass('x-item-disabled');
10590 onEnable : function(){
10591 this.inputEl().dom.disabled = false;
10592 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10594 // this.el.removeClass('x-item-disabled');
10599 onShow : function(){
10600 var ae = this.getActionEl();
10603 ae.dom.style.display = '';
10604 ae.dom.style.visibility = 'visible';
10610 onHide : function(){
10611 var ae = this.getActionEl();
10612 ae.dom.style.display = 'none';
10616 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10617 * by an implementing function.
10619 * @param {EventObject} e
10621 onTriggerClick : Roo.emptyFn
10625 * Ext JS Library 1.1.1
10626 * Copyright(c) 2006-2007, Ext JS, LLC.
10628 * Originally Released Under LGPL - original licence link has changed is not relivant.
10631 * <script type="text/javascript">
10636 * @class Roo.data.SortTypes
10638 * Defines the default sorting (casting?) comparison functions used when sorting data.
10640 Roo.data.SortTypes = {
10642 * Default sort that does nothing
10643 * @param {Mixed} s The value being converted
10644 * @return {Mixed} The comparison value
10646 none : function(s){
10651 * The regular expression used to strip tags
10655 stripTagsRE : /<\/?[^>]+>/gi,
10658 * Strips all HTML tags to sort on text only
10659 * @param {Mixed} s The value being converted
10660 * @return {String} The comparison value
10662 asText : function(s){
10663 return String(s).replace(this.stripTagsRE, "");
10667 * Strips all HTML tags to sort on text only - Case insensitive
10668 * @param {Mixed} s The value being converted
10669 * @return {String} The comparison value
10671 asUCText : function(s){
10672 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10676 * Case insensitive string
10677 * @param {Mixed} s The value being converted
10678 * @return {String} The comparison value
10680 asUCString : function(s) {
10681 return String(s).toUpperCase();
10686 * @param {Mixed} s The value being converted
10687 * @return {Number} The comparison value
10689 asDate : function(s) {
10693 if(s instanceof Date){
10694 return s.getTime();
10696 return Date.parse(String(s));
10701 * @param {Mixed} s The value being converted
10702 * @return {Float} The comparison value
10704 asFloat : function(s) {
10705 var val = parseFloat(String(s).replace(/,/g, ""));
10714 * @param {Mixed} s The value being converted
10715 * @return {Number} The comparison value
10717 asInt : function(s) {
10718 var val = parseInt(String(s).replace(/,/g, ""));
10726 * Ext JS Library 1.1.1
10727 * Copyright(c) 2006-2007, Ext JS, LLC.
10729 * Originally Released Under LGPL - original licence link has changed is not relivant.
10732 * <script type="text/javascript">
10736 * @class Roo.data.Record
10737 * Instances of this class encapsulate both record <em>definition</em> information, and record
10738 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10739 * to access Records cached in an {@link Roo.data.Store} object.<br>
10741 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10742 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10745 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10747 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10748 * {@link #create}. The parameters are the same.
10749 * @param {Array} data An associative Array of data values keyed by the field name.
10750 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10751 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10752 * not specified an integer id is generated.
10754 Roo.data.Record = function(data, id){
10755 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10760 * Generate a constructor for a specific record layout.
10761 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10762 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10763 * Each field definition object may contain the following properties: <ul>
10764 * <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,
10765 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10766 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10767 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10768 * is being used, then this is a string containing the javascript expression to reference the data relative to
10769 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10770 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10771 * this may be omitted.</p></li>
10772 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10773 * <ul><li>auto (Default, implies no conversion)</li>
10778 * <li>date</li></ul></p></li>
10779 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10780 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10781 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10782 * by the Reader into an object that will be stored in the Record. It is passed the
10783 * following parameters:<ul>
10784 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10786 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10788 * <br>usage:<br><pre><code>
10789 var TopicRecord = Roo.data.Record.create(
10790 {name: 'title', mapping: 'topic_title'},
10791 {name: 'author', mapping: 'username'},
10792 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10793 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10794 {name: 'lastPoster', mapping: 'user2'},
10795 {name: 'excerpt', mapping: 'post_text'}
10798 var myNewRecord = new TopicRecord({
10799 title: 'Do my job please',
10802 lastPost: new Date(),
10803 lastPoster: 'Animal',
10804 excerpt: 'No way dude!'
10806 myStore.add(myNewRecord);
10811 Roo.data.Record.create = function(o){
10812 var f = function(){
10813 f.superclass.constructor.apply(this, arguments);
10815 Roo.extend(f, Roo.data.Record);
10816 var p = f.prototype;
10817 p.fields = new Roo.util.MixedCollection(false, function(field){
10820 for(var i = 0, len = o.length; i < len; i++){
10821 p.fields.add(new Roo.data.Field(o[i]));
10823 f.getField = function(name){
10824 return p.fields.get(name);
10829 Roo.data.Record.AUTO_ID = 1000;
10830 Roo.data.Record.EDIT = 'edit';
10831 Roo.data.Record.REJECT = 'reject';
10832 Roo.data.Record.COMMIT = 'commit';
10834 Roo.data.Record.prototype = {
10836 * Readonly flag - true if this record has been modified.
10845 join : function(store){
10846 this.store = store;
10850 * Set the named field to the specified value.
10851 * @param {String} name The name of the field to set.
10852 * @param {Object} value The value to set the field to.
10854 set : function(name, value){
10855 if(this.data[name] == value){
10859 if(!this.modified){
10860 this.modified = {};
10862 if(typeof this.modified[name] == 'undefined'){
10863 this.modified[name] = this.data[name];
10865 this.data[name] = value;
10866 if(!this.editing && this.store){
10867 this.store.afterEdit(this);
10872 * Get the value of the named field.
10873 * @param {String} name The name of the field to get the value of.
10874 * @return {Object} The value of the field.
10876 get : function(name){
10877 return this.data[name];
10881 beginEdit : function(){
10882 this.editing = true;
10883 this.modified = {};
10887 cancelEdit : function(){
10888 this.editing = false;
10889 delete this.modified;
10893 endEdit : function(){
10894 this.editing = false;
10895 if(this.dirty && this.store){
10896 this.store.afterEdit(this);
10901 * Usually called by the {@link Roo.data.Store} which owns the Record.
10902 * Rejects all changes made to the Record since either creation, or the last commit operation.
10903 * Modified fields are reverted to their original values.
10905 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10906 * of reject operations.
10908 reject : function(){
10909 var m = this.modified;
10911 if(typeof m[n] != "function"){
10912 this.data[n] = m[n];
10915 this.dirty = false;
10916 delete this.modified;
10917 this.editing = false;
10919 this.store.afterReject(this);
10924 * Usually called by the {@link Roo.data.Store} which owns the Record.
10925 * Commits all changes made to the Record since either creation, or the last commit operation.
10927 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10928 * of commit operations.
10930 commit : function(){
10931 this.dirty = false;
10932 delete this.modified;
10933 this.editing = false;
10935 this.store.afterCommit(this);
10940 hasError : function(){
10941 return this.error != null;
10945 clearError : function(){
10950 * Creates a copy of this record.
10951 * @param {String} id (optional) A new record id if you don't want to use this record's id
10954 copy : function(newId) {
10955 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10959 * Ext JS Library 1.1.1
10960 * Copyright(c) 2006-2007, Ext JS, LLC.
10962 * Originally Released Under LGPL - original licence link has changed is not relivant.
10965 * <script type="text/javascript">
10971 * @class Roo.data.Store
10972 * @extends Roo.util.Observable
10973 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10974 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10976 * 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
10977 * has no knowledge of the format of the data returned by the Proxy.<br>
10979 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10980 * instances from the data object. These records are cached and made available through accessor functions.
10982 * Creates a new Store.
10983 * @param {Object} config A config object containing the objects needed for the Store to access data,
10984 * and read the data into Records.
10986 Roo.data.Store = function(config){
10987 this.data = new Roo.util.MixedCollection(false);
10988 this.data.getKey = function(o){
10991 this.baseParams = {};
10993 this.paramNames = {
10998 "multisort" : "_multisort"
11001 if(config && config.data){
11002 this.inlineData = config.data;
11003 delete config.data;
11006 Roo.apply(this, config);
11008 if(this.reader){ // reader passed
11009 this.reader = Roo.factory(this.reader, Roo.data);
11010 this.reader.xmodule = this.xmodule || false;
11011 if(!this.recordType){
11012 this.recordType = this.reader.recordType;
11014 if(this.reader.onMetaChange){
11015 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11019 if(this.recordType){
11020 this.fields = this.recordType.prototype.fields;
11022 this.modified = [];
11026 * @event datachanged
11027 * Fires when the data cache has changed, and a widget which is using this Store
11028 * as a Record cache should refresh its view.
11029 * @param {Store} this
11031 datachanged : true,
11033 * @event metachange
11034 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11035 * @param {Store} this
11036 * @param {Object} meta The JSON metadata
11041 * Fires when Records have been added to the Store
11042 * @param {Store} this
11043 * @param {Roo.data.Record[]} records The array of Records added
11044 * @param {Number} index The index at which the record(s) were added
11049 * Fires when a Record has been removed from the Store
11050 * @param {Store} this
11051 * @param {Roo.data.Record} record The Record that was removed
11052 * @param {Number} index The index at which the record was removed
11057 * Fires when a Record has been updated
11058 * @param {Store} this
11059 * @param {Roo.data.Record} record The Record that was updated
11060 * @param {String} operation The update operation being performed. Value may be one of:
11062 Roo.data.Record.EDIT
11063 Roo.data.Record.REJECT
11064 Roo.data.Record.COMMIT
11070 * Fires when the data cache has been cleared.
11071 * @param {Store} this
11075 * @event beforeload
11076 * Fires before a request is made for a new data object. If the beforeload handler returns false
11077 * the load action will be canceled.
11078 * @param {Store} this
11079 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11083 * @event beforeloadadd
11084 * Fires after a new set of Records has been loaded.
11085 * @param {Store} this
11086 * @param {Roo.data.Record[]} records The Records that were loaded
11087 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11089 beforeloadadd : true,
11092 * Fires after a new set of Records has been loaded, before they are added to the store.
11093 * @param {Store} this
11094 * @param {Roo.data.Record[]} records The Records that were loaded
11095 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11096 * @params {Object} return from reader
11100 * @event loadexception
11101 * Fires if an exception occurs in the Proxy during loading.
11102 * Called with the signature of the Proxy's "loadexception" event.
11103 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11106 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11107 * @param {Object} load options
11108 * @param {Object} jsonData from your request (normally this contains the Exception)
11110 loadexception : true
11114 this.proxy = Roo.factory(this.proxy, Roo.data);
11115 this.proxy.xmodule = this.xmodule || false;
11116 this.relayEvents(this.proxy, ["loadexception"]);
11118 this.sortToggle = {};
11119 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11121 Roo.data.Store.superclass.constructor.call(this);
11123 if(this.inlineData){
11124 this.loadData(this.inlineData);
11125 delete this.inlineData;
11129 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11131 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11132 * without a remote query - used by combo/forms at present.
11136 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11139 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11142 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11143 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11146 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11147 * on any HTTP request
11150 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11153 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11157 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11158 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11160 remoteSort : false,
11163 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11164 * loaded or when a record is removed. (defaults to false).
11166 pruneModifiedRecords : false,
11169 lastOptions : null,
11172 * Add Records to the Store and fires the add event.
11173 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11175 add : function(records){
11176 records = [].concat(records);
11177 for(var i = 0, len = records.length; i < len; i++){
11178 records[i].join(this);
11180 var index = this.data.length;
11181 this.data.addAll(records);
11182 this.fireEvent("add", this, records, index);
11186 * Remove a Record from the Store and fires the remove event.
11187 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11189 remove : function(record){
11190 var index = this.data.indexOf(record);
11191 this.data.removeAt(index);
11193 if(this.pruneModifiedRecords){
11194 this.modified.remove(record);
11196 this.fireEvent("remove", this, record, index);
11200 * Remove all Records from the Store and fires the clear event.
11202 removeAll : function(){
11204 if(this.pruneModifiedRecords){
11205 this.modified = [];
11207 this.fireEvent("clear", this);
11211 * Inserts Records to the Store at the given index and fires the add event.
11212 * @param {Number} index The start index at which to insert the passed Records.
11213 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11215 insert : function(index, records){
11216 records = [].concat(records);
11217 for(var i = 0, len = records.length; i < len; i++){
11218 this.data.insert(index, records[i]);
11219 records[i].join(this);
11221 this.fireEvent("add", this, records, index);
11225 * Get the index within the cache of the passed Record.
11226 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11227 * @return {Number} The index of the passed Record. Returns -1 if not found.
11229 indexOf : function(record){
11230 return this.data.indexOf(record);
11234 * Get the index within the cache of the Record with the passed id.
11235 * @param {String} id The id of the Record to find.
11236 * @return {Number} The index of the Record. Returns -1 if not found.
11238 indexOfId : function(id){
11239 return this.data.indexOfKey(id);
11243 * Get the Record with the specified id.
11244 * @param {String} id The id of the Record to find.
11245 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11247 getById : function(id){
11248 return this.data.key(id);
11252 * Get the Record at the specified index.
11253 * @param {Number} index The index of the Record to find.
11254 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11256 getAt : function(index){
11257 return this.data.itemAt(index);
11261 * Returns a range of Records between specified indices.
11262 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11263 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11264 * @return {Roo.data.Record[]} An array of Records
11266 getRange : function(start, end){
11267 return this.data.getRange(start, end);
11271 storeOptions : function(o){
11272 o = Roo.apply({}, o);
11275 this.lastOptions = o;
11279 * Loads the Record cache from the configured Proxy using the configured Reader.
11281 * If using remote paging, then the first load call must specify the <em>start</em>
11282 * and <em>limit</em> properties in the options.params property to establish the initial
11283 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11285 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11286 * and this call will return before the new data has been loaded. Perform any post-processing
11287 * in a callback function, or in a "load" event handler.</strong>
11289 * @param {Object} options An object containing properties which control loading options:<ul>
11290 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11291 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11292 * passed the following arguments:<ul>
11293 * <li>r : Roo.data.Record[]</li>
11294 * <li>options: Options object from the load call</li>
11295 * <li>success: Boolean success indicator</li></ul></li>
11296 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11297 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11300 load : function(options){
11301 options = options || {};
11302 if(this.fireEvent("beforeload", this, options) !== false){
11303 this.storeOptions(options);
11304 var p = Roo.apply(options.params || {}, this.baseParams);
11305 // if meta was not loaded from remote source.. try requesting it.
11306 if (!this.reader.metaFromRemote) {
11307 p._requestMeta = 1;
11309 if(this.sortInfo && this.remoteSort){
11310 var pn = this.paramNames;
11311 p[pn["sort"]] = this.sortInfo.field;
11312 p[pn["dir"]] = this.sortInfo.direction;
11314 if (this.multiSort) {
11315 var pn = this.paramNames;
11316 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11319 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11324 * Reloads the Record cache from the configured Proxy using the configured Reader and
11325 * the options from the last load operation performed.
11326 * @param {Object} options (optional) An object containing properties which may override the options
11327 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11328 * the most recently used options are reused).
11330 reload : function(options){
11331 this.load(Roo.applyIf(options||{}, this.lastOptions));
11335 // Called as a callback by the Reader during a load operation.
11336 loadRecords : function(o, options, success){
11337 if(!o || success === false){
11338 if(success !== false){
11339 this.fireEvent("load", this, [], options, o);
11341 if(options.callback){
11342 options.callback.call(options.scope || this, [], options, false);
11346 // if data returned failure - throw an exception.
11347 if (o.success === false) {
11348 // show a message if no listener is registered.
11349 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11350 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11352 // loadmask wil be hooked into this..
11353 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11356 var r = o.records, t = o.totalRecords || r.length;
11358 this.fireEvent("beforeloadadd", this, r, options, o);
11360 if(!options || options.add !== true){
11361 if(this.pruneModifiedRecords){
11362 this.modified = [];
11364 for(var i = 0, len = r.length; i < len; i++){
11368 this.data = this.snapshot;
11369 delete this.snapshot;
11372 this.data.addAll(r);
11373 this.totalLength = t;
11375 this.fireEvent("datachanged", this);
11377 this.totalLength = Math.max(t, this.data.length+r.length);
11381 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11383 var e = new Roo.data.Record({});
11385 e.set(this.parent.displayField, this.parent.emptyTitle);
11386 e.set(this.parent.valueField, '');
11391 this.fireEvent("load", this, r, options, o);
11392 if(options.callback){
11393 options.callback.call(options.scope || this, r, options, true);
11399 * Loads data from a passed data block. A Reader which understands the format of the data
11400 * must have been configured in the constructor.
11401 * @param {Object} data The data block from which to read the Records. The format of the data expected
11402 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11403 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11405 loadData : function(o, append){
11406 var r = this.reader.readRecords(o);
11407 this.loadRecords(r, {add: append}, true);
11411 * Gets the number of cached records.
11413 * <em>If using paging, this may not be the total size of the dataset. If the data object
11414 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11415 * the data set size</em>
11417 getCount : function(){
11418 return this.data.length || 0;
11422 * Gets the total number of records in the dataset as returned by the server.
11424 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11425 * the dataset size</em>
11427 getTotalCount : function(){
11428 return this.totalLength || 0;
11432 * Returns the sort state of the Store as an object with two properties:
11434 field {String} The name of the field by which the Records are sorted
11435 direction {String} The sort order, "ASC" or "DESC"
11438 getSortState : function(){
11439 return this.sortInfo;
11443 applySort : function(){
11444 if(this.sortInfo && !this.remoteSort){
11445 var s = this.sortInfo, f = s.field;
11446 var st = this.fields.get(f).sortType;
11447 var fn = function(r1, r2){
11448 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11449 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11451 this.data.sort(s.direction, fn);
11452 if(this.snapshot && this.snapshot != this.data){
11453 this.snapshot.sort(s.direction, fn);
11459 * Sets the default sort column and order to be used by the next load operation.
11460 * @param {String} fieldName The name of the field to sort by.
11461 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11463 setDefaultSort : function(field, dir){
11464 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11468 * Sort the Records.
11469 * If remote sorting is used, the sort is performed on the server, and the cache is
11470 * reloaded. If local sorting is used, the cache is sorted internally.
11471 * @param {String} fieldName The name of the field to sort by.
11472 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11474 sort : function(fieldName, dir){
11475 var f = this.fields.get(fieldName);
11477 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11479 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11480 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11485 this.sortToggle[f.name] = dir;
11486 this.sortInfo = {field: f.name, direction: dir};
11487 if(!this.remoteSort){
11489 this.fireEvent("datachanged", this);
11491 this.load(this.lastOptions);
11496 * Calls the specified function for each of the Records in the cache.
11497 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11498 * Returning <em>false</em> aborts and exits the iteration.
11499 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11501 each : function(fn, scope){
11502 this.data.each(fn, scope);
11506 * Gets all records modified since the last commit. Modified records are persisted across load operations
11507 * (e.g., during paging).
11508 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11510 getModifiedRecords : function(){
11511 return this.modified;
11515 createFilterFn : function(property, value, anyMatch){
11516 if(!value.exec){ // not a regex
11517 value = String(value);
11518 if(value.length == 0){
11521 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11523 return function(r){
11524 return value.test(r.data[property]);
11529 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11530 * @param {String} property A field on your records
11531 * @param {Number} start The record index to start at (defaults to 0)
11532 * @param {Number} end The last record index to include (defaults to length - 1)
11533 * @return {Number} The sum
11535 sum : function(property, start, end){
11536 var rs = this.data.items, v = 0;
11537 start = start || 0;
11538 end = (end || end === 0) ? end : rs.length-1;
11540 for(var i = start; i <= end; i++){
11541 v += (rs[i].data[property] || 0);
11547 * Filter the records by a specified property.
11548 * @param {String} field A field on your records
11549 * @param {String/RegExp} value Either a string that the field
11550 * should start with or a RegExp to test against the field
11551 * @param {Boolean} anyMatch True to match any part not just the beginning
11553 filter : function(property, value, anyMatch){
11554 var fn = this.createFilterFn(property, value, anyMatch);
11555 return fn ? this.filterBy(fn) : this.clearFilter();
11559 * Filter by a function. The specified function will be called with each
11560 * record in this data source. If the function returns true the record is included,
11561 * otherwise it is filtered.
11562 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11563 * @param {Object} scope (optional) The scope of the function (defaults to this)
11565 filterBy : function(fn, scope){
11566 this.snapshot = this.snapshot || this.data;
11567 this.data = this.queryBy(fn, scope||this);
11568 this.fireEvent("datachanged", this);
11572 * Query the records by a specified property.
11573 * @param {String} field A field on your records
11574 * @param {String/RegExp} value Either a string that the field
11575 * should start with or a RegExp to test against the field
11576 * @param {Boolean} anyMatch True to match any part not just the beginning
11577 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11579 query : function(property, value, anyMatch){
11580 var fn = this.createFilterFn(property, value, anyMatch);
11581 return fn ? this.queryBy(fn) : this.data.clone();
11585 * Query by a function. The specified function will be called with each
11586 * record in this data source. If the function returns true the record is included
11588 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11589 * @param {Object} scope (optional) The scope of the function (defaults to this)
11590 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11592 queryBy : function(fn, scope){
11593 var data = this.snapshot || this.data;
11594 return data.filterBy(fn, scope||this);
11598 * Collects unique values for a particular dataIndex from this store.
11599 * @param {String} dataIndex The property to collect
11600 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11601 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11602 * @return {Array} An array of the unique values
11604 collect : function(dataIndex, allowNull, bypassFilter){
11605 var d = (bypassFilter === true && this.snapshot) ?
11606 this.snapshot.items : this.data.items;
11607 var v, sv, r = [], l = {};
11608 for(var i = 0, len = d.length; i < len; i++){
11609 v = d[i].data[dataIndex];
11611 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11620 * Revert to a view of the Record cache with no filtering applied.
11621 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11623 clearFilter : function(suppressEvent){
11624 if(this.snapshot && this.snapshot != this.data){
11625 this.data = this.snapshot;
11626 delete this.snapshot;
11627 if(suppressEvent !== true){
11628 this.fireEvent("datachanged", this);
11634 afterEdit : function(record){
11635 if(this.modified.indexOf(record) == -1){
11636 this.modified.push(record);
11638 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11642 afterReject : function(record){
11643 this.modified.remove(record);
11644 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11648 afterCommit : function(record){
11649 this.modified.remove(record);
11650 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11654 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11655 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11657 commitChanges : function(){
11658 var m = this.modified.slice(0);
11659 this.modified = [];
11660 for(var i = 0, len = m.length; i < len; i++){
11666 * Cancel outstanding changes on all changed records.
11668 rejectChanges : function(){
11669 var m = this.modified.slice(0);
11670 this.modified = [];
11671 for(var i = 0, len = m.length; i < len; i++){
11676 onMetaChange : function(meta, rtype, o){
11677 this.recordType = rtype;
11678 this.fields = rtype.prototype.fields;
11679 delete this.snapshot;
11680 this.sortInfo = meta.sortInfo || this.sortInfo;
11681 this.modified = [];
11682 this.fireEvent('metachange', this, this.reader.meta);
11685 moveIndex : function(data, type)
11687 var index = this.indexOf(data);
11689 var newIndex = index + type;
11693 this.insert(newIndex, data);
11698 * Ext JS Library 1.1.1
11699 * Copyright(c) 2006-2007, Ext JS, LLC.
11701 * Originally Released Under LGPL - original licence link has changed is not relivant.
11704 * <script type="text/javascript">
11708 * @class Roo.data.SimpleStore
11709 * @extends Roo.data.Store
11710 * Small helper class to make creating Stores from Array data easier.
11711 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11712 * @cfg {Array} fields An array of field definition objects, or field name strings.
11713 * @cfg {Array} data The multi-dimensional array of data
11715 * @param {Object} config
11717 Roo.data.SimpleStore = function(config){
11718 Roo.data.SimpleStore.superclass.constructor.call(this, {
11720 reader: new Roo.data.ArrayReader({
11723 Roo.data.Record.create(config.fields)
11725 proxy : new Roo.data.MemoryProxy(config.data)
11729 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11731 * Ext JS Library 1.1.1
11732 * Copyright(c) 2006-2007, Ext JS, LLC.
11734 * Originally Released Under LGPL - original licence link has changed is not relivant.
11737 * <script type="text/javascript">
11742 * @extends Roo.data.Store
11743 * @class Roo.data.JsonStore
11744 * Small helper class to make creating Stores for JSON data easier. <br/>
11746 var store = new Roo.data.JsonStore({
11747 url: 'get-images.php',
11749 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11752 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11753 * JsonReader and HttpProxy (unless inline data is provided).</b>
11754 * @cfg {Array} fields An array of field definition objects, or field name strings.
11756 * @param {Object} config
11758 Roo.data.JsonStore = function(c){
11759 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11760 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11761 reader: new Roo.data.JsonReader(c, c.fields)
11764 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11766 * Ext JS Library 1.1.1
11767 * Copyright(c) 2006-2007, Ext JS, LLC.
11769 * Originally Released Under LGPL - original licence link has changed is not relivant.
11772 * <script type="text/javascript">
11776 Roo.data.Field = function(config){
11777 if(typeof config == "string"){
11778 config = {name: config};
11780 Roo.apply(this, config);
11783 this.type = "auto";
11786 var st = Roo.data.SortTypes;
11787 // named sortTypes are supported, here we look them up
11788 if(typeof this.sortType == "string"){
11789 this.sortType = st[this.sortType];
11792 // set default sortType for strings and dates
11793 if(!this.sortType){
11796 this.sortType = st.asUCString;
11799 this.sortType = st.asDate;
11802 this.sortType = st.none;
11807 var stripRe = /[\$,%]/g;
11809 // prebuilt conversion function for this field, instead of
11810 // switching every time we're reading a value
11812 var cv, dateFormat = this.dateFormat;
11817 cv = function(v){ return v; };
11820 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11824 return v !== undefined && v !== null && v !== '' ?
11825 parseInt(String(v).replace(stripRe, ""), 10) : '';
11830 return v !== undefined && v !== null && v !== '' ?
11831 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11836 cv = function(v){ return v === true || v === "true" || v == 1; };
11843 if(v instanceof Date){
11847 if(dateFormat == "timestamp"){
11848 return new Date(v*1000);
11850 return Date.parseDate(v, dateFormat);
11852 var parsed = Date.parse(v);
11853 return parsed ? new Date(parsed) : null;
11862 Roo.data.Field.prototype = {
11870 * Ext JS Library 1.1.1
11871 * Copyright(c) 2006-2007, Ext JS, LLC.
11873 * Originally Released Under LGPL - original licence link has changed is not relivant.
11876 * <script type="text/javascript">
11879 // Base class for reading structured data from a data source. This class is intended to be
11880 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11883 * @class Roo.data.DataReader
11884 * Base class for reading structured data from a data source. This class is intended to be
11885 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11888 Roo.data.DataReader = function(meta, recordType){
11892 this.recordType = recordType instanceof Array ?
11893 Roo.data.Record.create(recordType) : recordType;
11896 Roo.data.DataReader.prototype = {
11898 * Create an empty record
11899 * @param {Object} data (optional) - overlay some values
11900 * @return {Roo.data.Record} record created.
11902 newRow : function(d) {
11904 this.recordType.prototype.fields.each(function(c) {
11906 case 'int' : da[c.name] = 0; break;
11907 case 'date' : da[c.name] = new Date(); break;
11908 case 'float' : da[c.name] = 0.0; break;
11909 case 'boolean' : da[c.name] = false; break;
11910 default : da[c.name] = ""; break;
11914 return new this.recordType(Roo.apply(da, d));
11919 * Ext JS Library 1.1.1
11920 * Copyright(c) 2006-2007, Ext JS, LLC.
11922 * Originally Released Under LGPL - original licence link has changed is not relivant.
11925 * <script type="text/javascript">
11929 * @class Roo.data.DataProxy
11930 * @extends Roo.data.Observable
11931 * This class is an abstract base class for implementations which provide retrieval of
11932 * unformatted data objects.<br>
11934 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11935 * (of the appropriate type which knows how to parse the data object) to provide a block of
11936 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11938 * Custom implementations must implement the load method as described in
11939 * {@link Roo.data.HttpProxy#load}.
11941 Roo.data.DataProxy = function(){
11944 * @event beforeload
11945 * Fires before a network request is made to retrieve a data object.
11946 * @param {Object} This DataProxy object.
11947 * @param {Object} params The params parameter to the load function.
11952 * Fires before the load method's callback is called.
11953 * @param {Object} This DataProxy object.
11954 * @param {Object} o The data object.
11955 * @param {Object} arg The callback argument object passed to the load function.
11959 * @event loadexception
11960 * Fires if an Exception occurs during data retrieval.
11961 * @param {Object} This DataProxy object.
11962 * @param {Object} o The data object.
11963 * @param {Object} arg The callback argument object passed to the load function.
11964 * @param {Object} e The Exception.
11966 loadexception : true
11968 Roo.data.DataProxy.superclass.constructor.call(this);
11971 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11974 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11978 * Ext JS Library 1.1.1
11979 * Copyright(c) 2006-2007, Ext JS, LLC.
11981 * Originally Released Under LGPL - original licence link has changed is not relivant.
11984 * <script type="text/javascript">
11987 * @class Roo.data.MemoryProxy
11988 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11989 * to the Reader when its load method is called.
11991 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11993 Roo.data.MemoryProxy = function(data){
11997 Roo.data.MemoryProxy.superclass.constructor.call(this);
12001 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12004 * Load data from the requested source (in this case an in-memory
12005 * data object passed to the constructor), read the data object into
12006 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12007 * process that block using the passed callback.
12008 * @param {Object} params This parameter is not used by the MemoryProxy class.
12009 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12010 * object into a block of Roo.data.Records.
12011 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12012 * The function must be passed <ul>
12013 * <li>The Record block object</li>
12014 * <li>The "arg" argument from the load function</li>
12015 * <li>A boolean success indicator</li>
12017 * @param {Object} scope The scope in which to call the callback
12018 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12020 load : function(params, reader, callback, scope, arg){
12021 params = params || {};
12024 result = reader.readRecords(this.data);
12026 this.fireEvent("loadexception", this, arg, null, e);
12027 callback.call(scope, null, arg, false);
12030 callback.call(scope, result, arg, true);
12034 update : function(params, records){
12039 * Ext JS Library 1.1.1
12040 * Copyright(c) 2006-2007, Ext JS, LLC.
12042 * Originally Released Under LGPL - original licence link has changed is not relivant.
12045 * <script type="text/javascript">
12048 * @class Roo.data.HttpProxy
12049 * @extends Roo.data.DataProxy
12050 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12051 * configured to reference a certain URL.<br><br>
12053 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12054 * from which the running page was served.<br><br>
12056 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12058 * Be aware that to enable the browser to parse an XML document, the server must set
12059 * the Content-Type header in the HTTP response to "text/xml".
12061 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12062 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12063 * will be used to make the request.
12065 Roo.data.HttpProxy = function(conn){
12066 Roo.data.HttpProxy.superclass.constructor.call(this);
12067 // is conn a conn config or a real conn?
12069 this.useAjax = !conn || !conn.events;
12073 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12074 // thse are take from connection...
12077 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12080 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12081 * extra parameters to each request made by this object. (defaults to undefined)
12084 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12085 * to each request made by this object. (defaults to undefined)
12088 * @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)
12091 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12094 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12100 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12104 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12105 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12106 * a finer-grained basis than the DataProxy events.
12108 getConnection : function(){
12109 return this.useAjax ? Roo.Ajax : this.conn;
12113 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12114 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12115 * process that block using the passed callback.
12116 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12117 * for the request to the remote server.
12118 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12119 * object into a block of Roo.data.Records.
12120 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12121 * The function must be passed <ul>
12122 * <li>The Record block object</li>
12123 * <li>The "arg" argument from the load function</li>
12124 * <li>A boolean success indicator</li>
12126 * @param {Object} scope The scope in which to call the callback
12127 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12129 load : function(params, reader, callback, scope, arg){
12130 if(this.fireEvent("beforeload", this, params) !== false){
12132 params : params || {},
12134 callback : callback,
12139 callback : this.loadResponse,
12143 Roo.applyIf(o, this.conn);
12144 if(this.activeRequest){
12145 Roo.Ajax.abort(this.activeRequest);
12147 this.activeRequest = Roo.Ajax.request(o);
12149 this.conn.request(o);
12152 callback.call(scope||this, null, arg, false);
12157 loadResponse : function(o, success, response){
12158 delete this.activeRequest;
12160 this.fireEvent("loadexception", this, o, response);
12161 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12166 result = o.reader.read(response);
12168 this.fireEvent("loadexception", this, o, response, e);
12169 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12173 this.fireEvent("load", this, o, o.request.arg);
12174 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12178 update : function(dataSet){
12183 updateResponse : function(dataSet){
12188 * Ext JS Library 1.1.1
12189 * Copyright(c) 2006-2007, Ext JS, LLC.
12191 * Originally Released Under LGPL - original licence link has changed is not relivant.
12194 * <script type="text/javascript">
12198 * @class Roo.data.ScriptTagProxy
12199 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12200 * other than the originating domain of the running page.<br><br>
12202 * <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
12203 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12205 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12206 * source code that is used as the source inside a <script> tag.<br><br>
12208 * In order for the browser to process the returned data, the server must wrap the data object
12209 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12210 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12211 * depending on whether the callback name was passed:
12214 boolean scriptTag = false;
12215 String cb = request.getParameter("callback");
12218 response.setContentType("text/javascript");
12220 response.setContentType("application/x-json");
12222 Writer out = response.getWriter();
12224 out.write(cb + "(");
12226 out.print(dataBlock.toJsonString());
12233 * @param {Object} config A configuration object.
12235 Roo.data.ScriptTagProxy = function(config){
12236 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12237 Roo.apply(this, config);
12238 this.head = document.getElementsByTagName("head")[0];
12241 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12243 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12245 * @cfg {String} url The URL from which to request the data object.
12248 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12252 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12253 * the server the name of the callback function set up by the load call to process the returned data object.
12254 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12255 * javascript output which calls this named function passing the data object as its only parameter.
12257 callbackParam : "callback",
12259 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12260 * name to the request.
12265 * Load data from the configured URL, read the data object into
12266 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12267 * process that block using the passed callback.
12268 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12269 * for the request to the remote server.
12270 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12271 * object into a block of Roo.data.Records.
12272 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12273 * The function must be passed <ul>
12274 * <li>The Record block object</li>
12275 * <li>The "arg" argument from the load function</li>
12276 * <li>A boolean success indicator</li>
12278 * @param {Object} scope The scope in which to call the callback
12279 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12281 load : function(params, reader, callback, scope, arg){
12282 if(this.fireEvent("beforeload", this, params) !== false){
12284 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12286 var url = this.url;
12287 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12289 url += "&_dc=" + (new Date().getTime());
12291 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12294 cb : "stcCallback"+transId,
12295 scriptId : "stcScript"+transId,
12299 callback : callback,
12305 window[trans.cb] = function(o){
12306 conn.handleResponse(o, trans);
12309 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12311 if(this.autoAbort !== false){
12315 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12317 var script = document.createElement("script");
12318 script.setAttribute("src", url);
12319 script.setAttribute("type", "text/javascript");
12320 script.setAttribute("id", trans.scriptId);
12321 this.head.appendChild(script);
12323 this.trans = trans;
12325 callback.call(scope||this, null, arg, false);
12330 isLoading : function(){
12331 return this.trans ? true : false;
12335 * Abort the current server request.
12337 abort : function(){
12338 if(this.isLoading()){
12339 this.destroyTrans(this.trans);
12344 destroyTrans : function(trans, isLoaded){
12345 this.head.removeChild(document.getElementById(trans.scriptId));
12346 clearTimeout(trans.timeoutId);
12348 window[trans.cb] = undefined;
12350 delete window[trans.cb];
12353 // if hasn't been loaded, wait for load to remove it to prevent script error
12354 window[trans.cb] = function(){
12355 window[trans.cb] = undefined;
12357 delete window[trans.cb];
12364 handleResponse : function(o, trans){
12365 this.trans = false;
12366 this.destroyTrans(trans, true);
12369 result = trans.reader.readRecords(o);
12371 this.fireEvent("loadexception", this, o, trans.arg, e);
12372 trans.callback.call(trans.scope||window, null, trans.arg, false);
12375 this.fireEvent("load", this, o, trans.arg);
12376 trans.callback.call(trans.scope||window, result, trans.arg, true);
12380 handleFailure : function(trans){
12381 this.trans = false;
12382 this.destroyTrans(trans, false);
12383 this.fireEvent("loadexception", this, null, trans.arg);
12384 trans.callback.call(trans.scope||window, null, trans.arg, false);
12388 * Ext JS Library 1.1.1
12389 * Copyright(c) 2006-2007, Ext JS, LLC.
12391 * Originally Released Under LGPL - original licence link has changed is not relivant.
12394 * <script type="text/javascript">
12398 * @class Roo.data.JsonReader
12399 * @extends Roo.data.DataReader
12400 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12401 * based on mappings in a provided Roo.data.Record constructor.
12403 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12404 * in the reply previously.
12409 var RecordDef = Roo.data.Record.create([
12410 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12411 {name: 'occupation'} // This field will use "occupation" as the mapping.
12413 var myReader = new Roo.data.JsonReader({
12414 totalProperty: "results", // The property which contains the total dataset size (optional)
12415 root: "rows", // The property which contains an Array of row objects
12416 id: "id" // The property within each row object that provides an ID for the record (optional)
12420 * This would consume a JSON file like this:
12422 { 'results': 2, 'rows': [
12423 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12424 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12427 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12428 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12429 * paged from the remote server.
12430 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12431 * @cfg {String} root name of the property which contains the Array of row objects.
12432 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12433 * @cfg {Array} fields Array of field definition objects
12435 * Create a new JsonReader
12436 * @param {Object} meta Metadata configuration options
12437 * @param {Object} recordType Either an Array of field definition objects,
12438 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12440 Roo.data.JsonReader = function(meta, recordType){
12443 // set some defaults:
12444 Roo.applyIf(meta, {
12445 totalProperty: 'total',
12446 successProperty : 'success',
12451 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12453 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12456 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12457 * Used by Store query builder to append _requestMeta to params.
12460 metaFromRemote : false,
12462 * This method is only used by a DataProxy which has retrieved data from a remote server.
12463 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12464 * @return {Object} data A data block which is used by an Roo.data.Store object as
12465 * a cache of Roo.data.Records.
12467 read : function(response){
12468 var json = response.responseText;
12470 var o = /* eval:var:o */ eval("("+json+")");
12472 throw {message: "JsonReader.read: Json object not found"};
12478 this.metaFromRemote = true;
12479 this.meta = o.metaData;
12480 this.recordType = Roo.data.Record.create(o.metaData.fields);
12481 this.onMetaChange(this.meta, this.recordType, o);
12483 return this.readRecords(o);
12486 // private function a store will implement
12487 onMetaChange : function(meta, recordType, o){
12494 simpleAccess: function(obj, subsc) {
12501 getJsonAccessor: function(){
12503 return function(expr) {
12505 return(re.test(expr))
12506 ? new Function("obj", "return obj." + expr)
12511 return Roo.emptyFn;
12516 * Create a data block containing Roo.data.Records from an XML document.
12517 * @param {Object} o An object which contains an Array of row objects in the property specified
12518 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12519 * which contains the total size of the dataset.
12520 * @return {Object} data A data block which is used by an Roo.data.Store object as
12521 * a cache of Roo.data.Records.
12523 readRecords : function(o){
12525 * After any data loads, the raw JSON data is available for further custom processing.
12529 var s = this.meta, Record = this.recordType,
12530 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12532 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12534 if(s.totalProperty) {
12535 this.getTotal = this.getJsonAccessor(s.totalProperty);
12537 if(s.successProperty) {
12538 this.getSuccess = this.getJsonAccessor(s.successProperty);
12540 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12542 var g = this.getJsonAccessor(s.id);
12543 this.getId = function(rec) {
12545 return (r === undefined || r === "") ? null : r;
12548 this.getId = function(){return null;};
12551 for(var jj = 0; jj < fl; jj++){
12553 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12554 this.ef[jj] = this.getJsonAccessor(map);
12558 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12559 if(s.totalProperty){
12560 var vt = parseInt(this.getTotal(o), 10);
12565 if(s.successProperty){
12566 var vs = this.getSuccess(o);
12567 if(vs === false || vs === 'false'){
12572 for(var i = 0; i < c; i++){
12575 var id = this.getId(n);
12576 for(var j = 0; j < fl; j++){
12578 var v = this.ef[j](n);
12580 Roo.log('missing convert for ' + f.name);
12584 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12586 var record = new Record(values, id);
12588 records[i] = record;
12594 totalRecords : totalRecords
12599 * Ext JS Library 1.1.1
12600 * Copyright(c) 2006-2007, Ext JS, LLC.
12602 * Originally Released Under LGPL - original licence link has changed is not relivant.
12605 * <script type="text/javascript">
12609 * @class Roo.data.ArrayReader
12610 * @extends Roo.data.DataReader
12611 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12612 * Each element of that Array represents a row of data fields. The
12613 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12614 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12618 var RecordDef = Roo.data.Record.create([
12619 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12620 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12622 var myReader = new Roo.data.ArrayReader({
12623 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12627 * This would consume an Array like this:
12629 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12631 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12633 * Create a new JsonReader
12634 * @param {Object} meta Metadata configuration options.
12635 * @param {Object} recordType Either an Array of field definition objects
12636 * as specified to {@link Roo.data.Record#create},
12637 * or an {@link Roo.data.Record} object
12638 * created using {@link Roo.data.Record#create}.
12640 Roo.data.ArrayReader = function(meta, recordType){
12641 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12644 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12646 * Create a data block containing Roo.data.Records from an XML document.
12647 * @param {Object} o An Array of row objects which represents the dataset.
12648 * @return {Object} data A data block which is used by an Roo.data.Store object as
12649 * a cache of Roo.data.Records.
12651 readRecords : function(o){
12652 var sid = this.meta ? this.meta.id : null;
12653 var recordType = this.recordType, fields = recordType.prototype.fields;
12656 for(var i = 0; i < root.length; i++){
12659 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12660 for(var j = 0, jlen = fields.length; j < jlen; j++){
12661 var f = fields.items[j];
12662 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12663 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12665 values[f.name] = v;
12667 var record = new recordType(values, id);
12669 records[records.length] = record;
12673 totalRecords : records.length
12682 * @class Roo.bootstrap.ComboBox
12683 * @extends Roo.bootstrap.TriggerField
12684 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12685 * @cfg {Boolean} append (true|false) default false
12686 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12687 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12688 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12689 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12690 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12691 * @cfg {Boolean} animate default true
12692 * @cfg {Boolean} emptyResultText only for touch device
12693 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12694 * @cfg {String} emptyTitle default ''
12696 * Create a new ComboBox.
12697 * @param {Object} config Configuration options
12699 Roo.bootstrap.ComboBox = function(config){
12700 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12704 * Fires when the dropdown list is expanded
12705 * @param {Roo.bootstrap.ComboBox} combo This combo box
12710 * Fires when the dropdown list is collapsed
12711 * @param {Roo.bootstrap.ComboBox} combo This combo box
12715 * @event beforeselect
12716 * Fires before a list item is selected. Return false to cancel the selection.
12717 * @param {Roo.bootstrap.ComboBox} combo This combo box
12718 * @param {Roo.data.Record} record The data record returned from the underlying store
12719 * @param {Number} index The index of the selected item in the dropdown list
12721 'beforeselect' : true,
12724 * Fires when a list item is selected
12725 * @param {Roo.bootstrap.ComboBox} combo This combo box
12726 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12727 * @param {Number} index The index of the selected item in the dropdown list
12731 * @event beforequery
12732 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12733 * The event object passed has these properties:
12734 * @param {Roo.bootstrap.ComboBox} combo This combo box
12735 * @param {String} query The query
12736 * @param {Boolean} forceAll true to force "all" query
12737 * @param {Boolean} cancel true to cancel the query
12738 * @param {Object} e The query event object
12740 'beforequery': true,
12743 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12744 * @param {Roo.bootstrap.ComboBox} combo This combo box
12749 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12750 * @param {Roo.bootstrap.ComboBox} combo This combo box
12751 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12756 * Fires when the remove value from the combobox array
12757 * @param {Roo.bootstrap.ComboBox} combo This combo box
12761 * @event afterremove
12762 * Fires when the remove value from the combobox array
12763 * @param {Roo.bootstrap.ComboBox} combo This combo box
12765 'afterremove' : true,
12767 * @event specialfilter
12768 * Fires when specialfilter
12769 * @param {Roo.bootstrap.ComboBox} combo This combo box
12771 'specialfilter' : true,
12774 * Fires when tick the element
12775 * @param {Roo.bootstrap.ComboBox} combo This combo box
12779 * @event touchviewdisplay
12780 * Fires when touch view require special display (default is using displayField)
12781 * @param {Roo.bootstrap.ComboBox} combo This combo box
12782 * @param {Object} cfg set html .
12784 'touchviewdisplay' : true
12789 this.tickItems = [];
12791 this.selectedIndex = -1;
12792 if(this.mode == 'local'){
12793 if(config.queryDelay === undefined){
12794 this.queryDelay = 10;
12796 if(config.minChars === undefined){
12802 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12805 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12806 * rendering into an Roo.Editor, defaults to false)
12809 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12810 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12813 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12816 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12817 * the dropdown list (defaults to undefined, with no header element)
12821 * @cfg {String/Roo.Template} tpl The template to use to render the output
12825 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12827 listWidth: undefined,
12829 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12830 * mode = 'remote' or 'text' if mode = 'local')
12832 displayField: undefined,
12835 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12836 * mode = 'remote' or 'value' if mode = 'local').
12837 * Note: use of a valueField requires the user make a selection
12838 * in order for a value to be mapped.
12840 valueField: undefined,
12842 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12847 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12848 * field's data value (defaults to the underlying DOM element's name)
12850 hiddenName: undefined,
12852 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12856 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12858 selectedClass: 'active',
12861 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12865 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12866 * anchor positions (defaults to 'tl-bl')
12868 listAlign: 'tl-bl?',
12870 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12874 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12875 * query specified by the allQuery config option (defaults to 'query')
12877 triggerAction: 'query',
12879 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12880 * (defaults to 4, does not apply if editable = false)
12884 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12885 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12889 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12890 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12894 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12895 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12899 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12900 * when editable = true (defaults to false)
12902 selectOnFocus:false,
12904 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12906 queryParam: 'query',
12908 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12909 * when mode = 'remote' (defaults to 'Loading...')
12911 loadingText: 'Loading...',
12913 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12917 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12921 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12922 * traditional select (defaults to true)
12926 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12930 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12934 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12935 * listWidth has a higher value)
12939 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12940 * allow the user to set arbitrary text into the field (defaults to false)
12942 forceSelection:false,
12944 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12945 * if typeAhead = true (defaults to 250)
12947 typeAheadDelay : 250,
12949 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12950 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12952 valueNotFoundText : undefined,
12954 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12956 blockFocus : false,
12959 * @cfg {Boolean} disableClear Disable showing of clear button.
12961 disableClear : false,
12963 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12965 alwaysQuery : false,
12968 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12973 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12975 invalidClass : "has-warning",
12978 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12980 validClass : "has-success",
12983 * @cfg {Boolean} specialFilter (true|false) special filter default false
12985 specialFilter : false,
12988 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12990 mobileTouchView : true,
12993 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12995 useNativeIOS : false,
12997 ios_options : false,
13009 btnPosition : 'right',
13010 triggerList : true,
13011 showToggleBtn : true,
13013 emptyResultText: 'Empty',
13014 triggerText : 'Select',
13017 // element that contains real text value.. (when hidden is used..)
13019 getAutoCreate : function()
13024 * Render classic select for iso
13027 if(Roo.isIOS && this.useNativeIOS){
13028 cfg = this.getAutoCreateNativeIOS();
13036 if(Roo.isTouch && this.mobileTouchView){
13037 cfg = this.getAutoCreateTouchView();
13044 if(!this.tickable){
13045 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13050 * ComboBox with tickable selections
13053 var align = this.labelAlign || this.parentLabelAlign();
13056 cls : 'form-group roo-combobox-tickable' //input-group
13059 var btn_text_select = '';
13060 var btn_text_done = '';
13061 var btn_text_cancel = '';
13063 if (this.btn_text_show) {
13064 btn_text_select = 'Select';
13065 btn_text_done = 'Done';
13066 btn_text_cancel = 'Cancel';
13071 cls : 'tickable-buttons',
13076 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13077 //html : this.triggerText
13078 html: btn_text_select
13084 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13086 html: btn_text_done
13092 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13094 html: btn_text_cancel
13100 buttons.cn.unshift({
13102 cls: 'roo-select2-search-field-input'
13108 Roo.each(buttons.cn, function(c){
13110 c.cls += ' btn-' + _this.size;
13113 if (_this.disabled) {
13124 cls: 'form-hidden-field'
13128 cls: 'roo-select2-choices',
13132 cls: 'roo-select2-search-field',
13143 cls: 'roo-select2-container input-group roo-select2-container-multi',
13148 // cls: 'typeahead typeahead-long dropdown-menu',
13149 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13154 if(this.hasFeedback && !this.allowBlank){
13158 cls: 'glyphicon form-control-feedback'
13161 combobox.cn.push(feedback);
13165 if (align ==='left' && this.fieldLabel.length) {
13167 cfg.cls += ' roo-form-group-label-left';
13172 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13173 tooltip : 'This field is required'
13178 cls : 'control-label',
13179 html : this.fieldLabel
13191 var labelCfg = cfg.cn[1];
13192 var contentCfg = cfg.cn[2];
13195 if(this.indicatorpos == 'right'){
13201 cls : 'control-label',
13205 html : this.fieldLabel
13209 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13210 tooltip : 'This field is required'
13225 labelCfg = cfg.cn[0];
13226 contentCfg = cfg.cn[1];
13230 if(this.labelWidth > 12){
13231 labelCfg.style = "width: " + this.labelWidth + 'px';
13234 if(this.labelWidth < 13 && this.labelmd == 0){
13235 this.labelmd = this.labelWidth;
13238 if(this.labellg > 0){
13239 labelCfg.cls += ' col-lg-' + this.labellg;
13240 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13243 if(this.labelmd > 0){
13244 labelCfg.cls += ' col-md-' + this.labelmd;
13245 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13248 if(this.labelsm > 0){
13249 labelCfg.cls += ' col-sm-' + this.labelsm;
13250 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13253 if(this.labelxs > 0){
13254 labelCfg.cls += ' col-xs-' + this.labelxs;
13255 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13259 } else if ( this.fieldLabel.length) {
13260 // Roo.log(" label");
13264 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13265 tooltip : 'This field is required'
13269 //cls : 'input-group-addon',
13270 html : this.fieldLabel
13275 if(this.indicatorpos == 'right'){
13279 //cls : 'input-group-addon',
13280 html : this.fieldLabel
13284 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13285 tooltip : 'This field is required'
13294 // Roo.log(" no label && no align");
13301 ['xs','sm','md','lg'].map(function(size){
13302 if (settings[size]) {
13303 cfg.cls += ' col-' + size + '-' + settings[size];
13311 _initEventsCalled : false,
13314 initEvents: function()
13316 if (this._initEventsCalled) { // as we call render... prevent looping...
13319 this._initEventsCalled = true;
13322 throw "can not find store for combo";
13325 this.indicator = this.indicatorEl();
13327 this.store = Roo.factory(this.store, Roo.data);
13328 this.store.parent = this;
13330 // if we are building from html. then this element is so complex, that we can not really
13331 // use the rendered HTML.
13332 // so we have to trash and replace the previous code.
13333 if (Roo.XComponent.build_from_html) {
13334 // remove this element....
13335 var e = this.el.dom, k=0;
13336 while (e ) { e = e.previousSibling; ++k;}
13341 this.rendered = false;
13343 this.render(this.parent().getChildContainer(true), k);
13346 if(Roo.isIOS && this.useNativeIOS){
13347 this.initIOSView();
13355 if(Roo.isTouch && this.mobileTouchView){
13356 this.initTouchView();
13361 this.initTickableEvents();
13365 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13367 if(this.hiddenName){
13369 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13371 this.hiddenField.dom.value =
13372 this.hiddenValue !== undefined ? this.hiddenValue :
13373 this.value !== undefined ? this.value : '';
13375 // prevent input submission
13376 this.el.dom.removeAttribute('name');
13377 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13382 // this.el.dom.setAttribute('autocomplete', 'off');
13385 var cls = 'x-combo-list';
13387 //this.list = new Roo.Layer({
13388 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13394 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13395 _this.list.setWidth(lw);
13398 this.list.on('mouseover', this.onViewOver, this);
13399 this.list.on('mousemove', this.onViewMove, this);
13400 this.list.on('scroll', this.onViewScroll, this);
13403 this.list.swallowEvent('mousewheel');
13404 this.assetHeight = 0;
13407 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13408 this.assetHeight += this.header.getHeight();
13411 this.innerList = this.list.createChild({cls:cls+'-inner'});
13412 this.innerList.on('mouseover', this.onViewOver, this);
13413 this.innerList.on('mousemove', this.onViewMove, this);
13414 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13416 if(this.allowBlank && !this.pageSize && !this.disableClear){
13417 this.footer = this.list.createChild({cls:cls+'-ft'});
13418 this.pageTb = new Roo.Toolbar(this.footer);
13422 this.footer = this.list.createChild({cls:cls+'-ft'});
13423 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13424 {pageSize: this.pageSize});
13428 if (this.pageTb && this.allowBlank && !this.disableClear) {
13430 this.pageTb.add(new Roo.Toolbar.Fill(), {
13431 cls: 'x-btn-icon x-btn-clear',
13433 handler: function()
13436 _this.clearValue();
13437 _this.onSelect(false, -1);
13442 this.assetHeight += this.footer.getHeight();
13447 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13450 this.view = new Roo.View(this.list, this.tpl, {
13451 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13453 //this.view.wrapEl.setDisplayed(false);
13454 this.view.on('click', this.onViewClick, this);
13457 this.store.on('beforeload', this.onBeforeLoad, this);
13458 this.store.on('load', this.onLoad, this);
13459 this.store.on('loadexception', this.onLoadException, this);
13461 if(this.resizable){
13462 this.resizer = new Roo.Resizable(this.list, {
13463 pinned:true, handles:'se'
13465 this.resizer.on('resize', function(r, w, h){
13466 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13467 this.listWidth = w;
13468 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13469 this.restrictHeight();
13471 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13474 if(!this.editable){
13475 this.editable = true;
13476 this.setEditable(false);
13481 if (typeof(this.events.add.listeners) != 'undefined') {
13483 this.addicon = this.wrap.createChild(
13484 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13486 this.addicon.on('click', function(e) {
13487 this.fireEvent('add', this);
13490 if (typeof(this.events.edit.listeners) != 'undefined') {
13492 this.editicon = this.wrap.createChild(
13493 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13494 if (this.addicon) {
13495 this.editicon.setStyle('margin-left', '40px');
13497 this.editicon.on('click', function(e) {
13499 // we fire even if inothing is selected..
13500 this.fireEvent('edit', this, this.lastData );
13506 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13507 "up" : function(e){
13508 this.inKeyMode = true;
13512 "down" : function(e){
13513 if(!this.isExpanded()){
13514 this.onTriggerClick();
13516 this.inKeyMode = true;
13521 "enter" : function(e){
13522 // this.onViewClick();
13526 if(this.fireEvent("specialkey", this, e)){
13527 this.onViewClick(false);
13533 "esc" : function(e){
13537 "tab" : function(e){
13540 if(this.fireEvent("specialkey", this, e)){
13541 this.onViewClick(false);
13549 doRelay : function(foo, bar, hname){
13550 if(hname == 'down' || this.scope.isExpanded()){
13551 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13560 this.queryDelay = Math.max(this.queryDelay || 10,
13561 this.mode == 'local' ? 10 : 250);
13564 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13566 if(this.typeAhead){
13567 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13569 if(this.editable !== false){
13570 this.inputEl().on("keyup", this.onKeyUp, this);
13572 if(this.forceSelection){
13573 this.inputEl().on('blur', this.doForce, this);
13577 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13578 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13582 initTickableEvents: function()
13586 if(this.hiddenName){
13588 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13590 this.hiddenField.dom.value =
13591 this.hiddenValue !== undefined ? this.hiddenValue :
13592 this.value !== undefined ? this.value : '';
13594 // prevent input submission
13595 this.el.dom.removeAttribute('name');
13596 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13601 // this.list = this.el.select('ul.dropdown-menu',true).first();
13603 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13604 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13605 if(this.triggerList){
13606 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13609 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13610 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13612 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13613 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13615 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13616 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13618 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13619 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13620 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13623 this.cancelBtn.hide();
13628 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13629 _this.list.setWidth(lw);
13632 this.list.on('mouseover', this.onViewOver, this);
13633 this.list.on('mousemove', this.onViewMove, this);
13635 this.list.on('scroll', this.onViewScroll, this);
13638 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13639 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13642 this.view = new Roo.View(this.list, this.tpl, {
13647 selectedClass: this.selectedClass
13650 //this.view.wrapEl.setDisplayed(false);
13651 this.view.on('click', this.onViewClick, this);
13655 this.store.on('beforeload', this.onBeforeLoad, this);
13656 this.store.on('load', this.onLoad, this);
13657 this.store.on('loadexception', this.onLoadException, this);
13660 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13661 "up" : function(e){
13662 this.inKeyMode = true;
13666 "down" : function(e){
13667 this.inKeyMode = true;
13671 "enter" : function(e){
13672 if(this.fireEvent("specialkey", this, e)){
13673 this.onViewClick(false);
13679 "esc" : function(e){
13680 this.onTickableFooterButtonClick(e, false, false);
13683 "tab" : function(e){
13684 this.fireEvent("specialkey", this, e);
13686 this.onTickableFooterButtonClick(e, false, false);
13693 doRelay : function(e, fn, key){
13694 if(this.scope.isExpanded()){
13695 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13704 this.queryDelay = Math.max(this.queryDelay || 10,
13705 this.mode == 'local' ? 10 : 250);
13708 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13710 if(this.typeAhead){
13711 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13714 if(this.editable !== false){
13715 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13718 this.indicator = this.indicatorEl();
13720 if(this.indicator){
13721 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13722 this.indicator.hide();
13727 onDestroy : function(){
13729 this.view.setStore(null);
13730 this.view.el.removeAllListeners();
13731 this.view.el.remove();
13732 this.view.purgeListeners();
13735 this.list.dom.innerHTML = '';
13739 this.store.un('beforeload', this.onBeforeLoad, this);
13740 this.store.un('load', this.onLoad, this);
13741 this.store.un('loadexception', this.onLoadException, this);
13743 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13747 fireKey : function(e){
13748 if(e.isNavKeyPress() && !this.list.isVisible()){
13749 this.fireEvent("specialkey", this, e);
13754 onResize: function(w, h){
13755 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13757 // if(typeof w != 'number'){
13758 // // we do not handle it!?!?
13761 // var tw = this.trigger.getWidth();
13762 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13763 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13765 // this.inputEl().setWidth( this.adjustWidth('input', x));
13767 // //this.trigger.setStyle('left', x+'px');
13769 // if(this.list && this.listWidth === undefined){
13770 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13771 // this.list.setWidth(lw);
13772 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13780 * Allow or prevent the user from directly editing the field text. If false is passed,
13781 * the user will only be able to select from the items defined in the dropdown list. This method
13782 * is the runtime equivalent of setting the 'editable' config option at config time.
13783 * @param {Boolean} value True to allow the user to directly edit the field text
13785 setEditable : function(value){
13786 if(value == this.editable){
13789 this.editable = value;
13791 this.inputEl().dom.setAttribute('readOnly', true);
13792 this.inputEl().on('mousedown', this.onTriggerClick, this);
13793 this.inputEl().addClass('x-combo-noedit');
13795 this.inputEl().dom.setAttribute('readOnly', false);
13796 this.inputEl().un('mousedown', this.onTriggerClick, this);
13797 this.inputEl().removeClass('x-combo-noedit');
13803 onBeforeLoad : function(combo,opts){
13804 if(!this.hasFocus){
13808 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13810 this.restrictHeight();
13811 this.selectedIndex = -1;
13815 onLoad : function(){
13817 this.hasQuery = false;
13819 if(!this.hasFocus){
13823 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13824 this.loading.hide();
13827 if(this.store.getCount() > 0){
13830 this.restrictHeight();
13831 if(this.lastQuery == this.allQuery){
13832 if(this.editable && !this.tickable){
13833 this.inputEl().dom.select();
13837 !this.selectByValue(this.value, true) &&
13840 !this.store.lastOptions ||
13841 typeof(this.store.lastOptions.add) == 'undefined' ||
13842 this.store.lastOptions.add != true
13845 this.select(0, true);
13848 if(this.autoFocus){
13851 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13852 this.taTask.delay(this.typeAheadDelay);
13856 this.onEmptyResults();
13862 onLoadException : function()
13864 this.hasQuery = false;
13866 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13867 this.loading.hide();
13870 if(this.tickable && this.editable){
13875 // only causes errors at present
13876 //Roo.log(this.store.reader.jsonData);
13877 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13879 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13885 onTypeAhead : function(){
13886 if(this.store.getCount() > 0){
13887 var r = this.store.getAt(0);
13888 var newValue = r.data[this.displayField];
13889 var len = newValue.length;
13890 var selStart = this.getRawValue().length;
13892 if(selStart != len){
13893 this.setRawValue(newValue);
13894 this.selectText(selStart, newValue.length);
13900 onSelect : function(record, index){
13902 if(this.fireEvent('beforeselect', this, record, index) !== false){
13904 this.setFromData(index > -1 ? record.data : false);
13907 this.fireEvent('select', this, record, index);
13912 * Returns the currently selected field value or empty string if no value is set.
13913 * @return {String} value The selected value
13915 getValue : function()
13917 if(Roo.isIOS && this.useNativeIOS){
13918 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13922 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13925 if(this.valueField){
13926 return typeof this.value != 'undefined' ? this.value : '';
13928 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13932 getRawValue : function()
13934 if(Roo.isIOS && this.useNativeIOS){
13935 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13938 var v = this.inputEl().getValue();
13944 * Clears any text/value currently set in the field
13946 clearValue : function(){
13948 if(this.hiddenField){
13949 this.hiddenField.dom.value = '';
13952 this.setRawValue('');
13953 this.lastSelectionText = '';
13954 this.lastData = false;
13956 var close = this.closeTriggerEl();
13967 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13968 * will be displayed in the field. If the value does not match the data value of an existing item,
13969 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13970 * Otherwise the field will be blank (although the value will still be set).
13971 * @param {String} value The value to match
13973 setValue : function(v)
13975 if(Roo.isIOS && this.useNativeIOS){
13976 this.setIOSValue(v);
13986 if(this.valueField){
13987 var r = this.findRecord(this.valueField, v);
13989 text = r.data[this.displayField];
13990 }else if(this.valueNotFoundText !== undefined){
13991 text = this.valueNotFoundText;
13994 this.lastSelectionText = text;
13995 if(this.hiddenField){
13996 this.hiddenField.dom.value = v;
13998 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14001 var close = this.closeTriggerEl();
14004 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14010 * @property {Object} the last set data for the element
14015 * Sets the value of the field based on a object which is related to the record format for the store.
14016 * @param {Object} value the value to set as. or false on reset?
14018 setFromData : function(o){
14025 var dv = ''; // display value
14026 var vv = ''; // value value..
14028 if (this.displayField) {
14029 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14031 // this is an error condition!!!
14032 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14035 if(this.valueField){
14036 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14039 var close = this.closeTriggerEl();
14042 if(dv.length || vv * 1 > 0){
14044 this.blockFocus=true;
14050 if(this.hiddenField){
14051 this.hiddenField.dom.value = vv;
14053 this.lastSelectionText = dv;
14054 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14058 // no hidden field.. - we store the value in 'value', but still display
14059 // display field!!!!
14060 this.lastSelectionText = dv;
14061 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14068 reset : function(){
14069 // overridden so that last data is reset..
14076 this.setValue(this.originalValue);
14077 //this.clearInvalid();
14078 this.lastData = false;
14080 this.view.clearSelections();
14086 findRecord : function(prop, value){
14088 if(this.store.getCount() > 0){
14089 this.store.each(function(r){
14090 if(r.data[prop] == value){
14100 getName: function()
14102 // returns hidden if it's set..
14103 if (!this.rendered) {return ''};
14104 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14108 onViewMove : function(e, t){
14109 this.inKeyMode = false;
14113 onViewOver : function(e, t){
14114 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14117 var item = this.view.findItemFromChild(t);
14120 var index = this.view.indexOf(item);
14121 this.select(index, false);
14126 onViewClick : function(view, doFocus, el, e)
14128 var index = this.view.getSelectedIndexes()[0];
14130 var r = this.store.getAt(index);
14134 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14141 Roo.each(this.tickItems, function(v,k){
14143 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14145 _this.tickItems.splice(k, 1);
14147 if(typeof(e) == 'undefined' && view == false){
14148 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14160 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14161 this.tickItems.push(r.data);
14164 if(typeof(e) == 'undefined' && view == false){
14165 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14172 this.onSelect(r, index);
14174 if(doFocus !== false && !this.blockFocus){
14175 this.inputEl().focus();
14180 restrictHeight : function(){
14181 //this.innerList.dom.style.height = '';
14182 //var inner = this.innerList.dom;
14183 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14184 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14185 //this.list.beginUpdate();
14186 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14187 this.list.alignTo(this.inputEl(), this.listAlign);
14188 this.list.alignTo(this.inputEl(), this.listAlign);
14189 //this.list.endUpdate();
14193 onEmptyResults : function(){
14195 if(this.tickable && this.editable){
14196 this.hasFocus = false;
14197 this.restrictHeight();
14205 * Returns true if the dropdown list is expanded, else false.
14207 isExpanded : function(){
14208 return this.list.isVisible();
14212 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14213 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14214 * @param {String} value The data value of the item to select
14215 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14216 * selected item if it is not currently in view (defaults to true)
14217 * @return {Boolean} True if the value matched an item in the list, else false
14219 selectByValue : function(v, scrollIntoView){
14220 if(v !== undefined && v !== null){
14221 var r = this.findRecord(this.valueField || this.displayField, v);
14223 this.select(this.store.indexOf(r), scrollIntoView);
14231 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14232 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14233 * @param {Number} index The zero-based index of the list item to select
14234 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14235 * selected item if it is not currently in view (defaults to true)
14237 select : function(index, scrollIntoView){
14238 this.selectedIndex = index;
14239 this.view.select(index);
14240 if(scrollIntoView !== false){
14241 var el = this.view.getNode(index);
14243 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14246 this.list.scrollChildIntoView(el, false);
14252 selectNext : function(){
14253 var ct = this.store.getCount();
14255 if(this.selectedIndex == -1){
14257 }else if(this.selectedIndex < ct-1){
14258 this.select(this.selectedIndex+1);
14264 selectPrev : function(){
14265 var ct = this.store.getCount();
14267 if(this.selectedIndex == -1){
14269 }else if(this.selectedIndex != 0){
14270 this.select(this.selectedIndex-1);
14276 onKeyUp : function(e){
14277 if(this.editable !== false && !e.isSpecialKey()){
14278 this.lastKey = e.getKey();
14279 this.dqTask.delay(this.queryDelay);
14284 validateBlur : function(){
14285 return !this.list || !this.list.isVisible();
14289 initQuery : function(){
14291 var v = this.getRawValue();
14293 if(this.tickable && this.editable){
14294 v = this.tickableInputEl().getValue();
14301 doForce : function(){
14302 if(this.inputEl().dom.value.length > 0){
14303 this.inputEl().dom.value =
14304 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14310 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14311 * query allowing the query action to be canceled if needed.
14312 * @param {String} query The SQL query to execute
14313 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14314 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14315 * saved in the current store (defaults to false)
14317 doQuery : function(q, forceAll){
14319 if(q === undefined || q === null){
14324 forceAll: forceAll,
14328 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14333 forceAll = qe.forceAll;
14334 if(forceAll === true || (q.length >= this.minChars)){
14336 this.hasQuery = true;
14338 if(this.lastQuery != q || this.alwaysQuery){
14339 this.lastQuery = q;
14340 if(this.mode == 'local'){
14341 this.selectedIndex = -1;
14343 this.store.clearFilter();
14346 if(this.specialFilter){
14347 this.fireEvent('specialfilter', this);
14352 this.store.filter(this.displayField, q);
14355 this.store.fireEvent("datachanged", this.store);
14362 this.store.baseParams[this.queryParam] = q;
14364 var options = {params : this.getParams(q)};
14367 options.add = true;
14368 options.params.start = this.page * this.pageSize;
14371 this.store.load(options);
14374 * this code will make the page width larger, at the beginning, the list not align correctly,
14375 * we should expand the list on onLoad
14376 * so command out it
14381 this.selectedIndex = -1;
14386 this.loadNext = false;
14390 getParams : function(q){
14392 //p[this.queryParam] = q;
14396 p.limit = this.pageSize;
14402 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14404 collapse : function(){
14405 if(!this.isExpanded()){
14411 this.hasFocus = false;
14415 this.cancelBtn.hide();
14416 this.trigger.show();
14419 this.tickableInputEl().dom.value = '';
14420 this.tickableInputEl().blur();
14425 Roo.get(document).un('mousedown', this.collapseIf, this);
14426 Roo.get(document).un('mousewheel', this.collapseIf, this);
14427 if (!this.editable) {
14428 Roo.get(document).un('keydown', this.listKeyPress, this);
14430 this.fireEvent('collapse', this);
14436 collapseIf : function(e){
14437 var in_combo = e.within(this.el);
14438 var in_list = e.within(this.list);
14439 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14441 if (in_combo || in_list || is_list) {
14442 //e.stopPropagation();
14447 this.onTickableFooterButtonClick(e, false, false);
14455 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14457 expand : function(){
14459 if(this.isExpanded() || !this.hasFocus){
14463 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14464 this.list.setWidth(lw);
14470 this.restrictHeight();
14474 this.tickItems = Roo.apply([], this.item);
14477 this.cancelBtn.show();
14478 this.trigger.hide();
14481 this.tickableInputEl().focus();
14486 Roo.get(document).on('mousedown', this.collapseIf, this);
14487 Roo.get(document).on('mousewheel', this.collapseIf, this);
14488 if (!this.editable) {
14489 Roo.get(document).on('keydown', this.listKeyPress, this);
14492 this.fireEvent('expand', this);
14496 // Implements the default empty TriggerField.onTriggerClick function
14497 onTriggerClick : function(e)
14499 Roo.log('trigger click');
14501 if(this.disabled || !this.triggerList){
14506 this.loadNext = false;
14508 if(this.isExpanded()){
14510 if (!this.blockFocus) {
14511 this.inputEl().focus();
14515 this.hasFocus = true;
14516 if(this.triggerAction == 'all') {
14517 this.doQuery(this.allQuery, true);
14519 this.doQuery(this.getRawValue());
14521 if (!this.blockFocus) {
14522 this.inputEl().focus();
14527 onTickableTriggerClick : function(e)
14534 this.loadNext = false;
14535 this.hasFocus = true;
14537 if(this.triggerAction == 'all') {
14538 this.doQuery(this.allQuery, true);
14540 this.doQuery(this.getRawValue());
14544 onSearchFieldClick : function(e)
14546 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14547 this.onTickableFooterButtonClick(e, false, false);
14551 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14556 this.loadNext = false;
14557 this.hasFocus = true;
14559 if(this.triggerAction == 'all') {
14560 this.doQuery(this.allQuery, true);
14562 this.doQuery(this.getRawValue());
14566 listKeyPress : function(e)
14568 //Roo.log('listkeypress');
14569 // scroll to first matching element based on key pres..
14570 if (e.isSpecialKey()) {
14573 var k = String.fromCharCode(e.getKey()).toUpperCase();
14576 var csel = this.view.getSelectedNodes();
14577 var cselitem = false;
14579 var ix = this.view.indexOf(csel[0]);
14580 cselitem = this.store.getAt(ix);
14581 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14587 this.store.each(function(v) {
14589 // start at existing selection.
14590 if (cselitem.id == v.id) {
14596 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14597 match = this.store.indexOf(v);
14603 if (match === false) {
14604 return true; // no more action?
14607 this.view.select(match);
14608 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14609 sn.scrollIntoView(sn.dom.parentNode, false);
14612 onViewScroll : function(e, t){
14614 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){
14618 this.hasQuery = true;
14620 this.loading = this.list.select('.loading', true).first();
14622 if(this.loading === null){
14623 this.list.createChild({
14625 cls: 'loading roo-select2-more-results roo-select2-active',
14626 html: 'Loading more results...'
14629 this.loading = this.list.select('.loading', true).first();
14631 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14633 this.loading.hide();
14636 this.loading.show();
14641 this.loadNext = true;
14643 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14648 addItem : function(o)
14650 var dv = ''; // display value
14652 if (this.displayField) {
14653 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14655 // this is an error condition!!!
14656 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14663 var choice = this.choices.createChild({
14665 cls: 'roo-select2-search-choice',
14674 cls: 'roo-select2-search-choice-close fa fa-times',
14679 }, this.searchField);
14681 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14683 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14691 this.inputEl().dom.value = '';
14696 onRemoveItem : function(e, _self, o)
14698 e.preventDefault();
14700 this.lastItem = Roo.apply([], this.item);
14702 var index = this.item.indexOf(o.data) * 1;
14705 Roo.log('not this item?!');
14709 this.item.splice(index, 1);
14714 this.fireEvent('remove', this, e);
14720 syncValue : function()
14722 if(!this.item.length){
14729 Roo.each(this.item, function(i){
14730 if(_this.valueField){
14731 value.push(i[_this.valueField]);
14738 this.value = value.join(',');
14740 if(this.hiddenField){
14741 this.hiddenField.dom.value = this.value;
14744 this.store.fireEvent("datachanged", this.store);
14749 clearItem : function()
14751 if(!this.multiple){
14757 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14765 if(this.tickable && !Roo.isTouch){
14766 this.view.refresh();
14770 inputEl: function ()
14772 if(Roo.isIOS && this.useNativeIOS){
14773 return this.el.select('select.roo-ios-select', true).first();
14776 if(Roo.isTouch && this.mobileTouchView){
14777 return this.el.select('input.form-control',true).first();
14781 return this.searchField;
14784 return this.el.select('input.form-control',true).first();
14787 onTickableFooterButtonClick : function(e, btn, el)
14789 e.preventDefault();
14791 this.lastItem = Roo.apply([], this.item);
14793 if(btn && btn.name == 'cancel'){
14794 this.tickItems = Roo.apply([], this.item);
14803 Roo.each(this.tickItems, function(o){
14811 validate : function()
14813 if(this.getVisibilityEl().hasClass('hidden')){
14817 var v = this.getRawValue();
14820 v = this.getValue();
14823 if(this.disabled || this.allowBlank || v.length){
14828 this.markInvalid();
14832 tickableInputEl : function()
14834 if(!this.tickable || !this.editable){
14835 return this.inputEl();
14838 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14842 getAutoCreateTouchView : function()
14847 cls: 'form-group' //input-group
14853 type : this.inputType,
14854 cls : 'form-control x-combo-noedit',
14855 autocomplete: 'new-password',
14856 placeholder : this.placeholder || '',
14861 input.name = this.name;
14865 input.cls += ' input-' + this.size;
14868 if (this.disabled) {
14869 input.disabled = true;
14880 inputblock.cls += ' input-group';
14882 inputblock.cn.unshift({
14884 cls : 'input-group-addon',
14889 if(this.removable && !this.multiple){
14890 inputblock.cls += ' roo-removable';
14892 inputblock.cn.push({
14895 cls : 'roo-combo-removable-btn close'
14899 if(this.hasFeedback && !this.allowBlank){
14901 inputblock.cls += ' has-feedback';
14903 inputblock.cn.push({
14905 cls: 'glyphicon form-control-feedback'
14912 inputblock.cls += (this.before) ? '' : ' input-group';
14914 inputblock.cn.push({
14916 cls : 'input-group-addon',
14927 cls: 'form-hidden-field'
14941 cls: 'form-hidden-field'
14945 cls: 'roo-select2-choices',
14949 cls: 'roo-select2-search-field',
14962 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14968 if(!this.multiple && this.showToggleBtn){
14975 if (this.caret != false) {
14978 cls: 'fa fa-' + this.caret
14985 cls : 'input-group-addon btn dropdown-toggle',
14990 cls: 'combobox-clear',
15004 combobox.cls += ' roo-select2-container-multi';
15007 var align = this.labelAlign || this.parentLabelAlign();
15009 if (align ==='left' && this.fieldLabel.length) {
15014 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15015 tooltip : 'This field is required'
15019 cls : 'control-label',
15020 html : this.fieldLabel
15031 var labelCfg = cfg.cn[1];
15032 var contentCfg = cfg.cn[2];
15035 if(this.indicatorpos == 'right'){
15040 cls : 'control-label',
15044 html : this.fieldLabel
15048 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15049 tooltip : 'This field is required'
15062 labelCfg = cfg.cn[0];
15063 contentCfg = cfg.cn[1];
15068 if(this.labelWidth > 12){
15069 labelCfg.style = "width: " + this.labelWidth + 'px';
15072 if(this.labelWidth < 13 && this.labelmd == 0){
15073 this.labelmd = this.labelWidth;
15076 if(this.labellg > 0){
15077 labelCfg.cls += ' col-lg-' + this.labellg;
15078 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15081 if(this.labelmd > 0){
15082 labelCfg.cls += ' col-md-' + this.labelmd;
15083 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15086 if(this.labelsm > 0){
15087 labelCfg.cls += ' col-sm-' + this.labelsm;
15088 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15091 if(this.labelxs > 0){
15092 labelCfg.cls += ' col-xs-' + this.labelxs;
15093 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15097 } else if ( this.fieldLabel.length) {
15101 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15102 tooltip : 'This field is required'
15106 cls : 'control-label',
15107 html : this.fieldLabel
15118 if(this.indicatorpos == 'right'){
15122 cls : 'control-label',
15123 html : this.fieldLabel,
15127 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15128 tooltip : 'This field is required'
15145 var settings = this;
15147 ['xs','sm','md','lg'].map(function(size){
15148 if (settings[size]) {
15149 cfg.cls += ' col-' + size + '-' + settings[size];
15156 initTouchView : function()
15158 this.renderTouchView();
15160 this.touchViewEl.on('scroll', function(){
15161 this.el.dom.scrollTop = 0;
15164 this.originalValue = this.getValue();
15166 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15168 this.inputEl().on("click", this.showTouchView, this);
15169 if (this.triggerEl) {
15170 this.triggerEl.on("click", this.showTouchView, this);
15174 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15175 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15177 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15179 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15180 this.store.on('load', this.onTouchViewLoad, this);
15181 this.store.on('loadexception', this.onTouchViewLoadException, this);
15183 if(this.hiddenName){
15185 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15187 this.hiddenField.dom.value =
15188 this.hiddenValue !== undefined ? this.hiddenValue :
15189 this.value !== undefined ? this.value : '';
15191 this.el.dom.removeAttribute('name');
15192 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15196 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15197 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15200 if(this.removable && !this.multiple){
15201 var close = this.closeTriggerEl();
15203 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15204 close.on('click', this.removeBtnClick, this, close);
15208 * fix the bug in Safari iOS8
15210 this.inputEl().on("focus", function(e){
15211 document.activeElement.blur();
15219 renderTouchView : function()
15221 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15222 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15224 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15225 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15227 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15228 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15229 this.touchViewBodyEl.setStyle('overflow', 'auto');
15231 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15232 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15234 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15235 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15239 showTouchView : function()
15245 this.touchViewHeaderEl.hide();
15247 if(this.modalTitle.length){
15248 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15249 this.touchViewHeaderEl.show();
15252 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15253 this.touchViewEl.show();
15255 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15257 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15258 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15260 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15262 if(this.modalTitle.length){
15263 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15266 this.touchViewBodyEl.setHeight(bodyHeight);
15270 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15272 this.touchViewEl.addClass('in');
15275 this.doTouchViewQuery();
15279 hideTouchView : function()
15281 this.touchViewEl.removeClass('in');
15285 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15287 this.touchViewEl.setStyle('display', 'none');
15292 setTouchViewValue : function()
15299 Roo.each(this.tickItems, function(o){
15304 this.hideTouchView();
15307 doTouchViewQuery : function()
15316 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15320 if(!this.alwaysQuery || this.mode == 'local'){
15321 this.onTouchViewLoad();
15328 onTouchViewBeforeLoad : function(combo,opts)
15334 onTouchViewLoad : function()
15336 if(this.store.getCount() < 1){
15337 this.onTouchViewEmptyResults();
15341 this.clearTouchView();
15343 var rawValue = this.getRawValue();
15345 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15347 this.tickItems = [];
15349 this.store.data.each(function(d, rowIndex){
15350 var row = this.touchViewListGroup.createChild(template);
15352 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15353 row.addClass(d.data.cls);
15356 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15359 html : d.data[this.displayField]
15362 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15363 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15366 row.removeClass('selected');
15367 if(!this.multiple && this.valueField &&
15368 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15371 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15372 row.addClass('selected');
15375 if(this.multiple && this.valueField &&
15376 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15380 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15381 this.tickItems.push(d.data);
15384 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15388 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15390 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15392 if(this.modalTitle.length){
15393 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15396 var listHeight = this.touchViewListGroup.getHeight();
15400 if(firstChecked && listHeight > bodyHeight){
15401 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15406 onTouchViewLoadException : function()
15408 this.hideTouchView();
15411 onTouchViewEmptyResults : function()
15413 this.clearTouchView();
15415 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15417 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15421 clearTouchView : function()
15423 this.touchViewListGroup.dom.innerHTML = '';
15426 onTouchViewClick : function(e, el, o)
15428 e.preventDefault();
15431 var rowIndex = o.rowIndex;
15433 var r = this.store.getAt(rowIndex);
15435 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15437 if(!this.multiple){
15438 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15439 c.dom.removeAttribute('checked');
15442 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15444 this.setFromData(r.data);
15446 var close = this.closeTriggerEl();
15452 this.hideTouchView();
15454 this.fireEvent('select', this, r, rowIndex);
15459 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15460 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15461 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15465 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15466 this.addItem(r.data);
15467 this.tickItems.push(r.data);
15471 getAutoCreateNativeIOS : function()
15474 cls: 'form-group' //input-group,
15479 cls : 'roo-ios-select'
15483 combobox.name = this.name;
15486 if (this.disabled) {
15487 combobox.disabled = true;
15490 var settings = this;
15492 ['xs','sm','md','lg'].map(function(size){
15493 if (settings[size]) {
15494 cfg.cls += ' col-' + size + '-' + settings[size];
15504 initIOSView : function()
15506 this.store.on('load', this.onIOSViewLoad, this);
15511 onIOSViewLoad : function()
15513 if(this.store.getCount() < 1){
15517 this.clearIOSView();
15519 if(this.allowBlank) {
15521 var default_text = '-- SELECT --';
15523 if(this.placeholder.length){
15524 default_text = this.placeholder;
15527 if(this.emptyTitle.length){
15528 default_text += ' - ' + this.emptyTitle + ' -';
15531 var opt = this.inputEl().createChild({
15534 html : default_text
15538 o[this.valueField] = 0;
15539 o[this.displayField] = default_text;
15541 this.ios_options.push({
15548 this.store.data.each(function(d, rowIndex){
15552 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15553 html = d.data[this.displayField];
15558 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15559 value = d.data[this.valueField];
15568 if(this.value == d.data[this.valueField]){
15569 option['selected'] = true;
15572 var opt = this.inputEl().createChild(option);
15574 this.ios_options.push({
15581 this.inputEl().on('change', function(){
15582 this.fireEvent('select', this);
15587 clearIOSView: function()
15589 this.inputEl().dom.innerHTML = '';
15591 this.ios_options = [];
15594 setIOSValue: function(v)
15598 if(!this.ios_options){
15602 Roo.each(this.ios_options, function(opts){
15604 opts.el.dom.removeAttribute('selected');
15606 if(opts.data[this.valueField] != v){
15610 opts.el.dom.setAttribute('selected', true);
15616 * @cfg {Boolean} grow
15620 * @cfg {Number} growMin
15624 * @cfg {Number} growMax
15633 Roo.apply(Roo.bootstrap.ComboBox, {
15637 cls: 'modal-header',
15659 cls: 'list-group-item',
15663 cls: 'roo-combobox-list-group-item-value'
15667 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15681 listItemCheckbox : {
15683 cls: 'list-group-item',
15687 cls: 'roo-combobox-list-group-item-value'
15691 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15707 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15712 cls: 'modal-footer',
15720 cls: 'col-xs-6 text-left',
15723 cls: 'btn btn-danger roo-touch-view-cancel',
15729 cls: 'col-xs-6 text-right',
15732 cls: 'btn btn-success roo-touch-view-ok',
15743 Roo.apply(Roo.bootstrap.ComboBox, {
15745 touchViewTemplate : {
15747 cls: 'modal fade roo-combobox-touch-view',
15751 cls: 'modal-dialog',
15752 style : 'position:fixed', // we have to fix position....
15756 cls: 'modal-content',
15758 Roo.bootstrap.ComboBox.header,
15759 Roo.bootstrap.ComboBox.body,
15760 Roo.bootstrap.ComboBox.footer
15769 * Ext JS Library 1.1.1
15770 * Copyright(c) 2006-2007, Ext JS, LLC.
15772 * Originally Released Under LGPL - original licence link has changed is not relivant.
15775 * <script type="text/javascript">
15780 * @extends Roo.util.Observable
15781 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15782 * This class also supports single and multi selection modes. <br>
15783 * Create a data model bound view:
15785 var store = new Roo.data.Store(...);
15787 var view = new Roo.View({
15789 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15791 singleSelect: true,
15792 selectedClass: "ydataview-selected",
15796 // listen for node click?
15797 view.on("click", function(vw, index, node, e){
15798 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15802 dataModel.load("foobar.xml");
15804 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15806 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15807 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15809 * Note: old style constructor is still suported (container, template, config)
15812 * Create a new View
15813 * @param {Object} config The config object
15816 Roo.View = function(config, depreciated_tpl, depreciated_config){
15818 this.parent = false;
15820 if (typeof(depreciated_tpl) == 'undefined') {
15821 // new way.. - universal constructor.
15822 Roo.apply(this, config);
15823 this.el = Roo.get(this.el);
15826 this.el = Roo.get(config);
15827 this.tpl = depreciated_tpl;
15828 Roo.apply(this, depreciated_config);
15830 this.wrapEl = this.el.wrap().wrap();
15831 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15834 if(typeof(this.tpl) == "string"){
15835 this.tpl = new Roo.Template(this.tpl);
15837 // support xtype ctors..
15838 this.tpl = new Roo.factory(this.tpl, Roo);
15842 this.tpl.compile();
15847 * @event beforeclick
15848 * Fires before a click is processed. Returns false to cancel the default action.
15849 * @param {Roo.View} this
15850 * @param {Number} index The index of the target node
15851 * @param {HTMLElement} node The target node
15852 * @param {Roo.EventObject} e The raw event object
15854 "beforeclick" : true,
15857 * Fires when a template node is clicked.
15858 * @param {Roo.View} this
15859 * @param {Number} index The index of the target node
15860 * @param {HTMLElement} node The target node
15861 * @param {Roo.EventObject} e The raw event object
15866 * Fires when a template node is double clicked.
15867 * @param {Roo.View} this
15868 * @param {Number} index The index of the target node
15869 * @param {HTMLElement} node The target node
15870 * @param {Roo.EventObject} e The raw event object
15874 * @event contextmenu
15875 * Fires when a template node is right clicked.
15876 * @param {Roo.View} this
15877 * @param {Number} index The index of the target node
15878 * @param {HTMLElement} node The target node
15879 * @param {Roo.EventObject} e The raw event object
15881 "contextmenu" : true,
15883 * @event selectionchange
15884 * Fires when the selected nodes change.
15885 * @param {Roo.View} this
15886 * @param {Array} selections Array of the selected nodes
15888 "selectionchange" : true,
15891 * @event beforeselect
15892 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15893 * @param {Roo.View} this
15894 * @param {HTMLElement} node The node to be selected
15895 * @param {Array} selections Array of currently selected nodes
15897 "beforeselect" : true,
15899 * @event preparedata
15900 * Fires on every row to render, to allow you to change the data.
15901 * @param {Roo.View} this
15902 * @param {Object} data to be rendered (change this)
15904 "preparedata" : true
15912 "click": this.onClick,
15913 "dblclick": this.onDblClick,
15914 "contextmenu": this.onContextMenu,
15918 this.selections = [];
15920 this.cmp = new Roo.CompositeElementLite([]);
15922 this.store = Roo.factory(this.store, Roo.data);
15923 this.setStore(this.store, true);
15926 if ( this.footer && this.footer.xtype) {
15928 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15930 this.footer.dataSource = this.store;
15931 this.footer.container = fctr;
15932 this.footer = Roo.factory(this.footer, Roo);
15933 fctr.insertFirst(this.el);
15935 // this is a bit insane - as the paging toolbar seems to detach the el..
15936 // dom.parentNode.parentNode.parentNode
15937 // they get detached?
15941 Roo.View.superclass.constructor.call(this);
15946 Roo.extend(Roo.View, Roo.util.Observable, {
15949 * @cfg {Roo.data.Store} store Data store to load data from.
15954 * @cfg {String|Roo.Element} el The container element.
15959 * @cfg {String|Roo.Template} tpl The template used by this View
15963 * @cfg {String} dataName the named area of the template to use as the data area
15964 * Works with domtemplates roo-name="name"
15968 * @cfg {String} selectedClass The css class to add to selected nodes
15970 selectedClass : "x-view-selected",
15972 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15977 * @cfg {String} text to display on mask (default Loading)
15981 * @cfg {Boolean} multiSelect Allow multiple selection
15983 multiSelect : false,
15985 * @cfg {Boolean} singleSelect Allow single selection
15987 singleSelect: false,
15990 * @cfg {Boolean} toggleSelect - selecting
15992 toggleSelect : false,
15995 * @cfg {Boolean} tickable - selecting
16000 * Returns the element this view is bound to.
16001 * @return {Roo.Element}
16003 getEl : function(){
16004 return this.wrapEl;
16010 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16012 refresh : function(){
16013 //Roo.log('refresh');
16016 // if we are using something like 'domtemplate', then
16017 // the what gets used is:
16018 // t.applySubtemplate(NAME, data, wrapping data..)
16019 // the outer template then get' applied with
16020 // the store 'extra data'
16021 // and the body get's added to the
16022 // roo-name="data" node?
16023 // <span class='roo-tpl-{name}'></span> ?????
16027 this.clearSelections();
16028 this.el.update("");
16030 var records = this.store.getRange();
16031 if(records.length < 1) {
16033 // is this valid?? = should it render a template??
16035 this.el.update(this.emptyText);
16039 if (this.dataName) {
16040 this.el.update(t.apply(this.store.meta)); //????
16041 el = this.el.child('.roo-tpl-' + this.dataName);
16044 for(var i = 0, len = records.length; i < len; i++){
16045 var data = this.prepareData(records[i].data, i, records[i]);
16046 this.fireEvent("preparedata", this, data, i, records[i]);
16048 var d = Roo.apply({}, data);
16051 Roo.apply(d, {'roo-id' : Roo.id()});
16055 Roo.each(this.parent.item, function(item){
16056 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16059 Roo.apply(d, {'roo-data-checked' : 'checked'});
16063 html[html.length] = Roo.util.Format.trim(
16065 t.applySubtemplate(this.dataName, d, this.store.meta) :
16072 el.update(html.join(""));
16073 this.nodes = el.dom.childNodes;
16074 this.updateIndexes(0);
16079 * Function to override to reformat the data that is sent to
16080 * the template for each node.
16081 * DEPRICATED - use the preparedata event handler.
16082 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16083 * a JSON object for an UpdateManager bound view).
16085 prepareData : function(data, index, record)
16087 this.fireEvent("preparedata", this, data, index, record);
16091 onUpdate : function(ds, record){
16092 // Roo.log('on update');
16093 this.clearSelections();
16094 var index = this.store.indexOf(record);
16095 var n = this.nodes[index];
16096 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16097 n.parentNode.removeChild(n);
16098 this.updateIndexes(index, index);
16104 onAdd : function(ds, records, index)
16106 //Roo.log(['on Add', ds, records, index] );
16107 this.clearSelections();
16108 if(this.nodes.length == 0){
16112 var n = this.nodes[index];
16113 for(var i = 0, len = records.length; i < len; i++){
16114 var d = this.prepareData(records[i].data, i, records[i]);
16116 this.tpl.insertBefore(n, d);
16119 this.tpl.append(this.el, d);
16122 this.updateIndexes(index);
16125 onRemove : function(ds, record, index){
16126 // Roo.log('onRemove');
16127 this.clearSelections();
16128 var el = this.dataName ?
16129 this.el.child('.roo-tpl-' + this.dataName) :
16132 el.dom.removeChild(this.nodes[index]);
16133 this.updateIndexes(index);
16137 * Refresh an individual node.
16138 * @param {Number} index
16140 refreshNode : function(index){
16141 this.onUpdate(this.store, this.store.getAt(index));
16144 updateIndexes : function(startIndex, endIndex){
16145 var ns = this.nodes;
16146 startIndex = startIndex || 0;
16147 endIndex = endIndex || ns.length - 1;
16148 for(var i = startIndex; i <= endIndex; i++){
16149 ns[i].nodeIndex = i;
16154 * Changes the data store this view uses and refresh the view.
16155 * @param {Store} store
16157 setStore : function(store, initial){
16158 if(!initial && this.store){
16159 this.store.un("datachanged", this.refresh);
16160 this.store.un("add", this.onAdd);
16161 this.store.un("remove", this.onRemove);
16162 this.store.un("update", this.onUpdate);
16163 this.store.un("clear", this.refresh);
16164 this.store.un("beforeload", this.onBeforeLoad);
16165 this.store.un("load", this.onLoad);
16166 this.store.un("loadexception", this.onLoad);
16170 store.on("datachanged", this.refresh, this);
16171 store.on("add", this.onAdd, this);
16172 store.on("remove", this.onRemove, this);
16173 store.on("update", this.onUpdate, this);
16174 store.on("clear", this.refresh, this);
16175 store.on("beforeload", this.onBeforeLoad, this);
16176 store.on("load", this.onLoad, this);
16177 store.on("loadexception", this.onLoad, this);
16185 * onbeforeLoad - masks the loading area.
16188 onBeforeLoad : function(store,opts)
16190 //Roo.log('onBeforeLoad');
16192 this.el.update("");
16194 this.el.mask(this.mask ? this.mask : "Loading" );
16196 onLoad : function ()
16203 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16204 * @param {HTMLElement} node
16205 * @return {HTMLElement} The template node
16207 findItemFromChild : function(node){
16208 var el = this.dataName ?
16209 this.el.child('.roo-tpl-' + this.dataName,true) :
16212 if(!node || node.parentNode == el){
16215 var p = node.parentNode;
16216 while(p && p != el){
16217 if(p.parentNode == el){
16226 onClick : function(e){
16227 var item = this.findItemFromChild(e.getTarget());
16229 var index = this.indexOf(item);
16230 if(this.onItemClick(item, index, e) !== false){
16231 this.fireEvent("click", this, index, item, e);
16234 this.clearSelections();
16239 onContextMenu : function(e){
16240 var item = this.findItemFromChild(e.getTarget());
16242 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16247 onDblClick : function(e){
16248 var item = this.findItemFromChild(e.getTarget());
16250 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16254 onItemClick : function(item, index, e)
16256 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16259 if (this.toggleSelect) {
16260 var m = this.isSelected(item) ? 'unselect' : 'select';
16263 _t[m](item, true, false);
16266 if(this.multiSelect || this.singleSelect){
16267 if(this.multiSelect && e.shiftKey && this.lastSelection){
16268 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16270 this.select(item, this.multiSelect && e.ctrlKey);
16271 this.lastSelection = item;
16274 if(!this.tickable){
16275 e.preventDefault();
16283 * Get the number of selected nodes.
16286 getSelectionCount : function(){
16287 return this.selections.length;
16291 * Get the currently selected nodes.
16292 * @return {Array} An array of HTMLElements
16294 getSelectedNodes : function(){
16295 return this.selections;
16299 * Get the indexes of the selected nodes.
16302 getSelectedIndexes : function(){
16303 var indexes = [], s = this.selections;
16304 for(var i = 0, len = s.length; i < len; i++){
16305 indexes.push(s[i].nodeIndex);
16311 * Clear all selections
16312 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16314 clearSelections : function(suppressEvent){
16315 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16316 this.cmp.elements = this.selections;
16317 this.cmp.removeClass(this.selectedClass);
16318 this.selections = [];
16319 if(!suppressEvent){
16320 this.fireEvent("selectionchange", this, this.selections);
16326 * Returns true if the passed node is selected
16327 * @param {HTMLElement/Number} node The node or node index
16328 * @return {Boolean}
16330 isSelected : function(node){
16331 var s = this.selections;
16335 node = this.getNode(node);
16336 return s.indexOf(node) !== -1;
16341 * @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
16342 * @param {Boolean} keepExisting (optional) true to keep existing selections
16343 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16345 select : function(nodeInfo, keepExisting, suppressEvent){
16346 if(nodeInfo instanceof Array){
16348 this.clearSelections(true);
16350 for(var i = 0, len = nodeInfo.length; i < len; i++){
16351 this.select(nodeInfo[i], true, true);
16355 var node = this.getNode(nodeInfo);
16356 if(!node || this.isSelected(node)){
16357 return; // already selected.
16360 this.clearSelections(true);
16363 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16364 Roo.fly(node).addClass(this.selectedClass);
16365 this.selections.push(node);
16366 if(!suppressEvent){
16367 this.fireEvent("selectionchange", this, this.selections);
16375 * @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
16376 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16377 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16379 unselect : function(nodeInfo, keepExisting, suppressEvent)
16381 if(nodeInfo instanceof Array){
16382 Roo.each(this.selections, function(s) {
16383 this.unselect(s, nodeInfo);
16387 var node = this.getNode(nodeInfo);
16388 if(!node || !this.isSelected(node)){
16389 //Roo.log("not selected");
16390 return; // not selected.
16394 Roo.each(this.selections, function(s) {
16396 Roo.fly(node).removeClass(this.selectedClass);
16403 this.selections= ns;
16404 this.fireEvent("selectionchange", this, this.selections);
16408 * Gets a template node.
16409 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16410 * @return {HTMLElement} The node or null if it wasn't found
16412 getNode : function(nodeInfo){
16413 if(typeof nodeInfo == "string"){
16414 return document.getElementById(nodeInfo);
16415 }else if(typeof nodeInfo == "number"){
16416 return this.nodes[nodeInfo];
16422 * Gets a range template nodes.
16423 * @param {Number} startIndex
16424 * @param {Number} endIndex
16425 * @return {Array} An array of nodes
16427 getNodes : function(start, end){
16428 var ns = this.nodes;
16429 start = start || 0;
16430 end = typeof end == "undefined" ? ns.length - 1 : end;
16433 for(var i = start; i <= end; i++){
16437 for(var i = start; i >= end; i--){
16445 * Finds the index of the passed node
16446 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16447 * @return {Number} The index of the node or -1
16449 indexOf : function(node){
16450 node = this.getNode(node);
16451 if(typeof node.nodeIndex == "number"){
16452 return node.nodeIndex;
16454 var ns = this.nodes;
16455 for(var i = 0, len = ns.length; i < len; i++){
16466 * based on jquery fullcalendar
16470 Roo.bootstrap = Roo.bootstrap || {};
16472 * @class Roo.bootstrap.Calendar
16473 * @extends Roo.bootstrap.Component
16474 * Bootstrap Calendar class
16475 * @cfg {Boolean} loadMask (true|false) default false
16476 * @cfg {Object} header generate the user specific header of the calendar, default false
16479 * Create a new Container
16480 * @param {Object} config The config object
16485 Roo.bootstrap.Calendar = function(config){
16486 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16490 * Fires when a date is selected
16491 * @param {DatePicker} this
16492 * @param {Date} date The selected date
16496 * @event monthchange
16497 * Fires when the displayed month changes
16498 * @param {DatePicker} this
16499 * @param {Date} date The selected month
16501 'monthchange': true,
16503 * @event evententer
16504 * Fires when mouse over an event
16505 * @param {Calendar} this
16506 * @param {event} Event
16508 'evententer': true,
16510 * @event eventleave
16511 * Fires when the mouse leaves an
16512 * @param {Calendar} this
16515 'eventleave': true,
16517 * @event eventclick
16518 * Fires when the mouse click an
16519 * @param {Calendar} this
16528 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16531 * @cfg {Number} startDay
16532 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16540 getAutoCreate : function(){
16543 var fc_button = function(name, corner, style, content ) {
16544 return Roo.apply({},{
16546 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16548 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16551 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16562 style : 'width:100%',
16569 cls : 'fc-header-left',
16571 fc_button('prev', 'left', 'arrow', '‹' ),
16572 fc_button('next', 'right', 'arrow', '›' ),
16573 { tag: 'span', cls: 'fc-header-space' },
16574 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16582 cls : 'fc-header-center',
16586 cls: 'fc-header-title',
16589 html : 'month / year'
16597 cls : 'fc-header-right',
16599 /* fc_button('month', 'left', '', 'month' ),
16600 fc_button('week', '', '', 'week' ),
16601 fc_button('day', 'right', '', 'day' )
16613 header = this.header;
16616 var cal_heads = function() {
16618 // fixme - handle this.
16620 for (var i =0; i < Date.dayNames.length; i++) {
16621 var d = Date.dayNames[i];
16624 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16625 html : d.substring(0,3)
16629 ret[0].cls += ' fc-first';
16630 ret[6].cls += ' fc-last';
16633 var cal_cell = function(n) {
16636 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16641 cls: 'fc-day-number',
16645 cls: 'fc-day-content',
16649 style: 'position: relative;' // height: 17px;
16661 var cal_rows = function() {
16664 for (var r = 0; r < 6; r++) {
16671 for (var i =0; i < Date.dayNames.length; i++) {
16672 var d = Date.dayNames[i];
16673 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16676 row.cn[0].cls+=' fc-first';
16677 row.cn[0].cn[0].style = 'min-height:90px';
16678 row.cn[6].cls+=' fc-last';
16682 ret[0].cls += ' fc-first';
16683 ret[4].cls += ' fc-prev-last';
16684 ret[5].cls += ' fc-last';
16691 cls: 'fc-border-separate',
16692 style : 'width:100%',
16700 cls : 'fc-first fc-last',
16718 cls : 'fc-content',
16719 style : "position: relative;",
16722 cls : 'fc-view fc-view-month fc-grid',
16723 style : 'position: relative',
16724 unselectable : 'on',
16727 cls : 'fc-event-container',
16728 style : 'position:absolute;z-index:8;top:0;left:0;'
16746 initEvents : function()
16749 throw "can not find store for calendar";
16755 style: "text-align:center",
16759 style: "background-color:white;width:50%;margin:250 auto",
16763 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16774 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16776 var size = this.el.select('.fc-content', true).first().getSize();
16777 this.maskEl.setSize(size.width, size.height);
16778 this.maskEl.enableDisplayMode("block");
16779 if(!this.loadMask){
16780 this.maskEl.hide();
16783 this.store = Roo.factory(this.store, Roo.data);
16784 this.store.on('load', this.onLoad, this);
16785 this.store.on('beforeload', this.onBeforeLoad, this);
16789 this.cells = this.el.select('.fc-day',true);
16790 //Roo.log(this.cells);
16791 this.textNodes = this.el.query('.fc-day-number');
16792 this.cells.addClassOnOver('fc-state-hover');
16794 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16795 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16796 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16797 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16799 this.on('monthchange', this.onMonthChange, this);
16801 this.update(new Date().clearTime());
16804 resize : function() {
16805 var sz = this.el.getSize();
16807 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16808 this.el.select('.fc-day-content div',true).setHeight(34);
16813 showPrevMonth : function(e){
16814 this.update(this.activeDate.add("mo", -1));
16816 showToday : function(e){
16817 this.update(new Date().clearTime());
16820 showNextMonth : function(e){
16821 this.update(this.activeDate.add("mo", 1));
16825 showPrevYear : function(){
16826 this.update(this.activeDate.add("y", -1));
16830 showNextYear : function(){
16831 this.update(this.activeDate.add("y", 1));
16836 update : function(date)
16838 var vd = this.activeDate;
16839 this.activeDate = date;
16840 // if(vd && this.el){
16841 // var t = date.getTime();
16842 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16843 // Roo.log('using add remove');
16845 // this.fireEvent('monthchange', this, date);
16847 // this.cells.removeClass("fc-state-highlight");
16848 // this.cells.each(function(c){
16849 // if(c.dateValue == t){
16850 // c.addClass("fc-state-highlight");
16851 // setTimeout(function(){
16852 // try{c.dom.firstChild.focus();}catch(e){}
16862 var days = date.getDaysInMonth();
16864 var firstOfMonth = date.getFirstDateOfMonth();
16865 var startingPos = firstOfMonth.getDay()-this.startDay;
16867 if(startingPos < this.startDay){
16871 var pm = date.add(Date.MONTH, -1);
16872 var prevStart = pm.getDaysInMonth()-startingPos;
16874 this.cells = this.el.select('.fc-day',true);
16875 this.textNodes = this.el.query('.fc-day-number');
16876 this.cells.addClassOnOver('fc-state-hover');
16878 var cells = this.cells.elements;
16879 var textEls = this.textNodes;
16881 Roo.each(cells, function(cell){
16882 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16885 days += startingPos;
16887 // convert everything to numbers so it's fast
16888 var day = 86400000;
16889 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16892 //Roo.log(prevStart);
16894 var today = new Date().clearTime().getTime();
16895 var sel = date.clearTime().getTime();
16896 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16897 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16898 var ddMatch = this.disabledDatesRE;
16899 var ddText = this.disabledDatesText;
16900 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16901 var ddaysText = this.disabledDaysText;
16902 var format = this.format;
16904 var setCellClass = function(cal, cell){
16908 //Roo.log('set Cell Class');
16910 var t = d.getTime();
16914 cell.dateValue = t;
16916 cell.className += " fc-today";
16917 cell.className += " fc-state-highlight";
16918 cell.title = cal.todayText;
16921 // disable highlight in other month..
16922 //cell.className += " fc-state-highlight";
16927 cell.className = " fc-state-disabled";
16928 cell.title = cal.minText;
16932 cell.className = " fc-state-disabled";
16933 cell.title = cal.maxText;
16937 if(ddays.indexOf(d.getDay()) != -1){
16938 cell.title = ddaysText;
16939 cell.className = " fc-state-disabled";
16942 if(ddMatch && format){
16943 var fvalue = d.dateFormat(format);
16944 if(ddMatch.test(fvalue)){
16945 cell.title = ddText.replace("%0", fvalue);
16946 cell.className = " fc-state-disabled";
16950 if (!cell.initialClassName) {
16951 cell.initialClassName = cell.dom.className;
16954 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16959 for(; i < startingPos; i++) {
16960 textEls[i].innerHTML = (++prevStart);
16961 d.setDate(d.getDate()+1);
16963 cells[i].className = "fc-past fc-other-month";
16964 setCellClass(this, cells[i]);
16969 for(; i < days; i++){
16970 intDay = i - startingPos + 1;
16971 textEls[i].innerHTML = (intDay);
16972 d.setDate(d.getDate()+1);
16974 cells[i].className = ''; // "x-date-active";
16975 setCellClass(this, cells[i]);
16979 for(; i < 42; i++) {
16980 textEls[i].innerHTML = (++extraDays);
16981 d.setDate(d.getDate()+1);
16983 cells[i].className = "fc-future fc-other-month";
16984 setCellClass(this, cells[i]);
16987 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16989 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16991 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16992 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16994 if(totalRows != 6){
16995 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16996 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16999 this.fireEvent('monthchange', this, date);
17003 if(!this.internalRender){
17004 var main = this.el.dom.firstChild;
17005 var w = main.offsetWidth;
17006 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17007 Roo.fly(main).setWidth(w);
17008 this.internalRender = true;
17009 // opera does not respect the auto grow header center column
17010 // then, after it gets a width opera refuses to recalculate
17011 // without a second pass
17012 if(Roo.isOpera && !this.secondPass){
17013 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17014 this.secondPass = true;
17015 this.update.defer(10, this, [date]);
17022 findCell : function(dt) {
17023 dt = dt.clearTime().getTime();
17025 this.cells.each(function(c){
17026 //Roo.log("check " +c.dateValue + '?=' + dt);
17027 if(c.dateValue == dt){
17037 findCells : function(ev) {
17038 var s = ev.start.clone().clearTime().getTime();
17040 var e= ev.end.clone().clearTime().getTime();
17043 this.cells.each(function(c){
17044 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17046 if(c.dateValue > e){
17049 if(c.dateValue < s){
17058 // findBestRow: function(cells)
17062 // for (var i =0 ; i < cells.length;i++) {
17063 // ret = Math.max(cells[i].rows || 0,ret);
17070 addItem : function(ev)
17072 // look for vertical location slot in
17073 var cells = this.findCells(ev);
17075 // ev.row = this.findBestRow(cells);
17077 // work out the location.
17081 for(var i =0; i < cells.length; i++) {
17083 cells[i].row = cells[0].row;
17086 cells[i].row = cells[i].row + 1;
17096 if (crow.start.getY() == cells[i].getY()) {
17098 crow.end = cells[i];
17115 cells[0].events.push(ev);
17117 this.calevents.push(ev);
17120 clearEvents: function() {
17122 if(!this.calevents){
17126 Roo.each(this.cells.elements, function(c){
17132 Roo.each(this.calevents, function(e) {
17133 Roo.each(e.els, function(el) {
17134 el.un('mouseenter' ,this.onEventEnter, this);
17135 el.un('mouseleave' ,this.onEventLeave, this);
17140 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17146 renderEvents: function()
17150 this.cells.each(function(c) {
17159 if(c.row != c.events.length){
17160 r = 4 - (4 - (c.row - c.events.length));
17163 c.events = ev.slice(0, r);
17164 c.more = ev.slice(r);
17166 if(c.more.length && c.more.length == 1){
17167 c.events.push(c.more.pop());
17170 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17174 this.cells.each(function(c) {
17176 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17179 for (var e = 0; e < c.events.length; e++){
17180 var ev = c.events[e];
17181 var rows = ev.rows;
17183 for(var i = 0; i < rows.length; i++) {
17185 // how many rows should it span..
17188 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17189 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17191 unselectable : "on",
17194 cls: 'fc-event-inner',
17198 // cls: 'fc-event-time',
17199 // html : cells.length > 1 ? '' : ev.time
17203 cls: 'fc-event-title',
17204 html : String.format('{0}', ev.title)
17211 cls: 'ui-resizable-handle ui-resizable-e',
17212 html : '  '
17219 cfg.cls += ' fc-event-start';
17221 if ((i+1) == rows.length) {
17222 cfg.cls += ' fc-event-end';
17225 var ctr = _this.el.select('.fc-event-container',true).first();
17226 var cg = ctr.createChild(cfg);
17228 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17229 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17231 var r = (c.more.length) ? 1 : 0;
17232 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17233 cg.setWidth(ebox.right - sbox.x -2);
17235 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17236 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17237 cg.on('click', _this.onEventClick, _this, ev);
17248 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17249 style : 'position: absolute',
17250 unselectable : "on",
17253 cls: 'fc-event-inner',
17257 cls: 'fc-event-title',
17265 cls: 'ui-resizable-handle ui-resizable-e',
17266 html : '  '
17272 var ctr = _this.el.select('.fc-event-container',true).first();
17273 var cg = ctr.createChild(cfg);
17275 var sbox = c.select('.fc-day-content',true).first().getBox();
17276 var ebox = c.select('.fc-day-content',true).first().getBox();
17278 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17279 cg.setWidth(ebox.right - sbox.x -2);
17281 cg.on('click', _this.onMoreEventClick, _this, c.more);
17291 onEventEnter: function (e, el,event,d) {
17292 this.fireEvent('evententer', this, el, event);
17295 onEventLeave: function (e, el,event,d) {
17296 this.fireEvent('eventleave', this, el, event);
17299 onEventClick: function (e, el,event,d) {
17300 this.fireEvent('eventclick', this, el, event);
17303 onMonthChange: function () {
17307 onMoreEventClick: function(e, el, more)
17311 this.calpopover.placement = 'right';
17312 this.calpopover.setTitle('More');
17314 this.calpopover.setContent('');
17316 var ctr = this.calpopover.el.select('.popover-content', true).first();
17318 Roo.each(more, function(m){
17320 cls : 'fc-event-hori fc-event-draggable',
17323 var cg = ctr.createChild(cfg);
17325 cg.on('click', _this.onEventClick, _this, m);
17328 this.calpopover.show(el);
17333 onLoad: function ()
17335 this.calevents = [];
17338 if(this.store.getCount() > 0){
17339 this.store.data.each(function(d){
17342 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17343 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17344 time : d.data.start_time,
17345 title : d.data.title,
17346 description : d.data.description,
17347 venue : d.data.venue
17352 this.renderEvents();
17354 if(this.calevents.length && this.loadMask){
17355 this.maskEl.hide();
17359 onBeforeLoad: function()
17361 this.clearEvents();
17363 this.maskEl.show();
17377 * @class Roo.bootstrap.Popover
17378 * @extends Roo.bootstrap.Component
17379 * Bootstrap Popover class
17380 * @cfg {String} html contents of the popover (or false to use children..)
17381 * @cfg {String} title of popover (or false to hide)
17382 * @cfg {String} placement how it is placed
17383 * @cfg {String} trigger click || hover (or false to trigger manually)
17384 * @cfg {String} over what (parent or false to trigger manually.)
17385 * @cfg {Number} delay - delay before showing
17388 * Create a new Popover
17389 * @param {Object} config The config object
17392 Roo.bootstrap.Popover = function(config){
17393 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17399 * After the popover show
17401 * @param {Roo.bootstrap.Popover} this
17406 * After the popover hide
17408 * @param {Roo.bootstrap.Popover} this
17414 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17416 title: 'Fill in a title',
17419 placement : 'right',
17420 trigger : 'hover', // hover
17426 can_build_overlaid : false,
17428 getChildContainer : function()
17430 return this.el.select('.popover-content',true).first();
17433 getAutoCreate : function(){
17436 cls : 'popover roo-dynamic',
17437 style: 'display:block',
17443 cls : 'popover-inner',
17447 cls: 'popover-title',
17451 cls : 'popover-content',
17462 setTitle: function(str)
17465 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17467 setContent: function(str)
17470 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17472 // as it get's added to the bottom of the page.
17473 onRender : function(ct, position)
17475 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17477 var cfg = Roo.apply({}, this.getAutoCreate());
17481 cfg.cls += ' ' + this.cls;
17484 cfg.style = this.style;
17486 //Roo.log("adding to ");
17487 this.el = Roo.get(document.body).createChild(cfg, position);
17488 // Roo.log(this.el);
17493 initEvents : function()
17495 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17496 this.el.enableDisplayMode('block');
17498 if (this.over === false) {
17501 if (this.triggers === false) {
17504 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17505 var triggers = this.trigger ? this.trigger.split(' ') : [];
17506 Roo.each(triggers, function(trigger) {
17508 if (trigger == 'click') {
17509 on_el.on('click', this.toggle, this);
17510 } else if (trigger != 'manual') {
17511 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17512 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17514 on_el.on(eventIn ,this.enter, this);
17515 on_el.on(eventOut, this.leave, this);
17526 toggle : function () {
17527 this.hoverState == 'in' ? this.leave() : this.enter();
17530 enter : function () {
17532 clearTimeout(this.timeout);
17534 this.hoverState = 'in';
17536 if (!this.delay || !this.delay.show) {
17541 this.timeout = setTimeout(function () {
17542 if (_t.hoverState == 'in') {
17545 }, this.delay.show)
17548 leave : function() {
17549 clearTimeout(this.timeout);
17551 this.hoverState = 'out';
17553 if (!this.delay || !this.delay.hide) {
17558 this.timeout = setTimeout(function () {
17559 if (_t.hoverState == 'out') {
17562 }, this.delay.hide)
17565 show : function (on_el)
17568 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17572 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17573 if (this.html !== false) {
17574 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17576 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17577 if (!this.title.length) {
17578 this.el.select('.popover-title',true).hide();
17581 var placement = typeof this.placement == 'function' ?
17582 this.placement.call(this, this.el, on_el) :
17585 var autoToken = /\s?auto?\s?/i;
17586 var autoPlace = autoToken.test(placement);
17588 placement = placement.replace(autoToken, '') || 'top';
17592 //this.el.setXY([0,0]);
17594 this.el.dom.style.display='block';
17595 this.el.addClass(placement);
17597 //this.el.appendTo(on_el);
17599 var p = this.getPosition();
17600 var box = this.el.getBox();
17605 var align = Roo.bootstrap.Popover.alignment[placement];
17608 this.el.alignTo(on_el, align[0],align[1]);
17609 //var arrow = this.el.select('.arrow',true).first();
17610 //arrow.set(align[2],
17612 this.el.addClass('in');
17615 if (this.el.hasClass('fade')) {
17619 this.hoverState = 'in';
17621 this.fireEvent('show', this);
17626 this.el.setXY([0,0]);
17627 this.el.removeClass('in');
17629 this.hoverState = null;
17631 this.fireEvent('hide', this);
17636 Roo.bootstrap.Popover.alignment = {
17637 'left' : ['r-l', [-10,0], 'right'],
17638 'right' : ['l-r', [10,0], 'left'],
17639 'bottom' : ['t-b', [0,10], 'top'],
17640 'top' : [ 'b-t', [0,-10], 'bottom']
17651 * @class Roo.bootstrap.Progress
17652 * @extends Roo.bootstrap.Component
17653 * Bootstrap Progress class
17654 * @cfg {Boolean} striped striped of the progress bar
17655 * @cfg {Boolean} active animated of the progress bar
17659 * Create a new Progress
17660 * @param {Object} config The config object
17663 Roo.bootstrap.Progress = function(config){
17664 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17667 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17672 getAutoCreate : function(){
17680 cfg.cls += ' progress-striped';
17684 cfg.cls += ' active';
17703 * @class Roo.bootstrap.ProgressBar
17704 * @extends Roo.bootstrap.Component
17705 * Bootstrap ProgressBar class
17706 * @cfg {Number} aria_valuenow aria-value now
17707 * @cfg {Number} aria_valuemin aria-value min
17708 * @cfg {Number} aria_valuemax aria-value max
17709 * @cfg {String} label label for the progress bar
17710 * @cfg {String} panel (success | info | warning | danger )
17711 * @cfg {String} role role of the progress bar
17712 * @cfg {String} sr_only text
17716 * Create a new ProgressBar
17717 * @param {Object} config The config object
17720 Roo.bootstrap.ProgressBar = function(config){
17721 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17724 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17728 aria_valuemax : 100,
17734 getAutoCreate : function()
17739 cls: 'progress-bar',
17740 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17752 cfg.role = this.role;
17755 if(this.aria_valuenow){
17756 cfg['aria-valuenow'] = this.aria_valuenow;
17759 if(this.aria_valuemin){
17760 cfg['aria-valuemin'] = this.aria_valuemin;
17763 if(this.aria_valuemax){
17764 cfg['aria-valuemax'] = this.aria_valuemax;
17767 if(this.label && !this.sr_only){
17768 cfg.html = this.label;
17772 cfg.cls += ' progress-bar-' + this.panel;
17778 update : function(aria_valuenow)
17780 this.aria_valuenow = aria_valuenow;
17782 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17797 * @class Roo.bootstrap.TabGroup
17798 * @extends Roo.bootstrap.Column
17799 * Bootstrap Column class
17800 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17801 * @cfg {Boolean} carousel true to make the group behave like a carousel
17802 * @cfg {Boolean} bullets show bullets for the panels
17803 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17804 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17805 * @cfg {Boolean} showarrow (true|false) show arrow default true
17808 * Create a new TabGroup
17809 * @param {Object} config The config object
17812 Roo.bootstrap.TabGroup = function(config){
17813 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17815 this.navId = Roo.id();
17818 Roo.bootstrap.TabGroup.register(this);
17822 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17825 transition : false,
17830 slideOnTouch : false,
17833 getAutoCreate : function()
17835 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17837 cfg.cls += ' tab-content';
17839 if (this.carousel) {
17840 cfg.cls += ' carousel slide';
17843 cls : 'carousel-inner',
17847 if(this.bullets && !Roo.isTouch){
17850 cls : 'carousel-bullets',
17854 if(this.bullets_cls){
17855 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17862 cfg.cn[0].cn.push(bullets);
17865 if(this.showarrow){
17866 cfg.cn[0].cn.push({
17868 class : 'carousel-arrow',
17872 class : 'carousel-prev',
17876 class : 'fa fa-chevron-left'
17882 class : 'carousel-next',
17886 class : 'fa fa-chevron-right'
17899 initEvents: function()
17901 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17902 // this.el.on("touchstart", this.onTouchStart, this);
17905 if(this.autoslide){
17908 this.slideFn = window.setInterval(function() {
17909 _this.showPanelNext();
17913 if(this.showarrow){
17914 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17915 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17921 // onTouchStart : function(e, el, o)
17923 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17927 // this.showPanelNext();
17931 getChildContainer : function()
17933 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17937 * register a Navigation item
17938 * @param {Roo.bootstrap.NavItem} the navitem to add
17940 register : function(item)
17942 this.tabs.push( item);
17943 item.navId = this.navId; // not really needed..
17948 getActivePanel : function()
17951 Roo.each(this.tabs, function(t) {
17961 getPanelByName : function(n)
17964 Roo.each(this.tabs, function(t) {
17965 if (t.tabId == n) {
17973 indexOfPanel : function(p)
17976 Roo.each(this.tabs, function(t,i) {
17977 if (t.tabId == p.tabId) {
17986 * show a specific panel
17987 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17988 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17990 showPanel : function (pan)
17992 if(this.transition || typeof(pan) == 'undefined'){
17993 Roo.log("waiting for the transitionend");
17997 if (typeof(pan) == 'number') {
17998 pan = this.tabs[pan];
18001 if (typeof(pan) == 'string') {
18002 pan = this.getPanelByName(pan);
18005 var cur = this.getActivePanel();
18008 Roo.log('pan or acitve pan is undefined');
18012 if (pan.tabId == this.getActivePanel().tabId) {
18016 if (false === cur.fireEvent('beforedeactivate')) {
18020 if(this.bullets > 0 && !Roo.isTouch){
18021 this.setActiveBullet(this.indexOfPanel(pan));
18024 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18026 this.transition = true;
18027 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18028 var lr = dir == 'next' ? 'left' : 'right';
18029 pan.el.addClass(dir); // or prev
18030 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18031 cur.el.addClass(lr); // or right
18032 pan.el.addClass(lr);
18035 cur.el.on('transitionend', function() {
18036 Roo.log("trans end?");
18038 pan.el.removeClass([lr,dir]);
18039 pan.setActive(true);
18041 cur.el.removeClass([lr]);
18042 cur.setActive(false);
18044 _this.transition = false;
18046 }, this, { single: true } );
18051 cur.setActive(false);
18052 pan.setActive(true);
18057 showPanelNext : function()
18059 var i = this.indexOfPanel(this.getActivePanel());
18061 if (i >= this.tabs.length - 1 && !this.autoslide) {
18065 if (i >= this.tabs.length - 1 && this.autoslide) {
18069 this.showPanel(this.tabs[i+1]);
18072 showPanelPrev : function()
18074 var i = this.indexOfPanel(this.getActivePanel());
18076 if (i < 1 && !this.autoslide) {
18080 if (i < 1 && this.autoslide) {
18081 i = this.tabs.length;
18084 this.showPanel(this.tabs[i-1]);
18088 addBullet: function()
18090 if(!this.bullets || Roo.isTouch){
18093 var ctr = this.el.select('.carousel-bullets',true).first();
18094 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18095 var bullet = ctr.createChild({
18096 cls : 'bullet bullet-' + i
18097 },ctr.dom.lastChild);
18102 bullet.on('click', (function(e, el, o, ii, t){
18104 e.preventDefault();
18106 this.showPanel(ii);
18108 if(this.autoslide && this.slideFn){
18109 clearInterval(this.slideFn);
18110 this.slideFn = window.setInterval(function() {
18111 _this.showPanelNext();
18115 }).createDelegate(this, [i, bullet], true));
18120 setActiveBullet : function(i)
18126 Roo.each(this.el.select('.bullet', true).elements, function(el){
18127 el.removeClass('selected');
18130 var bullet = this.el.select('.bullet-' + i, true).first();
18136 bullet.addClass('selected');
18147 Roo.apply(Roo.bootstrap.TabGroup, {
18151 * register a Navigation Group
18152 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18154 register : function(navgrp)
18156 this.groups[navgrp.navId] = navgrp;
18160 * fetch a Navigation Group based on the navigation ID
18161 * if one does not exist , it will get created.
18162 * @param {string} the navgroup to add
18163 * @returns {Roo.bootstrap.NavGroup} the navgroup
18165 get: function(navId) {
18166 if (typeof(this.groups[navId]) == 'undefined') {
18167 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18169 return this.groups[navId] ;
18184 * @class Roo.bootstrap.TabPanel
18185 * @extends Roo.bootstrap.Component
18186 * Bootstrap TabPanel class
18187 * @cfg {Boolean} active panel active
18188 * @cfg {String} html panel content
18189 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18190 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18191 * @cfg {String} href click to link..
18195 * Create a new TabPanel
18196 * @param {Object} config The config object
18199 Roo.bootstrap.TabPanel = function(config){
18200 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18204 * Fires when the active status changes
18205 * @param {Roo.bootstrap.TabPanel} this
18206 * @param {Boolean} state the new state
18211 * @event beforedeactivate
18212 * Fires before a tab is de-activated - can be used to do validation on a form.
18213 * @param {Roo.bootstrap.TabPanel} this
18214 * @return {Boolean} false if there is an error
18217 'beforedeactivate': true
18220 this.tabId = this.tabId || Roo.id();
18224 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18232 getAutoCreate : function(){
18235 // item is needed for carousel - not sure if it has any effect otherwise
18236 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18237 html: this.html || ''
18241 cfg.cls += ' active';
18245 cfg.tabId = this.tabId;
18252 initEvents: function()
18254 var p = this.parent();
18256 this.navId = this.navId || p.navId;
18258 if (typeof(this.navId) != 'undefined') {
18259 // not really needed.. but just in case.. parent should be a NavGroup.
18260 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18264 var i = tg.tabs.length - 1;
18266 if(this.active && tg.bullets > 0 && i < tg.bullets){
18267 tg.setActiveBullet(i);
18271 this.el.on('click', this.onClick, this);
18274 this.el.on("touchstart", this.onTouchStart, this);
18275 this.el.on("touchmove", this.onTouchMove, this);
18276 this.el.on("touchend", this.onTouchEnd, this);
18281 onRender : function(ct, position)
18283 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18286 setActive : function(state)
18288 Roo.log("panel - set active " + this.tabId + "=" + state);
18290 this.active = state;
18292 this.el.removeClass('active');
18294 } else if (!this.el.hasClass('active')) {
18295 this.el.addClass('active');
18298 this.fireEvent('changed', this, state);
18301 onClick : function(e)
18303 e.preventDefault();
18305 if(!this.href.length){
18309 window.location.href = this.href;
18318 onTouchStart : function(e)
18320 this.swiping = false;
18322 this.startX = e.browserEvent.touches[0].clientX;
18323 this.startY = e.browserEvent.touches[0].clientY;
18326 onTouchMove : function(e)
18328 this.swiping = true;
18330 this.endX = e.browserEvent.touches[0].clientX;
18331 this.endY = e.browserEvent.touches[0].clientY;
18334 onTouchEnd : function(e)
18341 var tabGroup = this.parent();
18343 if(this.endX > this.startX){ // swiping right
18344 tabGroup.showPanelPrev();
18348 if(this.startX > this.endX){ // swiping left
18349 tabGroup.showPanelNext();
18368 * @class Roo.bootstrap.DateField
18369 * @extends Roo.bootstrap.Input
18370 * Bootstrap DateField class
18371 * @cfg {Number} weekStart default 0
18372 * @cfg {String} viewMode default empty, (months|years)
18373 * @cfg {String} minViewMode default empty, (months|years)
18374 * @cfg {Number} startDate default -Infinity
18375 * @cfg {Number} endDate default Infinity
18376 * @cfg {Boolean} todayHighlight default false
18377 * @cfg {Boolean} todayBtn default false
18378 * @cfg {Boolean} calendarWeeks default false
18379 * @cfg {Object} daysOfWeekDisabled default empty
18380 * @cfg {Boolean} singleMode default false (true | false)
18382 * @cfg {Boolean} keyboardNavigation default true
18383 * @cfg {String} language default en
18386 * Create a new DateField
18387 * @param {Object} config The config object
18390 Roo.bootstrap.DateField = function(config){
18391 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18395 * Fires when this field show.
18396 * @param {Roo.bootstrap.DateField} this
18397 * @param {Mixed} date The date value
18402 * Fires when this field hide.
18403 * @param {Roo.bootstrap.DateField} this
18404 * @param {Mixed} date The date value
18409 * Fires when select a date.
18410 * @param {Roo.bootstrap.DateField} this
18411 * @param {Mixed} date The date value
18415 * @event beforeselect
18416 * Fires when before select a date.
18417 * @param {Roo.bootstrap.DateField} this
18418 * @param {Mixed} date The date value
18420 beforeselect : true
18424 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18427 * @cfg {String} format
18428 * The default date format string which can be overriden for localization support. The format must be
18429 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18433 * @cfg {String} altFormats
18434 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18435 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18437 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18445 todayHighlight : false,
18451 keyboardNavigation: true,
18453 calendarWeeks: false,
18455 startDate: -Infinity,
18459 daysOfWeekDisabled: [],
18463 singleMode : false,
18465 UTCDate: function()
18467 return new Date(Date.UTC.apply(Date, arguments));
18470 UTCToday: function()
18472 var today = new Date();
18473 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18476 getDate: function() {
18477 var d = this.getUTCDate();
18478 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18481 getUTCDate: function() {
18485 setDate: function(d) {
18486 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18489 setUTCDate: function(d) {
18491 this.setValue(this.formatDate(this.date));
18494 onRender: function(ct, position)
18497 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18499 this.language = this.language || 'en';
18500 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18501 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18503 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18504 this.format = this.format || 'm/d/y';
18505 this.isInline = false;
18506 this.isInput = true;
18507 this.component = this.el.select('.add-on', true).first() || false;
18508 this.component = (this.component && this.component.length === 0) ? false : this.component;
18509 this.hasInput = this.component && this.inputEl().length;
18511 if (typeof(this.minViewMode === 'string')) {
18512 switch (this.minViewMode) {
18514 this.minViewMode = 1;
18517 this.minViewMode = 2;
18520 this.minViewMode = 0;
18525 if (typeof(this.viewMode === 'string')) {
18526 switch (this.viewMode) {
18539 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18541 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18543 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18545 this.picker().on('mousedown', this.onMousedown, this);
18546 this.picker().on('click', this.onClick, this);
18548 this.picker().addClass('datepicker-dropdown');
18550 this.startViewMode = this.viewMode;
18552 if(this.singleMode){
18553 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18554 v.setVisibilityMode(Roo.Element.DISPLAY);
18558 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18559 v.setStyle('width', '189px');
18563 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18564 if(!this.calendarWeeks){
18569 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18570 v.attr('colspan', function(i, val){
18571 return parseInt(val) + 1;
18576 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18578 this.setStartDate(this.startDate);
18579 this.setEndDate(this.endDate);
18581 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18588 if(this.isInline) {
18593 picker : function()
18595 return this.pickerEl;
18596 // return this.el.select('.datepicker', true).first();
18599 fillDow: function()
18601 var dowCnt = this.weekStart;
18610 if(this.calendarWeeks){
18618 while (dowCnt < this.weekStart + 7) {
18622 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18626 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18629 fillMonths: function()
18632 var months = this.picker().select('>.datepicker-months td', true).first();
18634 months.dom.innerHTML = '';
18640 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18643 months.createChild(month);
18650 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;
18652 if (this.date < this.startDate) {
18653 this.viewDate = new Date(this.startDate);
18654 } else if (this.date > this.endDate) {
18655 this.viewDate = new Date(this.endDate);
18657 this.viewDate = new Date(this.date);
18665 var d = new Date(this.viewDate),
18666 year = d.getUTCFullYear(),
18667 month = d.getUTCMonth(),
18668 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18669 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18670 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18671 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18672 currentDate = this.date && this.date.valueOf(),
18673 today = this.UTCToday();
18675 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18677 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18679 // this.picker.select('>tfoot th.today').
18680 // .text(dates[this.language].today)
18681 // .toggle(this.todayBtn !== false);
18683 this.updateNavArrows();
18686 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18688 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18690 prevMonth.setUTCDate(day);
18692 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18694 var nextMonth = new Date(prevMonth);
18696 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18698 nextMonth = nextMonth.valueOf();
18700 var fillMonths = false;
18702 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18704 while(prevMonth.valueOf() <= nextMonth) {
18707 if (prevMonth.getUTCDay() === this.weekStart) {
18709 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18717 if(this.calendarWeeks){
18718 // ISO 8601: First week contains first thursday.
18719 // ISO also states week starts on Monday, but we can be more abstract here.
18721 // Start of current week: based on weekstart/current date
18722 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18723 // Thursday of this week
18724 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18725 // First Thursday of year, year from thursday
18726 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18727 // Calendar week: ms between thursdays, div ms per day, div 7 days
18728 calWeek = (th - yth) / 864e5 / 7 + 1;
18730 fillMonths.cn.push({
18738 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18740 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18743 if (this.todayHighlight &&
18744 prevMonth.getUTCFullYear() == today.getFullYear() &&
18745 prevMonth.getUTCMonth() == today.getMonth() &&
18746 prevMonth.getUTCDate() == today.getDate()) {
18747 clsName += ' today';
18750 if (currentDate && prevMonth.valueOf() === currentDate) {
18751 clsName += ' active';
18754 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18755 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18756 clsName += ' disabled';
18759 fillMonths.cn.push({
18761 cls: 'day ' + clsName,
18762 html: prevMonth.getDate()
18765 prevMonth.setDate(prevMonth.getDate()+1);
18768 var currentYear = this.date && this.date.getUTCFullYear();
18769 var currentMonth = this.date && this.date.getUTCMonth();
18771 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18773 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18774 v.removeClass('active');
18776 if(currentYear === year && k === currentMonth){
18777 v.addClass('active');
18780 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18781 v.addClass('disabled');
18787 year = parseInt(year/10, 10) * 10;
18789 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18791 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18794 for (var i = -1; i < 11; i++) {
18795 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18797 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18805 showMode: function(dir)
18808 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18811 Roo.each(this.picker().select('>div',true).elements, function(v){
18812 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18815 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18820 if(this.isInline) {
18824 this.picker().removeClass(['bottom', 'top']);
18826 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18828 * place to the top of element!
18832 this.picker().addClass('top');
18833 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18838 this.picker().addClass('bottom');
18840 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18843 parseDate : function(value)
18845 if(!value || value instanceof Date){
18848 var v = Date.parseDate(value, this.format);
18849 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18850 v = Date.parseDate(value, 'Y-m-d');
18852 if(!v && this.altFormats){
18853 if(!this.altFormatsArray){
18854 this.altFormatsArray = this.altFormats.split("|");
18856 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18857 v = Date.parseDate(value, this.altFormatsArray[i]);
18863 formatDate : function(date, fmt)
18865 return (!date || !(date instanceof Date)) ?
18866 date : date.dateFormat(fmt || this.format);
18869 onFocus : function()
18871 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18875 onBlur : function()
18877 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18879 var d = this.inputEl().getValue();
18886 showPopup : function()
18888 this.picker().show();
18892 this.fireEvent('showpopup', this, this.date);
18895 hidePopup : function()
18897 if(this.isInline) {
18900 this.picker().hide();
18901 this.viewMode = this.startViewMode;
18904 this.fireEvent('hidepopup', this, this.date);
18908 onMousedown: function(e)
18910 e.stopPropagation();
18911 e.preventDefault();
18916 Roo.bootstrap.DateField.superclass.keyup.call(this);
18920 setValue: function(v)
18922 if(this.fireEvent('beforeselect', this, v) !== false){
18923 var d = new Date(this.parseDate(v) ).clearTime();
18925 if(isNaN(d.getTime())){
18926 this.date = this.viewDate = '';
18927 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18931 v = this.formatDate(d);
18933 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18935 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18939 this.fireEvent('select', this, this.date);
18943 getValue: function()
18945 return this.formatDate(this.date);
18948 fireKey: function(e)
18950 if (!this.picker().isVisible()){
18951 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18957 var dateChanged = false,
18959 newDate, newViewDate;
18964 e.preventDefault();
18968 if (!this.keyboardNavigation) {
18971 dir = e.keyCode == 37 ? -1 : 1;
18974 newDate = this.moveYear(this.date, dir);
18975 newViewDate = this.moveYear(this.viewDate, dir);
18976 } else if (e.shiftKey){
18977 newDate = this.moveMonth(this.date, dir);
18978 newViewDate = this.moveMonth(this.viewDate, dir);
18980 newDate = new Date(this.date);
18981 newDate.setUTCDate(this.date.getUTCDate() + dir);
18982 newViewDate = new Date(this.viewDate);
18983 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18985 if (this.dateWithinRange(newDate)){
18986 this.date = newDate;
18987 this.viewDate = newViewDate;
18988 this.setValue(this.formatDate(this.date));
18990 e.preventDefault();
18991 dateChanged = true;
18996 if (!this.keyboardNavigation) {
18999 dir = e.keyCode == 38 ? -1 : 1;
19001 newDate = this.moveYear(this.date, dir);
19002 newViewDate = this.moveYear(this.viewDate, dir);
19003 } else if (e.shiftKey){
19004 newDate = this.moveMonth(this.date, dir);
19005 newViewDate = this.moveMonth(this.viewDate, dir);
19007 newDate = new Date(this.date);
19008 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19009 newViewDate = new Date(this.viewDate);
19010 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19012 if (this.dateWithinRange(newDate)){
19013 this.date = newDate;
19014 this.viewDate = newViewDate;
19015 this.setValue(this.formatDate(this.date));
19017 e.preventDefault();
19018 dateChanged = true;
19022 this.setValue(this.formatDate(this.date));
19024 e.preventDefault();
19027 this.setValue(this.formatDate(this.date));
19041 onClick: function(e)
19043 e.stopPropagation();
19044 e.preventDefault();
19046 var target = e.getTarget();
19048 if(target.nodeName.toLowerCase() === 'i'){
19049 target = Roo.get(target).dom.parentNode;
19052 var nodeName = target.nodeName;
19053 var className = target.className;
19054 var html = target.innerHTML;
19055 //Roo.log(nodeName);
19057 switch(nodeName.toLowerCase()) {
19059 switch(className) {
19065 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19066 switch(this.viewMode){
19068 this.viewDate = this.moveMonth(this.viewDate, dir);
19072 this.viewDate = this.moveYear(this.viewDate, dir);
19078 var date = new Date();
19079 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19081 this.setValue(this.formatDate(this.date));
19088 if (className.indexOf('disabled') < 0) {
19089 this.viewDate.setUTCDate(1);
19090 if (className.indexOf('month') > -1) {
19091 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19093 var year = parseInt(html, 10) || 0;
19094 this.viewDate.setUTCFullYear(year);
19098 if(this.singleMode){
19099 this.setValue(this.formatDate(this.viewDate));
19110 //Roo.log(className);
19111 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19112 var day = parseInt(html, 10) || 1;
19113 var year = this.viewDate.getUTCFullYear(),
19114 month = this.viewDate.getUTCMonth();
19116 if (className.indexOf('old') > -1) {
19123 } else if (className.indexOf('new') > -1) {
19131 //Roo.log([year,month,day]);
19132 this.date = this.UTCDate(year, month, day,0,0,0,0);
19133 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19135 //Roo.log(this.formatDate(this.date));
19136 this.setValue(this.formatDate(this.date));
19143 setStartDate: function(startDate)
19145 this.startDate = startDate || -Infinity;
19146 if (this.startDate !== -Infinity) {
19147 this.startDate = this.parseDate(this.startDate);
19150 this.updateNavArrows();
19153 setEndDate: function(endDate)
19155 this.endDate = endDate || Infinity;
19156 if (this.endDate !== Infinity) {
19157 this.endDate = this.parseDate(this.endDate);
19160 this.updateNavArrows();
19163 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19165 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19166 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19167 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19169 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19170 return parseInt(d, 10);
19173 this.updateNavArrows();
19176 updateNavArrows: function()
19178 if(this.singleMode){
19182 var d = new Date(this.viewDate),
19183 year = d.getUTCFullYear(),
19184 month = d.getUTCMonth();
19186 Roo.each(this.picker().select('.prev', true).elements, function(v){
19188 switch (this.viewMode) {
19191 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19197 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19204 Roo.each(this.picker().select('.next', true).elements, function(v){
19206 switch (this.viewMode) {
19209 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19215 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19223 moveMonth: function(date, dir)
19228 var new_date = new Date(date.valueOf()),
19229 day = new_date.getUTCDate(),
19230 month = new_date.getUTCMonth(),
19231 mag = Math.abs(dir),
19233 dir = dir > 0 ? 1 : -1;
19236 // If going back one month, make sure month is not current month
19237 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19239 return new_date.getUTCMonth() == month;
19241 // If going forward one month, make sure month is as expected
19242 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19244 return new_date.getUTCMonth() != new_month;
19246 new_month = month + dir;
19247 new_date.setUTCMonth(new_month);
19248 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19249 if (new_month < 0 || new_month > 11) {
19250 new_month = (new_month + 12) % 12;
19253 // For magnitudes >1, move one month at a time...
19254 for (var i=0; i<mag; i++) {
19255 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19256 new_date = this.moveMonth(new_date, dir);
19258 // ...then reset the day, keeping it in the new month
19259 new_month = new_date.getUTCMonth();
19260 new_date.setUTCDate(day);
19262 return new_month != new_date.getUTCMonth();
19265 // Common date-resetting loop -- if date is beyond end of month, make it
19268 new_date.setUTCDate(--day);
19269 new_date.setUTCMonth(new_month);
19274 moveYear: function(date, dir)
19276 return this.moveMonth(date, dir*12);
19279 dateWithinRange: function(date)
19281 return date >= this.startDate && date <= this.endDate;
19287 this.picker().remove();
19290 validateValue : function(value)
19292 if(this.getVisibilityEl().hasClass('hidden')){
19296 if(value.length < 1) {
19297 if(this.allowBlank){
19303 if(value.length < this.minLength){
19306 if(value.length > this.maxLength){
19310 var vt = Roo.form.VTypes;
19311 if(!vt[this.vtype](value, this)){
19315 if(typeof this.validator == "function"){
19316 var msg = this.validator(value);
19322 if(this.regex && !this.regex.test(value)){
19326 if(typeof(this.parseDate(value)) == 'undefined'){
19330 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19334 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19344 this.date = this.viewDate = '';
19346 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19351 Roo.apply(Roo.bootstrap.DateField, {
19362 html: '<i class="fa fa-arrow-left"/>'
19372 html: '<i class="fa fa-arrow-right"/>'
19414 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19415 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19416 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19417 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19418 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19431 navFnc: 'FullYear',
19436 navFnc: 'FullYear',
19441 Roo.apply(Roo.bootstrap.DateField, {
19445 cls: 'datepicker dropdown-menu roo-dynamic',
19449 cls: 'datepicker-days',
19453 cls: 'table-condensed',
19455 Roo.bootstrap.DateField.head,
19459 Roo.bootstrap.DateField.footer
19466 cls: 'datepicker-months',
19470 cls: 'table-condensed',
19472 Roo.bootstrap.DateField.head,
19473 Roo.bootstrap.DateField.content,
19474 Roo.bootstrap.DateField.footer
19481 cls: 'datepicker-years',
19485 cls: 'table-condensed',
19487 Roo.bootstrap.DateField.head,
19488 Roo.bootstrap.DateField.content,
19489 Roo.bootstrap.DateField.footer
19508 * @class Roo.bootstrap.TimeField
19509 * @extends Roo.bootstrap.Input
19510 * Bootstrap DateField class
19514 * Create a new TimeField
19515 * @param {Object} config The config object
19518 Roo.bootstrap.TimeField = function(config){
19519 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19523 * Fires when this field show.
19524 * @param {Roo.bootstrap.DateField} thisthis
19525 * @param {Mixed} date The date value
19530 * Fires when this field hide.
19531 * @param {Roo.bootstrap.DateField} this
19532 * @param {Mixed} date The date value
19537 * Fires when select a date.
19538 * @param {Roo.bootstrap.DateField} this
19539 * @param {Mixed} date The date value
19545 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19548 * @cfg {String} format
19549 * The default time format string which can be overriden for localization support. The format must be
19550 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19554 onRender: function(ct, position)
19557 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19559 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19561 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19563 this.pop = this.picker().select('>.datepicker-time',true).first();
19564 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19566 this.picker().on('mousedown', this.onMousedown, this);
19567 this.picker().on('click', this.onClick, this);
19569 this.picker().addClass('datepicker-dropdown');
19574 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19575 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19576 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19577 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19578 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19579 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19583 fireKey: function(e){
19584 if (!this.picker().isVisible()){
19585 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19591 e.preventDefault();
19599 this.onTogglePeriod();
19602 this.onIncrementMinutes();
19605 this.onDecrementMinutes();
19614 onClick: function(e) {
19615 e.stopPropagation();
19616 e.preventDefault();
19619 picker : function()
19621 return this.el.select('.datepicker', true).first();
19624 fillTime: function()
19626 var time = this.pop.select('tbody', true).first();
19628 time.dom.innerHTML = '';
19643 cls: 'hours-up glyphicon glyphicon-chevron-up'
19663 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19684 cls: 'timepicker-hour',
19699 cls: 'timepicker-minute',
19714 cls: 'btn btn-primary period',
19736 cls: 'hours-down glyphicon glyphicon-chevron-down'
19756 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19774 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19781 var hours = this.time.getHours();
19782 var minutes = this.time.getMinutes();
19795 hours = hours - 12;
19799 hours = '0' + hours;
19803 minutes = '0' + minutes;
19806 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19807 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19808 this.pop.select('button', true).first().dom.innerHTML = period;
19814 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19816 var cls = ['bottom'];
19818 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19825 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19830 this.picker().addClass(cls.join('-'));
19834 Roo.each(cls, function(c){
19836 _this.picker().setTop(_this.inputEl().getHeight());
19840 _this.picker().setTop(0 - _this.picker().getHeight());
19845 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19849 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19856 onFocus : function()
19858 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19862 onBlur : function()
19864 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19870 this.picker().show();
19875 this.fireEvent('show', this, this.date);
19880 this.picker().hide();
19883 this.fireEvent('hide', this, this.date);
19886 setTime : function()
19889 this.setValue(this.time.format(this.format));
19891 this.fireEvent('select', this, this.date);
19896 onMousedown: function(e){
19897 e.stopPropagation();
19898 e.preventDefault();
19901 onIncrementHours: function()
19903 Roo.log('onIncrementHours');
19904 this.time = this.time.add(Date.HOUR, 1);
19909 onDecrementHours: function()
19911 Roo.log('onDecrementHours');
19912 this.time = this.time.add(Date.HOUR, -1);
19916 onIncrementMinutes: function()
19918 Roo.log('onIncrementMinutes');
19919 this.time = this.time.add(Date.MINUTE, 1);
19923 onDecrementMinutes: function()
19925 Roo.log('onDecrementMinutes');
19926 this.time = this.time.add(Date.MINUTE, -1);
19930 onTogglePeriod: function()
19932 Roo.log('onTogglePeriod');
19933 this.time = this.time.add(Date.HOUR, 12);
19940 Roo.apply(Roo.bootstrap.TimeField, {
19970 cls: 'btn btn-info ok',
19982 Roo.apply(Roo.bootstrap.TimeField, {
19986 cls: 'datepicker dropdown-menu',
19990 cls: 'datepicker-time',
19994 cls: 'table-condensed',
19996 Roo.bootstrap.TimeField.content,
19997 Roo.bootstrap.TimeField.footer
20016 * @class Roo.bootstrap.MonthField
20017 * @extends Roo.bootstrap.Input
20018 * Bootstrap MonthField class
20020 * @cfg {String} language default en
20023 * Create a new MonthField
20024 * @param {Object} config The config object
20027 Roo.bootstrap.MonthField = function(config){
20028 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20033 * Fires when this field show.
20034 * @param {Roo.bootstrap.MonthField} this
20035 * @param {Mixed} date The date value
20040 * Fires when this field hide.
20041 * @param {Roo.bootstrap.MonthField} this
20042 * @param {Mixed} date The date value
20047 * Fires when select a date.
20048 * @param {Roo.bootstrap.MonthField} this
20049 * @param {String} oldvalue The old value
20050 * @param {String} newvalue The new value
20056 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20058 onRender: function(ct, position)
20061 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20063 this.language = this.language || 'en';
20064 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20065 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20067 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20068 this.isInline = false;
20069 this.isInput = true;
20070 this.component = this.el.select('.add-on', true).first() || false;
20071 this.component = (this.component && this.component.length === 0) ? false : this.component;
20072 this.hasInput = this.component && this.inputEL().length;
20074 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20076 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20078 this.picker().on('mousedown', this.onMousedown, this);
20079 this.picker().on('click', this.onClick, this);
20081 this.picker().addClass('datepicker-dropdown');
20083 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20084 v.setStyle('width', '189px');
20091 if(this.isInline) {
20097 setValue: function(v, suppressEvent)
20099 var o = this.getValue();
20101 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20105 if(suppressEvent !== true){
20106 this.fireEvent('select', this, o, v);
20111 getValue: function()
20116 onClick: function(e)
20118 e.stopPropagation();
20119 e.preventDefault();
20121 var target = e.getTarget();
20123 if(target.nodeName.toLowerCase() === 'i'){
20124 target = Roo.get(target).dom.parentNode;
20127 var nodeName = target.nodeName;
20128 var className = target.className;
20129 var html = target.innerHTML;
20131 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20135 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20137 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20143 picker : function()
20145 return this.pickerEl;
20148 fillMonths: function()
20151 var months = this.picker().select('>.datepicker-months td', true).first();
20153 months.dom.innerHTML = '';
20159 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20162 months.createChild(month);
20171 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20172 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20175 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20176 e.removeClass('active');
20178 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20179 e.addClass('active');
20186 if(this.isInline) {
20190 this.picker().removeClass(['bottom', 'top']);
20192 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20194 * place to the top of element!
20198 this.picker().addClass('top');
20199 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20204 this.picker().addClass('bottom');
20206 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20209 onFocus : function()
20211 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20215 onBlur : function()
20217 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20219 var d = this.inputEl().getValue();
20228 this.picker().show();
20229 this.picker().select('>.datepicker-months', true).first().show();
20233 this.fireEvent('show', this, this.date);
20238 if(this.isInline) {
20241 this.picker().hide();
20242 this.fireEvent('hide', this, this.date);
20246 onMousedown: function(e)
20248 e.stopPropagation();
20249 e.preventDefault();
20254 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20258 fireKey: function(e)
20260 if (!this.picker().isVisible()){
20261 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20272 e.preventDefault();
20276 dir = e.keyCode == 37 ? -1 : 1;
20278 this.vIndex = this.vIndex + dir;
20280 if(this.vIndex < 0){
20284 if(this.vIndex > 11){
20288 if(isNaN(this.vIndex)){
20292 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20298 dir = e.keyCode == 38 ? -1 : 1;
20300 this.vIndex = this.vIndex + dir * 4;
20302 if(this.vIndex < 0){
20306 if(this.vIndex > 11){
20310 if(isNaN(this.vIndex)){
20314 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20319 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20320 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20324 e.preventDefault();
20327 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20328 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20344 this.picker().remove();
20349 Roo.apply(Roo.bootstrap.MonthField, {
20368 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20369 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20374 Roo.apply(Roo.bootstrap.MonthField, {
20378 cls: 'datepicker dropdown-menu roo-dynamic',
20382 cls: 'datepicker-months',
20386 cls: 'table-condensed',
20388 Roo.bootstrap.DateField.content
20408 * @class Roo.bootstrap.CheckBox
20409 * @extends Roo.bootstrap.Input
20410 * Bootstrap CheckBox class
20412 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20413 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20414 * @cfg {String} boxLabel The text that appears beside the checkbox
20415 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20416 * @cfg {Boolean} checked initnal the element
20417 * @cfg {Boolean} inline inline the element (default false)
20418 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20419 * @cfg {String} tooltip label tooltip
20422 * Create a new CheckBox
20423 * @param {Object} config The config object
20426 Roo.bootstrap.CheckBox = function(config){
20427 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20432 * Fires when the element is checked or unchecked.
20433 * @param {Roo.bootstrap.CheckBox} this This input
20434 * @param {Boolean} checked The new checked value
20439 * Fires when the element is click.
20440 * @param {Roo.bootstrap.CheckBox} this This input
20447 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20449 inputType: 'checkbox',
20458 getAutoCreate : function()
20460 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20466 cfg.cls = 'form-group ' + this.inputType; //input-group
20469 cfg.cls += ' ' + this.inputType + '-inline';
20475 type : this.inputType,
20476 value : this.inputValue,
20477 cls : 'roo-' + this.inputType, //'form-box',
20478 placeholder : this.placeholder || ''
20482 if(this.inputType != 'radio'){
20486 cls : 'roo-hidden-value',
20487 value : this.checked ? this.inputValue : this.valueOff
20492 if (this.weight) { // Validity check?
20493 cfg.cls += " " + this.inputType + "-" + this.weight;
20496 if (this.disabled) {
20497 input.disabled=true;
20501 input.checked = this.checked;
20506 input.name = this.name;
20508 if(this.inputType != 'radio'){
20509 hidden.name = this.name;
20510 input.name = '_hidden_' + this.name;
20515 input.cls += ' input-' + this.size;
20520 ['xs','sm','md','lg'].map(function(size){
20521 if (settings[size]) {
20522 cfg.cls += ' col-' + size + '-' + settings[size];
20526 var inputblock = input;
20528 if (this.before || this.after) {
20531 cls : 'input-group',
20536 inputblock.cn.push({
20538 cls : 'input-group-addon',
20543 inputblock.cn.push(input);
20545 if(this.inputType != 'radio'){
20546 inputblock.cn.push(hidden);
20550 inputblock.cn.push({
20552 cls : 'input-group-addon',
20559 if (align ==='left' && this.fieldLabel.length) {
20560 // Roo.log("left and has label");
20565 cls : 'control-label',
20566 html : this.fieldLabel
20576 if(this.labelWidth > 12){
20577 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20580 if(this.labelWidth < 13 && this.labelmd == 0){
20581 this.labelmd = this.labelWidth;
20584 if(this.labellg > 0){
20585 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20586 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20589 if(this.labelmd > 0){
20590 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20591 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20594 if(this.labelsm > 0){
20595 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20596 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20599 if(this.labelxs > 0){
20600 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20601 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20604 } else if ( this.fieldLabel.length) {
20605 // Roo.log(" label");
20609 tag: this.boxLabel ? 'span' : 'label',
20611 cls: 'control-label box-input-label',
20612 //cls : 'input-group-addon',
20613 html : this.fieldLabel
20622 // Roo.log(" no label && no align");
20623 cfg.cn = [ inputblock ] ;
20629 var boxLabelCfg = {
20631 //'for': id, // box label is handled by onclick - so no for...
20633 html: this.boxLabel
20637 boxLabelCfg.tooltip = this.tooltip;
20640 cfg.cn.push(boxLabelCfg);
20643 if(this.inputType != 'radio'){
20644 cfg.cn.push(hidden);
20652 * return the real input element.
20654 inputEl: function ()
20656 return this.el.select('input.roo-' + this.inputType,true).first();
20658 hiddenEl: function ()
20660 return this.el.select('input.roo-hidden-value',true).first();
20663 labelEl: function()
20665 return this.el.select('label.control-label',true).first();
20667 /* depricated... */
20671 return this.labelEl();
20674 boxLabelEl: function()
20676 return this.el.select('label.box-label',true).first();
20679 initEvents : function()
20681 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20683 this.inputEl().on('click', this.onClick, this);
20685 if (this.boxLabel) {
20686 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20689 this.startValue = this.getValue();
20692 Roo.bootstrap.CheckBox.register(this);
20696 onClick : function(e)
20698 if(this.fireEvent('click', this, e) !== false){
20699 this.setChecked(!this.checked);
20704 setChecked : function(state,suppressEvent)
20706 this.startValue = this.getValue();
20708 if(this.inputType == 'radio'){
20710 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20711 e.dom.checked = false;
20714 this.inputEl().dom.checked = true;
20716 this.inputEl().dom.value = this.inputValue;
20718 if(suppressEvent !== true){
20719 this.fireEvent('check', this, true);
20727 this.checked = state;
20729 this.inputEl().dom.checked = state;
20732 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20734 if(suppressEvent !== true){
20735 this.fireEvent('check', this, state);
20741 getValue : function()
20743 if(this.inputType == 'radio'){
20744 return this.getGroupValue();
20747 return this.hiddenEl().dom.value;
20751 getGroupValue : function()
20753 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20757 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20760 setValue : function(v,suppressEvent)
20762 if(this.inputType == 'radio'){
20763 this.setGroupValue(v, suppressEvent);
20767 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20772 setGroupValue : function(v, suppressEvent)
20774 this.startValue = this.getValue();
20776 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20777 e.dom.checked = false;
20779 if(e.dom.value == v){
20780 e.dom.checked = true;
20784 if(suppressEvent !== true){
20785 this.fireEvent('check', this, true);
20793 validate : function()
20795 if(this.getVisibilityEl().hasClass('hidden')){
20801 (this.inputType == 'radio' && this.validateRadio()) ||
20802 (this.inputType == 'checkbox' && this.validateCheckbox())
20808 this.markInvalid();
20812 validateRadio : function()
20814 if(this.getVisibilityEl().hasClass('hidden')){
20818 if(this.allowBlank){
20824 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20825 if(!e.dom.checked){
20837 validateCheckbox : function()
20840 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20841 //return (this.getValue() == this.inputValue) ? true : false;
20844 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20852 for(var i in group){
20853 if(group[i].el.isVisible(true)){
20861 for(var i in group){
20866 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20873 * Mark this field as valid
20875 markValid : function()
20879 this.fireEvent('valid', this);
20881 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20884 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20891 if(this.inputType == 'radio'){
20892 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20893 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20894 e.findParent('.form-group', false, true).addClass(_this.validClass);
20901 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20902 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20906 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20912 for(var i in group){
20913 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20914 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20919 * Mark this field as invalid
20920 * @param {String} msg The validation message
20922 markInvalid : function(msg)
20924 if(this.allowBlank){
20930 this.fireEvent('invalid', this, msg);
20932 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20935 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20939 label.markInvalid();
20942 if(this.inputType == 'radio'){
20943 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20945 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20952 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20953 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20957 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20963 for(var i in group){
20964 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20965 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20970 clearInvalid : function()
20972 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20974 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20976 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20978 if (label && label.iconEl) {
20979 label.iconEl.removeClass(label.validClass);
20980 label.iconEl.removeClass(label.invalidClass);
20984 disable : function()
20986 if(this.inputType != 'radio'){
20987 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20994 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20995 _this.getActionEl().addClass(this.disabledClass);
20996 e.dom.disabled = true;
21000 this.disabled = true;
21001 this.fireEvent("disable", this);
21005 enable : function()
21007 if(this.inputType != 'radio'){
21008 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21015 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21016 _this.getActionEl().removeClass(this.disabledClass);
21017 e.dom.disabled = false;
21021 this.disabled = false;
21022 this.fireEvent("enable", this);
21026 setBoxLabel : function(v)
21031 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21037 Roo.apply(Roo.bootstrap.CheckBox, {
21042 * register a CheckBox Group
21043 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21045 register : function(checkbox)
21047 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21048 this.groups[checkbox.groupId] = {};
21051 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21055 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21059 * fetch a CheckBox Group based on the group ID
21060 * @param {string} the group ID
21061 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21063 get: function(groupId) {
21064 if (typeof(this.groups[groupId]) == 'undefined') {
21068 return this.groups[groupId] ;
21081 * @class Roo.bootstrap.Radio
21082 * @extends Roo.bootstrap.Component
21083 * Bootstrap Radio class
21084 * @cfg {String} boxLabel - the label associated
21085 * @cfg {String} value - the value of radio
21088 * Create a new Radio
21089 * @param {Object} config The config object
21091 Roo.bootstrap.Radio = function(config){
21092 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21096 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21102 getAutoCreate : function()
21106 cls : 'form-group radio',
21111 html : this.boxLabel
21119 initEvents : function()
21121 this.parent().register(this);
21123 this.el.on('click', this.onClick, this);
21127 onClick : function(e)
21129 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21130 this.setChecked(true);
21134 setChecked : function(state, suppressEvent)
21136 this.parent().setValue(this.value, suppressEvent);
21140 setBoxLabel : function(v)
21145 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21160 * @class Roo.bootstrap.SecurePass
21161 * @extends Roo.bootstrap.Input
21162 * Bootstrap SecurePass class
21166 * Create a new SecurePass
21167 * @param {Object} config The config object
21170 Roo.bootstrap.SecurePass = function (config) {
21171 // these go here, so the translation tool can replace them..
21173 PwdEmpty: "Please type a password, and then retype it to confirm.",
21174 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21175 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21176 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21177 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21178 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21179 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21180 TooWeak: "Your password is Too Weak."
21182 this.meterLabel = "Password strength:";
21183 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21184 this.meterClass = [
21185 "roo-password-meter-tooweak",
21186 "roo-password-meter-weak",
21187 "roo-password-meter-medium",
21188 "roo-password-meter-strong",
21189 "roo-password-meter-grey"
21194 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21197 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21199 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21201 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21202 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21203 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21204 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21205 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21206 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21207 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21217 * @cfg {String/Object} Label for the strength meter (defaults to
21218 * 'Password strength:')
21223 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21224 * ['Weak', 'Medium', 'Strong'])
21227 pwdStrengths: false,
21240 initEvents: function ()
21242 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21244 if (this.el.is('input[type=password]') && Roo.isSafari) {
21245 this.el.on('keydown', this.SafariOnKeyDown, this);
21248 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21251 onRender: function (ct, position)
21253 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21254 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21255 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21257 this.trigger.createChild({
21262 cls: 'roo-password-meter-grey col-xs-12',
21265 //width: this.meterWidth + 'px'
21269 cls: 'roo-password-meter-text'
21275 if (this.hideTrigger) {
21276 this.trigger.setDisplayed(false);
21278 this.setSize(this.width || '', this.height || '');
21281 onDestroy: function ()
21283 if (this.trigger) {
21284 this.trigger.removeAllListeners();
21285 this.trigger.remove();
21288 this.wrap.remove();
21290 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21293 checkStrength: function ()
21295 var pwd = this.inputEl().getValue();
21296 if (pwd == this._lastPwd) {
21301 if (this.ClientSideStrongPassword(pwd)) {
21303 } else if (this.ClientSideMediumPassword(pwd)) {
21305 } else if (this.ClientSideWeakPassword(pwd)) {
21311 Roo.log('strength1: ' + strength);
21313 //var pm = this.trigger.child('div/div/div').dom;
21314 var pm = this.trigger.child('div/div');
21315 pm.removeClass(this.meterClass);
21316 pm.addClass(this.meterClass[strength]);
21319 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21321 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21323 this._lastPwd = pwd;
21327 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21329 this._lastPwd = '';
21331 var pm = this.trigger.child('div/div');
21332 pm.removeClass(this.meterClass);
21333 pm.addClass('roo-password-meter-grey');
21336 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21339 this.inputEl().dom.type='password';
21342 validateValue: function (value)
21345 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21348 if (value.length == 0) {
21349 if (this.allowBlank) {
21350 this.clearInvalid();
21354 this.markInvalid(this.errors.PwdEmpty);
21355 this.errorMsg = this.errors.PwdEmpty;
21363 if ('[\x21-\x7e]*'.match(value)) {
21364 this.markInvalid(this.errors.PwdBadChar);
21365 this.errorMsg = this.errors.PwdBadChar;
21368 if (value.length < 6) {
21369 this.markInvalid(this.errors.PwdShort);
21370 this.errorMsg = this.errors.PwdShort;
21373 if (value.length > 16) {
21374 this.markInvalid(this.errors.PwdLong);
21375 this.errorMsg = this.errors.PwdLong;
21379 if (this.ClientSideStrongPassword(value)) {
21381 } else if (this.ClientSideMediumPassword(value)) {
21383 } else if (this.ClientSideWeakPassword(value)) {
21390 if (strength < 2) {
21391 //this.markInvalid(this.errors.TooWeak);
21392 this.errorMsg = this.errors.TooWeak;
21397 console.log('strength2: ' + strength);
21399 //var pm = this.trigger.child('div/div/div').dom;
21401 var pm = this.trigger.child('div/div');
21402 pm.removeClass(this.meterClass);
21403 pm.addClass(this.meterClass[strength]);
21405 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21407 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21409 this.errorMsg = '';
21413 CharacterSetChecks: function (type)
21416 this.fResult = false;
21419 isctype: function (character, type)
21422 case this.kCapitalLetter:
21423 if (character >= 'A' && character <= 'Z') {
21428 case this.kSmallLetter:
21429 if (character >= 'a' && character <= 'z') {
21435 if (character >= '0' && character <= '9') {
21440 case this.kPunctuation:
21441 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21452 IsLongEnough: function (pwd, size)
21454 return !(pwd == null || isNaN(size) || pwd.length < size);
21457 SpansEnoughCharacterSets: function (word, nb)
21459 if (!this.IsLongEnough(word, nb))
21464 var characterSetChecks = new Array(
21465 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21466 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21469 for (var index = 0; index < word.length; ++index) {
21470 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21471 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21472 characterSetChecks[nCharSet].fResult = true;
21479 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21480 if (characterSetChecks[nCharSet].fResult) {
21485 if (nCharSets < nb) {
21491 ClientSideStrongPassword: function (pwd)
21493 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21496 ClientSideMediumPassword: function (pwd)
21498 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21501 ClientSideWeakPassword: function (pwd)
21503 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21506 })//<script type="text/javascript">
21509 * Based Ext JS Library 1.1.1
21510 * Copyright(c) 2006-2007, Ext JS, LLC.
21516 * @class Roo.HtmlEditorCore
21517 * @extends Roo.Component
21518 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21520 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21523 Roo.HtmlEditorCore = function(config){
21526 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21531 * @event initialize
21532 * Fires when the editor is fully initialized (including the iframe)
21533 * @param {Roo.HtmlEditorCore} this
21538 * Fires when the editor is first receives the focus. Any insertion must wait
21539 * until after this event.
21540 * @param {Roo.HtmlEditorCore} this
21544 * @event beforesync
21545 * Fires before the textarea is updated with content from the editor iframe. Return false
21546 * to cancel the sync.
21547 * @param {Roo.HtmlEditorCore} this
21548 * @param {String} html
21552 * @event beforepush
21553 * Fires before the iframe editor is updated with content from the textarea. Return false
21554 * to cancel the push.
21555 * @param {Roo.HtmlEditorCore} this
21556 * @param {String} html
21561 * Fires when the textarea is updated with content from the editor iframe.
21562 * @param {Roo.HtmlEditorCore} this
21563 * @param {String} html
21568 * Fires when the iframe editor is updated with content from the textarea.
21569 * @param {Roo.HtmlEditorCore} this
21570 * @param {String} html
21575 * @event editorevent
21576 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21577 * @param {Roo.HtmlEditorCore} this
21583 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21585 // defaults : white / black...
21586 this.applyBlacklists();
21593 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21597 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21603 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21608 * @cfg {Number} height (in pixels)
21612 * @cfg {Number} width (in pixels)
21617 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21620 stylesheets: false,
21625 // private properties
21626 validationEvent : false,
21628 initialized : false,
21630 sourceEditMode : false,
21631 onFocus : Roo.emptyFn,
21633 hideMode:'offsets',
21637 // blacklist + whitelisted elements..
21644 * Protected method that will not generally be called directly. It
21645 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21646 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21648 getDocMarkup : function(){
21652 // inherit styels from page...??
21653 if (this.stylesheets === false) {
21655 Roo.get(document.head).select('style').each(function(node) {
21656 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21659 Roo.get(document.head).select('link').each(function(node) {
21660 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21663 } else if (!this.stylesheets.length) {
21665 st = '<style type="text/css">' +
21666 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21669 st = '<style type="text/css">' +
21674 st += '<style type="text/css">' +
21675 'IMG { cursor: pointer } ' +
21678 var cls = 'roo-htmleditor-body';
21680 if(this.bodyCls.length){
21681 cls += ' ' + this.bodyCls;
21684 return '<html><head>' + st +
21685 //<style type="text/css">' +
21686 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21688 ' </head><body class="' + cls + '"></body></html>';
21692 onRender : function(ct, position)
21695 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21696 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21699 this.el.dom.style.border = '0 none';
21700 this.el.dom.setAttribute('tabIndex', -1);
21701 this.el.addClass('x-hidden hide');
21705 if(Roo.isIE){ // fix IE 1px bogus margin
21706 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21710 this.frameId = Roo.id();
21714 var iframe = this.owner.wrap.createChild({
21716 cls: 'form-control', // bootstrap..
21718 name: this.frameId,
21719 frameBorder : 'no',
21720 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21725 this.iframe = iframe.dom;
21727 this.assignDocWin();
21729 this.doc.designMode = 'on';
21732 this.doc.write(this.getDocMarkup());
21736 var task = { // must defer to wait for browser to be ready
21738 //console.log("run task?" + this.doc.readyState);
21739 this.assignDocWin();
21740 if(this.doc.body || this.doc.readyState == 'complete'){
21742 this.doc.designMode="on";
21746 Roo.TaskMgr.stop(task);
21747 this.initEditor.defer(10, this);
21754 Roo.TaskMgr.start(task);
21759 onResize : function(w, h)
21761 Roo.log('resize: ' +w + ',' + h );
21762 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21766 if(typeof w == 'number'){
21768 this.iframe.style.width = w + 'px';
21770 if(typeof h == 'number'){
21772 this.iframe.style.height = h + 'px';
21774 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21781 * Toggles the editor between standard and source edit mode.
21782 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21784 toggleSourceEdit : function(sourceEditMode){
21786 this.sourceEditMode = sourceEditMode === true;
21788 if(this.sourceEditMode){
21790 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21793 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21794 //this.iframe.className = '';
21797 //this.setSize(this.owner.wrap.getSize());
21798 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21805 * Protected method that will not generally be called directly. If you need/want
21806 * custom HTML cleanup, this is the method you should override.
21807 * @param {String} html The HTML to be cleaned
21808 * return {String} The cleaned HTML
21810 cleanHtml : function(html){
21811 html = String(html);
21812 if(html.length > 5){
21813 if(Roo.isSafari){ // strip safari nonsense
21814 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21817 if(html == ' '){
21824 * HTML Editor -> Textarea
21825 * Protected method that will not generally be called directly. Syncs the contents
21826 * of the editor iframe with the textarea.
21828 syncValue : function(){
21829 if(this.initialized){
21830 var bd = (this.doc.body || this.doc.documentElement);
21831 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21832 var html = bd.innerHTML;
21834 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21835 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21837 html = '<div style="'+m[0]+'">' + html + '</div>';
21840 html = this.cleanHtml(html);
21841 // fix up the special chars.. normaly like back quotes in word...
21842 // however we do not want to do this with chinese..
21843 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21844 var cc = b.charCodeAt();
21846 (cc >= 0x4E00 && cc < 0xA000 ) ||
21847 (cc >= 0x3400 && cc < 0x4E00 ) ||
21848 (cc >= 0xf900 && cc < 0xfb00 )
21854 if(this.owner.fireEvent('beforesync', this, html) !== false){
21855 this.el.dom.value = html;
21856 this.owner.fireEvent('sync', this, html);
21862 * Protected method that will not generally be called directly. Pushes the value of the textarea
21863 * into the iframe editor.
21865 pushValue : function(){
21866 if(this.initialized){
21867 var v = this.el.dom.value.trim();
21869 // if(v.length < 1){
21873 if(this.owner.fireEvent('beforepush', this, v) !== false){
21874 var d = (this.doc.body || this.doc.documentElement);
21876 this.cleanUpPaste();
21877 this.el.dom.value = d.innerHTML;
21878 this.owner.fireEvent('push', this, v);
21884 deferFocus : function(){
21885 this.focus.defer(10, this);
21889 focus : function(){
21890 if(this.win && !this.sourceEditMode){
21897 assignDocWin: function()
21899 var iframe = this.iframe;
21902 this.doc = iframe.contentWindow.document;
21903 this.win = iframe.contentWindow;
21905 // if (!Roo.get(this.frameId)) {
21908 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21909 // this.win = Roo.get(this.frameId).dom.contentWindow;
21911 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21915 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21916 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21921 initEditor : function(){
21922 //console.log("INIT EDITOR");
21923 this.assignDocWin();
21927 this.doc.designMode="on";
21929 this.doc.write(this.getDocMarkup());
21932 var dbody = (this.doc.body || this.doc.documentElement);
21933 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21934 // this copies styles from the containing element into thsi one..
21935 // not sure why we need all of this..
21936 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21938 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21939 //ss['background-attachment'] = 'fixed'; // w3c
21940 dbody.bgProperties = 'fixed'; // ie
21941 //Roo.DomHelper.applyStyles(dbody, ss);
21942 Roo.EventManager.on(this.doc, {
21943 //'mousedown': this.onEditorEvent,
21944 'mouseup': this.onEditorEvent,
21945 'dblclick': this.onEditorEvent,
21946 'click': this.onEditorEvent,
21947 'keyup': this.onEditorEvent,
21952 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21954 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21955 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21957 this.initialized = true;
21959 this.owner.fireEvent('initialize', this);
21964 onDestroy : function(){
21970 //for (var i =0; i < this.toolbars.length;i++) {
21971 // // fixme - ask toolbars for heights?
21972 // this.toolbars[i].onDestroy();
21975 //this.wrap.dom.innerHTML = '';
21976 //this.wrap.remove();
21981 onFirstFocus : function(){
21983 this.assignDocWin();
21986 this.activated = true;
21989 if(Roo.isGecko){ // prevent silly gecko errors
21991 var s = this.win.getSelection();
21992 if(!s.focusNode || s.focusNode.nodeType != 3){
21993 var r = s.getRangeAt(0);
21994 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21999 this.execCmd('useCSS', true);
22000 this.execCmd('styleWithCSS', false);
22003 this.owner.fireEvent('activate', this);
22007 adjustFont: function(btn){
22008 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22009 //if(Roo.isSafari){ // safari
22012 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22013 if(Roo.isSafari){ // safari
22014 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22015 v = (v < 10) ? 10 : v;
22016 v = (v > 48) ? 48 : v;
22017 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22022 v = Math.max(1, v+adjust);
22024 this.execCmd('FontSize', v );
22027 onEditorEvent : function(e)
22029 this.owner.fireEvent('editorevent', this, e);
22030 // this.updateToolbar();
22031 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22034 insertTag : function(tg)
22036 // could be a bit smarter... -> wrap the current selected tRoo..
22037 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22039 range = this.createRange(this.getSelection());
22040 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22041 wrappingNode.appendChild(range.extractContents());
22042 range.insertNode(wrappingNode);
22049 this.execCmd("formatblock", tg);
22053 insertText : function(txt)
22057 var range = this.createRange();
22058 range.deleteContents();
22059 //alert(Sender.getAttribute('label'));
22061 range.insertNode(this.doc.createTextNode(txt));
22067 * Executes a Midas editor command on the editor document and performs necessary focus and
22068 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22069 * @param {String} cmd The Midas command
22070 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22072 relayCmd : function(cmd, value){
22074 this.execCmd(cmd, value);
22075 this.owner.fireEvent('editorevent', this);
22076 //this.updateToolbar();
22077 this.owner.deferFocus();
22081 * Executes a Midas editor command directly on the editor document.
22082 * For visual commands, you should use {@link #relayCmd} instead.
22083 * <b>This should only be called after the editor is initialized.</b>
22084 * @param {String} cmd The Midas command
22085 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22087 execCmd : function(cmd, value){
22088 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22095 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22097 * @param {String} text | dom node..
22099 insertAtCursor : function(text)
22102 if(!this.activated){
22108 var r = this.doc.selection.createRange();
22119 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22123 // from jquery ui (MIT licenced)
22125 var win = this.win;
22127 if (win.getSelection && win.getSelection().getRangeAt) {
22128 range = win.getSelection().getRangeAt(0);
22129 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22130 range.insertNode(node);
22131 } else if (win.document.selection && win.document.selection.createRange) {
22132 // no firefox support
22133 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22134 win.document.selection.createRange().pasteHTML(txt);
22136 // no firefox support
22137 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22138 this.execCmd('InsertHTML', txt);
22147 mozKeyPress : function(e){
22149 var c = e.getCharCode(), cmd;
22152 c = String.fromCharCode(c).toLowerCase();
22166 this.cleanUpPaste.defer(100, this);
22174 e.preventDefault();
22182 fixKeys : function(){ // load time branching for fastest keydown performance
22184 return function(e){
22185 var k = e.getKey(), r;
22188 r = this.doc.selection.createRange();
22191 r.pasteHTML('    ');
22198 r = this.doc.selection.createRange();
22200 var target = r.parentElement();
22201 if(!target || target.tagName.toLowerCase() != 'li'){
22203 r.pasteHTML('<br />');
22209 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22210 this.cleanUpPaste.defer(100, this);
22216 }else if(Roo.isOpera){
22217 return function(e){
22218 var k = e.getKey();
22222 this.execCmd('InsertHTML','    ');
22225 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22226 this.cleanUpPaste.defer(100, this);
22231 }else if(Roo.isSafari){
22232 return function(e){
22233 var k = e.getKey();
22237 this.execCmd('InsertText','\t');
22241 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22242 this.cleanUpPaste.defer(100, this);
22250 getAllAncestors: function()
22252 var p = this.getSelectedNode();
22255 a.push(p); // push blank onto stack..
22256 p = this.getParentElement();
22260 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22264 a.push(this.doc.body);
22268 lastSelNode : false,
22271 getSelection : function()
22273 this.assignDocWin();
22274 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22277 getSelectedNode: function()
22279 // this may only work on Gecko!!!
22281 // should we cache this!!!!
22286 var range = this.createRange(this.getSelection()).cloneRange();
22289 var parent = range.parentElement();
22291 var testRange = range.duplicate();
22292 testRange.moveToElementText(parent);
22293 if (testRange.inRange(range)) {
22296 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22299 parent = parent.parentElement;
22304 // is ancestor a text element.
22305 var ac = range.commonAncestorContainer;
22306 if (ac.nodeType == 3) {
22307 ac = ac.parentNode;
22310 var ar = ac.childNodes;
22313 var other_nodes = [];
22314 var has_other_nodes = false;
22315 for (var i=0;i<ar.length;i++) {
22316 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22319 // fullly contained node.
22321 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22326 // probably selected..
22327 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22328 other_nodes.push(ar[i]);
22332 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22337 has_other_nodes = true;
22339 if (!nodes.length && other_nodes.length) {
22340 nodes= other_nodes;
22342 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22348 createRange: function(sel)
22350 // this has strange effects when using with
22351 // top toolbar - not sure if it's a great idea.
22352 //this.editor.contentWindow.focus();
22353 if (typeof sel != "undefined") {
22355 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22357 return this.doc.createRange();
22360 return this.doc.createRange();
22363 getParentElement: function()
22366 this.assignDocWin();
22367 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22369 var range = this.createRange(sel);
22372 var p = range.commonAncestorContainer;
22373 while (p.nodeType == 3) { // text node
22384 * Range intersection.. the hard stuff...
22388 * [ -- selected range --- ]
22392 * if end is before start or hits it. fail.
22393 * if start is after end or hits it fail.
22395 * if either hits (but other is outside. - then it's not
22401 // @see http://www.thismuchiknow.co.uk/?p=64.
22402 rangeIntersectsNode : function(range, node)
22404 var nodeRange = node.ownerDocument.createRange();
22406 nodeRange.selectNode(node);
22408 nodeRange.selectNodeContents(node);
22411 var rangeStartRange = range.cloneRange();
22412 rangeStartRange.collapse(true);
22414 var rangeEndRange = range.cloneRange();
22415 rangeEndRange.collapse(false);
22417 var nodeStartRange = nodeRange.cloneRange();
22418 nodeStartRange.collapse(true);
22420 var nodeEndRange = nodeRange.cloneRange();
22421 nodeEndRange.collapse(false);
22423 return rangeStartRange.compareBoundaryPoints(
22424 Range.START_TO_START, nodeEndRange) == -1 &&
22425 rangeEndRange.compareBoundaryPoints(
22426 Range.START_TO_START, nodeStartRange) == 1;
22430 rangeCompareNode : function(range, node)
22432 var nodeRange = node.ownerDocument.createRange();
22434 nodeRange.selectNode(node);
22436 nodeRange.selectNodeContents(node);
22440 range.collapse(true);
22442 nodeRange.collapse(true);
22444 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22445 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22447 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22449 var nodeIsBefore = ss == 1;
22450 var nodeIsAfter = ee == -1;
22452 if (nodeIsBefore && nodeIsAfter) {
22455 if (!nodeIsBefore && nodeIsAfter) {
22456 return 1; //right trailed.
22459 if (nodeIsBefore && !nodeIsAfter) {
22460 return 2; // left trailed.
22466 // private? - in a new class?
22467 cleanUpPaste : function()
22469 // cleans up the whole document..
22470 Roo.log('cleanuppaste');
22472 this.cleanUpChildren(this.doc.body);
22473 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22474 if (clean != this.doc.body.innerHTML) {
22475 this.doc.body.innerHTML = clean;
22480 cleanWordChars : function(input) {// change the chars to hex code
22481 var he = Roo.HtmlEditorCore;
22483 var output = input;
22484 Roo.each(he.swapCodes, function(sw) {
22485 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22487 output = output.replace(swapper, sw[1]);
22494 cleanUpChildren : function (n)
22496 if (!n.childNodes.length) {
22499 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22500 this.cleanUpChild(n.childNodes[i]);
22507 cleanUpChild : function (node)
22510 //console.log(node);
22511 if (node.nodeName == "#text") {
22512 // clean up silly Windows -- stuff?
22515 if (node.nodeName == "#comment") {
22516 node.parentNode.removeChild(node);
22517 // clean up silly Windows -- stuff?
22520 var lcname = node.tagName.toLowerCase();
22521 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22522 // whitelist of tags..
22524 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22526 node.parentNode.removeChild(node);
22531 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22533 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22534 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22536 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22537 // remove_keep_children = true;
22540 if (remove_keep_children) {
22541 this.cleanUpChildren(node);
22542 // inserts everything just before this node...
22543 while (node.childNodes.length) {
22544 var cn = node.childNodes[0];
22545 node.removeChild(cn);
22546 node.parentNode.insertBefore(cn, node);
22548 node.parentNode.removeChild(node);
22552 if (!node.attributes || !node.attributes.length) {
22553 this.cleanUpChildren(node);
22557 function cleanAttr(n,v)
22560 if (v.match(/^\./) || v.match(/^\//)) {
22563 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22566 if (v.match(/^#/)) {
22569 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22570 node.removeAttribute(n);
22574 var cwhite = this.cwhite;
22575 var cblack = this.cblack;
22577 function cleanStyle(n,v)
22579 if (v.match(/expression/)) { //XSS?? should we even bother..
22580 node.removeAttribute(n);
22584 var parts = v.split(/;/);
22587 Roo.each(parts, function(p) {
22588 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22592 var l = p.split(':').shift().replace(/\s+/g,'');
22593 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22595 if ( cwhite.length && cblack.indexOf(l) > -1) {
22596 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22597 //node.removeAttribute(n);
22601 // only allow 'c whitelisted system attributes'
22602 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22603 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22604 //node.removeAttribute(n);
22614 if (clean.length) {
22615 node.setAttribute(n, clean.join(';'));
22617 node.removeAttribute(n);
22623 for (var i = node.attributes.length-1; i > -1 ; i--) {
22624 var a = node.attributes[i];
22627 if (a.name.toLowerCase().substr(0,2)=='on') {
22628 node.removeAttribute(a.name);
22631 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22632 node.removeAttribute(a.name);
22635 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22636 cleanAttr(a.name,a.value); // fixme..
22639 if (a.name == 'style') {
22640 cleanStyle(a.name,a.value);
22643 /// clean up MS crap..
22644 // tecnically this should be a list of valid class'es..
22647 if (a.name == 'class') {
22648 if (a.value.match(/^Mso/)) {
22649 node.className = '';
22652 if (a.value.match(/^body$/)) {
22653 node.className = '';
22664 this.cleanUpChildren(node);
22670 * Clean up MS wordisms...
22672 cleanWord : function(node)
22677 this.cleanWord(this.doc.body);
22680 if (node.nodeName == "#text") {
22681 // clean up silly Windows -- stuff?
22684 if (node.nodeName == "#comment") {
22685 node.parentNode.removeChild(node);
22686 // clean up silly Windows -- stuff?
22690 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22691 node.parentNode.removeChild(node);
22695 // remove - but keep children..
22696 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22697 while (node.childNodes.length) {
22698 var cn = node.childNodes[0];
22699 node.removeChild(cn);
22700 node.parentNode.insertBefore(cn, node);
22702 node.parentNode.removeChild(node);
22703 this.iterateChildren(node, this.cleanWord);
22707 if (node.className.length) {
22709 var cn = node.className.split(/\W+/);
22711 Roo.each(cn, function(cls) {
22712 if (cls.match(/Mso[a-zA-Z]+/)) {
22717 node.className = cna.length ? cna.join(' ') : '';
22719 node.removeAttribute("class");
22723 if (node.hasAttribute("lang")) {
22724 node.removeAttribute("lang");
22727 if (node.hasAttribute("style")) {
22729 var styles = node.getAttribute("style").split(";");
22731 Roo.each(styles, function(s) {
22732 if (!s.match(/:/)) {
22735 var kv = s.split(":");
22736 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22739 // what ever is left... we allow.
22742 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22743 if (!nstyle.length) {
22744 node.removeAttribute('style');
22747 this.iterateChildren(node, this.cleanWord);
22753 * iterateChildren of a Node, calling fn each time, using this as the scole..
22754 * @param {DomNode} node node to iterate children of.
22755 * @param {Function} fn method of this class to call on each item.
22757 iterateChildren : function(node, fn)
22759 if (!node.childNodes.length) {
22762 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22763 fn.call(this, node.childNodes[i])
22769 * cleanTableWidths.
22771 * Quite often pasting from word etc.. results in tables with column and widths.
22772 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22775 cleanTableWidths : function(node)
22780 this.cleanTableWidths(this.doc.body);
22785 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22788 Roo.log(node.tagName);
22789 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22790 this.iterateChildren(node, this.cleanTableWidths);
22793 if (node.hasAttribute('width')) {
22794 node.removeAttribute('width');
22798 if (node.hasAttribute("style")) {
22801 var styles = node.getAttribute("style").split(";");
22803 Roo.each(styles, function(s) {
22804 if (!s.match(/:/)) {
22807 var kv = s.split(":");
22808 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22811 // what ever is left... we allow.
22814 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22815 if (!nstyle.length) {
22816 node.removeAttribute('style');
22820 this.iterateChildren(node, this.cleanTableWidths);
22828 domToHTML : function(currentElement, depth, nopadtext) {
22830 depth = depth || 0;
22831 nopadtext = nopadtext || false;
22833 if (!currentElement) {
22834 return this.domToHTML(this.doc.body);
22837 //Roo.log(currentElement);
22839 var allText = false;
22840 var nodeName = currentElement.nodeName;
22841 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22843 if (nodeName == '#text') {
22845 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22850 if (nodeName != 'BODY') {
22853 // Prints the node tagName, such as <A>, <IMG>, etc
22856 for(i = 0; i < currentElement.attributes.length;i++) {
22858 var aname = currentElement.attributes.item(i).name;
22859 if (!currentElement.attributes.item(i).value.length) {
22862 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22865 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22874 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22877 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22882 // Traverse the tree
22884 var currentElementChild = currentElement.childNodes.item(i);
22885 var allText = true;
22886 var innerHTML = '';
22888 while (currentElementChild) {
22889 // Formatting code (indent the tree so it looks nice on the screen)
22890 var nopad = nopadtext;
22891 if (lastnode == 'SPAN') {
22895 if (currentElementChild.nodeName == '#text') {
22896 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22897 toadd = nopadtext ? toadd : toadd.trim();
22898 if (!nopad && toadd.length > 80) {
22899 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22901 innerHTML += toadd;
22904 currentElementChild = currentElement.childNodes.item(i);
22910 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22912 // Recursively traverse the tree structure of the child node
22913 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22914 lastnode = currentElementChild.nodeName;
22916 currentElementChild=currentElement.childNodes.item(i);
22922 // The remaining code is mostly for formatting the tree
22923 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22928 ret+= "</"+tagName+">";
22934 applyBlacklists : function()
22936 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22937 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22941 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22942 if (b.indexOf(tag) > -1) {
22945 this.white.push(tag);
22949 Roo.each(w, function(tag) {
22950 if (b.indexOf(tag) > -1) {
22953 if (this.white.indexOf(tag) > -1) {
22956 this.white.push(tag);
22961 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22962 if (w.indexOf(tag) > -1) {
22965 this.black.push(tag);
22969 Roo.each(b, function(tag) {
22970 if (w.indexOf(tag) > -1) {
22973 if (this.black.indexOf(tag) > -1) {
22976 this.black.push(tag);
22981 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22982 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22986 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22987 if (b.indexOf(tag) > -1) {
22990 this.cwhite.push(tag);
22994 Roo.each(w, function(tag) {
22995 if (b.indexOf(tag) > -1) {
22998 if (this.cwhite.indexOf(tag) > -1) {
23001 this.cwhite.push(tag);
23006 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23007 if (w.indexOf(tag) > -1) {
23010 this.cblack.push(tag);
23014 Roo.each(b, function(tag) {
23015 if (w.indexOf(tag) > -1) {
23018 if (this.cblack.indexOf(tag) > -1) {
23021 this.cblack.push(tag);
23026 setStylesheets : function(stylesheets)
23028 if(typeof(stylesheets) == 'string'){
23029 Roo.get(this.iframe.contentDocument.head).createChild({
23031 rel : 'stylesheet',
23040 Roo.each(stylesheets, function(s) {
23045 Roo.get(_this.iframe.contentDocument.head).createChild({
23047 rel : 'stylesheet',
23056 removeStylesheets : function()
23060 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23065 setStyle : function(style)
23067 Roo.get(this.iframe.contentDocument.head).createChild({
23076 // hide stuff that is not compatible
23090 * @event specialkey
23094 * @cfg {String} fieldClass @hide
23097 * @cfg {String} focusClass @hide
23100 * @cfg {String} autoCreate @hide
23103 * @cfg {String} inputType @hide
23106 * @cfg {String} invalidClass @hide
23109 * @cfg {String} invalidText @hide
23112 * @cfg {String} msgFx @hide
23115 * @cfg {String} validateOnBlur @hide
23119 Roo.HtmlEditorCore.white = [
23120 'area', 'br', 'img', 'input', 'hr', 'wbr',
23122 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23123 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23124 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23125 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23126 'table', 'ul', 'xmp',
23128 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23131 'dir', 'menu', 'ol', 'ul', 'dl',
23137 Roo.HtmlEditorCore.black = [
23138 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23140 'base', 'basefont', 'bgsound', 'blink', 'body',
23141 'frame', 'frameset', 'head', 'html', 'ilayer',
23142 'iframe', 'layer', 'link', 'meta', 'object',
23143 'script', 'style' ,'title', 'xml' // clean later..
23145 Roo.HtmlEditorCore.clean = [
23146 'script', 'style', 'title', 'xml'
23148 Roo.HtmlEditorCore.remove = [
23153 Roo.HtmlEditorCore.ablack = [
23157 Roo.HtmlEditorCore.aclean = [
23158 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23162 Roo.HtmlEditorCore.pwhite= [
23163 'http', 'https', 'mailto'
23166 // white listed style attributes.
23167 Roo.HtmlEditorCore.cwhite= [
23168 // 'text-align', /// default is to allow most things..
23174 // black listed style attributes.
23175 Roo.HtmlEditorCore.cblack= [
23176 // 'font-size' -- this can be set by the project
23180 Roo.HtmlEditorCore.swapCodes =[
23199 * @class Roo.bootstrap.HtmlEditor
23200 * @extends Roo.bootstrap.TextArea
23201 * Bootstrap HtmlEditor class
23204 * Create a new HtmlEditor
23205 * @param {Object} config The config object
23208 Roo.bootstrap.HtmlEditor = function(config){
23209 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23210 if (!this.toolbars) {
23211 this.toolbars = [];
23214 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23217 * @event initialize
23218 * Fires when the editor is fully initialized (including the iframe)
23219 * @param {HtmlEditor} this
23224 * Fires when the editor is first receives the focus. Any insertion must wait
23225 * until after this event.
23226 * @param {HtmlEditor} this
23230 * @event beforesync
23231 * Fires before the textarea is updated with content from the editor iframe. Return false
23232 * to cancel the sync.
23233 * @param {HtmlEditor} this
23234 * @param {String} html
23238 * @event beforepush
23239 * Fires before the iframe editor is updated with content from the textarea. Return false
23240 * to cancel the push.
23241 * @param {HtmlEditor} this
23242 * @param {String} html
23247 * Fires when the textarea is updated with content from the editor iframe.
23248 * @param {HtmlEditor} this
23249 * @param {String} html
23254 * Fires when the iframe editor is updated with content from the textarea.
23255 * @param {HtmlEditor} this
23256 * @param {String} html
23260 * @event editmodechange
23261 * Fires when the editor switches edit modes
23262 * @param {HtmlEditor} this
23263 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23265 editmodechange: true,
23267 * @event editorevent
23268 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23269 * @param {HtmlEditor} this
23273 * @event firstfocus
23274 * Fires when on first focus - needed by toolbars..
23275 * @param {HtmlEditor} this
23280 * Auto save the htmlEditor value as a file into Events
23281 * @param {HtmlEditor} this
23285 * @event savedpreview
23286 * preview the saved version of htmlEditor
23287 * @param {HtmlEditor} this
23294 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23298 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23303 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23308 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23313 * @cfg {Number} height (in pixels)
23317 * @cfg {Number} width (in pixels)
23322 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23325 stylesheets: false,
23330 // private properties
23331 validationEvent : false,
23333 initialized : false,
23336 onFocus : Roo.emptyFn,
23338 hideMode:'offsets',
23340 tbContainer : false,
23344 toolbarContainer :function() {
23345 return this.wrap.select('.x-html-editor-tb',true).first();
23349 * Protected method that will not generally be called directly. It
23350 * is called when the editor creates its toolbar. Override this method if you need to
23351 * add custom toolbar buttons.
23352 * @param {HtmlEditor} editor
23354 createToolbar : function(){
23355 Roo.log('renewing');
23356 Roo.log("create toolbars");
23358 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23359 this.toolbars[0].render(this.toolbarContainer());
23363 // if (!editor.toolbars || !editor.toolbars.length) {
23364 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23367 // for (var i =0 ; i < editor.toolbars.length;i++) {
23368 // editor.toolbars[i] = Roo.factory(
23369 // typeof(editor.toolbars[i]) == 'string' ?
23370 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23371 // Roo.bootstrap.HtmlEditor);
23372 // editor.toolbars[i].init(editor);
23378 onRender : function(ct, position)
23380 // Roo.log("Call onRender: " + this.xtype);
23382 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23384 this.wrap = this.inputEl().wrap({
23385 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23388 this.editorcore.onRender(ct, position);
23390 if (this.resizable) {
23391 this.resizeEl = new Roo.Resizable(this.wrap, {
23395 minHeight : this.height,
23396 height: this.height,
23397 handles : this.resizable,
23400 resize : function(r, w, h) {
23401 _t.onResize(w,h); // -something
23407 this.createToolbar(this);
23410 if(!this.width && this.resizable){
23411 this.setSize(this.wrap.getSize());
23413 if (this.resizeEl) {
23414 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23415 // should trigger onReize..
23421 onResize : function(w, h)
23423 Roo.log('resize: ' +w + ',' + h );
23424 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23428 if(this.inputEl() ){
23429 if(typeof w == 'number'){
23430 var aw = w - this.wrap.getFrameWidth('lr');
23431 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23434 if(typeof h == 'number'){
23435 var tbh = -11; // fixme it needs to tool bar size!
23436 for (var i =0; i < this.toolbars.length;i++) {
23437 // fixme - ask toolbars for heights?
23438 tbh += this.toolbars[i].el.getHeight();
23439 //if (this.toolbars[i].footer) {
23440 // tbh += this.toolbars[i].footer.el.getHeight();
23448 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23449 ah -= 5; // knock a few pixes off for look..
23450 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23454 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23455 this.editorcore.onResize(ew,eh);
23460 * Toggles the editor between standard and source edit mode.
23461 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23463 toggleSourceEdit : function(sourceEditMode)
23465 this.editorcore.toggleSourceEdit(sourceEditMode);
23467 if(this.editorcore.sourceEditMode){
23468 Roo.log('editor - showing textarea');
23471 // Roo.log(this.syncValue());
23473 this.inputEl().removeClass(['hide', 'x-hidden']);
23474 this.inputEl().dom.removeAttribute('tabIndex');
23475 this.inputEl().focus();
23477 Roo.log('editor - hiding textarea');
23479 // Roo.log(this.pushValue());
23482 this.inputEl().addClass(['hide', 'x-hidden']);
23483 this.inputEl().dom.setAttribute('tabIndex', -1);
23484 //this.deferFocus();
23487 if(this.resizable){
23488 this.setSize(this.wrap.getSize());
23491 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23494 // private (for BoxComponent)
23495 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23497 // private (for BoxComponent)
23498 getResizeEl : function(){
23502 // private (for BoxComponent)
23503 getPositionEl : function(){
23508 initEvents : function(){
23509 this.originalValue = this.getValue();
23513 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23516 // markInvalid : Roo.emptyFn,
23518 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23521 // clearInvalid : Roo.emptyFn,
23523 setValue : function(v){
23524 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23525 this.editorcore.pushValue();
23530 deferFocus : function(){
23531 this.focus.defer(10, this);
23535 focus : function(){
23536 this.editorcore.focus();
23542 onDestroy : function(){
23548 for (var i =0; i < this.toolbars.length;i++) {
23549 // fixme - ask toolbars for heights?
23550 this.toolbars[i].onDestroy();
23553 this.wrap.dom.innerHTML = '';
23554 this.wrap.remove();
23559 onFirstFocus : function(){
23560 //Roo.log("onFirstFocus");
23561 this.editorcore.onFirstFocus();
23562 for (var i =0; i < this.toolbars.length;i++) {
23563 this.toolbars[i].onFirstFocus();
23569 syncValue : function()
23571 this.editorcore.syncValue();
23574 pushValue : function()
23576 this.editorcore.pushValue();
23580 // hide stuff that is not compatible
23594 * @event specialkey
23598 * @cfg {String} fieldClass @hide
23601 * @cfg {String} focusClass @hide
23604 * @cfg {String} autoCreate @hide
23607 * @cfg {String} inputType @hide
23610 * @cfg {String} invalidClass @hide
23613 * @cfg {String} invalidText @hide
23616 * @cfg {String} msgFx @hide
23619 * @cfg {String} validateOnBlur @hide
23628 Roo.namespace('Roo.bootstrap.htmleditor');
23630 * @class Roo.bootstrap.HtmlEditorToolbar1
23635 new Roo.bootstrap.HtmlEditor({
23638 new Roo.bootstrap.HtmlEditorToolbar1({
23639 disable : { fonts: 1 , format: 1, ..., ... , ...],
23645 * @cfg {Object} disable List of elements to disable..
23646 * @cfg {Array} btns List of additional buttons.
23650 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23653 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23656 Roo.apply(this, config);
23658 // default disabled, based on 'good practice'..
23659 this.disable = this.disable || {};
23660 Roo.applyIf(this.disable, {
23663 specialElements : true
23665 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23667 this.editor = config.editor;
23668 this.editorcore = config.editor.editorcore;
23670 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23672 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23673 // dont call parent... till later.
23675 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23680 editorcore : false,
23685 "h1","h2","h3","h4","h5","h6",
23687 "abbr", "acronym", "address", "cite", "samp", "var",
23691 onRender : function(ct, position)
23693 // Roo.log("Call onRender: " + this.xtype);
23695 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23697 this.el.dom.style.marginBottom = '0';
23699 var editorcore = this.editorcore;
23700 var editor= this.editor;
23703 var btn = function(id,cmd , toggle, handler, html){
23705 var event = toggle ? 'toggle' : 'click';
23710 xns: Roo.bootstrap,
23713 enableToggle:toggle !== false,
23715 pressed : toggle ? false : null,
23718 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23719 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23725 // var cb_box = function...
23730 xns: Roo.bootstrap,
23731 glyphicon : 'font',
23735 xns: Roo.bootstrap,
23739 Roo.each(this.formats, function(f) {
23740 style.menu.items.push({
23742 xns: Roo.bootstrap,
23743 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23748 editorcore.insertTag(this.tagname);
23755 children.push(style);
23757 btn('bold',false,true);
23758 btn('italic',false,true);
23759 btn('align-left', 'justifyleft',true);
23760 btn('align-center', 'justifycenter',true);
23761 btn('align-right' , 'justifyright',true);
23762 btn('link', false, false, function(btn) {
23763 //Roo.log("create link?");
23764 var url = prompt(this.createLinkText, this.defaultLinkValue);
23765 if(url && url != 'http:/'+'/'){
23766 this.editorcore.relayCmd('createlink', url);
23769 btn('list','insertunorderedlist',true);
23770 btn('pencil', false,true, function(btn){
23772 this.toggleSourceEdit(btn.pressed);
23775 if (this.editor.btns.length > 0) {
23776 for (var i = 0; i<this.editor.btns.length; i++) {
23777 children.push(this.editor.btns[i]);
23785 xns: Roo.bootstrap,
23790 xns: Roo.bootstrap,
23795 cog.menu.items.push({
23797 xns: Roo.bootstrap,
23798 html : Clean styles,
23803 editorcore.insertTag(this.tagname);
23812 this.xtype = 'NavSimplebar';
23814 for(var i=0;i< children.length;i++) {
23816 this.buttons.add(this.addxtypeChild(children[i]));
23820 editor.on('editorevent', this.updateToolbar, this);
23822 onBtnClick : function(id)
23824 this.editorcore.relayCmd(id);
23825 this.editorcore.focus();
23829 * Protected method that will not generally be called directly. It triggers
23830 * a toolbar update by reading the markup state of the current selection in the editor.
23832 updateToolbar: function(){
23834 if(!this.editorcore.activated){
23835 this.editor.onFirstFocus(); // is this neeed?
23839 var btns = this.buttons;
23840 var doc = this.editorcore.doc;
23841 btns.get('bold').setActive(doc.queryCommandState('bold'));
23842 btns.get('italic').setActive(doc.queryCommandState('italic'));
23843 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23845 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23846 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23847 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23849 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23850 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23853 var ans = this.editorcore.getAllAncestors();
23854 if (this.formatCombo) {
23857 var store = this.formatCombo.store;
23858 this.formatCombo.setValue("");
23859 for (var i =0; i < ans.length;i++) {
23860 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23862 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23870 // hides menus... - so this cant be on a menu...
23871 Roo.bootstrap.MenuMgr.hideAll();
23873 Roo.bootstrap.MenuMgr.hideAll();
23874 //this.editorsyncValue();
23876 onFirstFocus: function() {
23877 this.buttons.each(function(item){
23881 toggleSourceEdit : function(sourceEditMode){
23884 if(sourceEditMode){
23885 Roo.log("disabling buttons");
23886 this.buttons.each( function(item){
23887 if(item.cmd != 'pencil'){
23893 Roo.log("enabling buttons");
23894 if(this.editorcore.initialized){
23895 this.buttons.each( function(item){
23901 Roo.log("calling toggole on editor");
23902 // tell the editor that it's been pressed..
23903 this.editor.toggleSourceEdit(sourceEditMode);
23913 * @class Roo.bootstrap.Table.AbstractSelectionModel
23914 * @extends Roo.util.Observable
23915 * Abstract base class for grid SelectionModels. It provides the interface that should be
23916 * implemented by descendant classes. This class should not be directly instantiated.
23919 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23920 this.locked = false;
23921 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23925 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23926 /** @ignore Called by the grid automatically. Do not call directly. */
23927 init : function(grid){
23933 * Locks the selections.
23936 this.locked = true;
23940 * Unlocks the selections.
23942 unlock : function(){
23943 this.locked = false;
23947 * Returns true if the selections are locked.
23948 * @return {Boolean}
23950 isLocked : function(){
23951 return this.locked;
23955 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23956 * @class Roo.bootstrap.Table.RowSelectionModel
23957 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23958 * It supports multiple selections and keyboard selection/navigation.
23960 * @param {Object} config
23963 Roo.bootstrap.Table.RowSelectionModel = function(config){
23964 Roo.apply(this, config);
23965 this.selections = new Roo.util.MixedCollection(false, function(o){
23970 this.lastActive = false;
23974 * @event selectionchange
23975 * Fires when the selection changes
23976 * @param {SelectionModel} this
23978 "selectionchange" : true,
23980 * @event afterselectionchange
23981 * Fires after the selection changes (eg. by key press or clicking)
23982 * @param {SelectionModel} this
23984 "afterselectionchange" : true,
23986 * @event beforerowselect
23987 * Fires when a row is selected being selected, return false to cancel.
23988 * @param {SelectionModel} this
23989 * @param {Number} rowIndex The selected index
23990 * @param {Boolean} keepExisting False if other selections will be cleared
23992 "beforerowselect" : true,
23995 * Fires when a row is selected.
23996 * @param {SelectionModel} this
23997 * @param {Number} rowIndex The selected index
23998 * @param {Roo.data.Record} r The record
24000 "rowselect" : true,
24002 * @event rowdeselect
24003 * Fires when a row is deselected.
24004 * @param {SelectionModel} this
24005 * @param {Number} rowIndex The selected index
24007 "rowdeselect" : true
24009 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24010 this.locked = false;
24013 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24015 * @cfg {Boolean} singleSelect
24016 * True to allow selection of only one row at a time (defaults to false)
24018 singleSelect : false,
24021 initEvents : function()
24024 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24025 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24026 //}else{ // allow click to work like normal
24027 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24029 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24030 this.grid.on("rowclick", this.handleMouseDown, this);
24032 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24033 "up" : function(e){
24035 this.selectPrevious(e.shiftKey);
24036 }else if(this.last !== false && this.lastActive !== false){
24037 var last = this.last;
24038 this.selectRange(this.last, this.lastActive-1);
24039 this.grid.getView().focusRow(this.lastActive);
24040 if(last !== false){
24044 this.selectFirstRow();
24046 this.fireEvent("afterselectionchange", this);
24048 "down" : function(e){
24050 this.selectNext(e.shiftKey);
24051 }else if(this.last !== false && this.lastActive !== false){
24052 var last = this.last;
24053 this.selectRange(this.last, this.lastActive+1);
24054 this.grid.getView().focusRow(this.lastActive);
24055 if(last !== false){
24059 this.selectFirstRow();
24061 this.fireEvent("afterselectionchange", this);
24065 this.grid.store.on('load', function(){
24066 this.selections.clear();
24069 var view = this.grid.view;
24070 view.on("refresh", this.onRefresh, this);
24071 view.on("rowupdated", this.onRowUpdated, this);
24072 view.on("rowremoved", this.onRemove, this);
24077 onRefresh : function()
24079 var ds = this.grid.store, i, v = this.grid.view;
24080 var s = this.selections;
24081 s.each(function(r){
24082 if((i = ds.indexOfId(r.id)) != -1){
24091 onRemove : function(v, index, r){
24092 this.selections.remove(r);
24096 onRowUpdated : function(v, index, r){
24097 if(this.isSelected(r)){
24098 v.onRowSelect(index);
24104 * @param {Array} records The records to select
24105 * @param {Boolean} keepExisting (optional) True to keep existing selections
24107 selectRecords : function(records, keepExisting)
24110 this.clearSelections();
24112 var ds = this.grid.store;
24113 for(var i = 0, len = records.length; i < len; i++){
24114 this.selectRow(ds.indexOf(records[i]), true);
24119 * Gets the number of selected rows.
24122 getCount : function(){
24123 return this.selections.length;
24127 * Selects the first row in the grid.
24129 selectFirstRow : function(){
24134 * Select the last row.
24135 * @param {Boolean} keepExisting (optional) True to keep existing selections
24137 selectLastRow : function(keepExisting){
24138 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24139 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24143 * Selects the row immediately following the last selected row.
24144 * @param {Boolean} keepExisting (optional) True to keep existing selections
24146 selectNext : function(keepExisting)
24148 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24149 this.selectRow(this.last+1, keepExisting);
24150 this.grid.getView().focusRow(this.last);
24155 * Selects the row that precedes the last selected row.
24156 * @param {Boolean} keepExisting (optional) True to keep existing selections
24158 selectPrevious : function(keepExisting){
24160 this.selectRow(this.last-1, keepExisting);
24161 this.grid.getView().focusRow(this.last);
24166 * Returns the selected records
24167 * @return {Array} Array of selected records
24169 getSelections : function(){
24170 return [].concat(this.selections.items);
24174 * Returns the first selected record.
24177 getSelected : function(){
24178 return this.selections.itemAt(0);
24183 * Clears all selections.
24185 clearSelections : function(fast)
24191 var ds = this.grid.store;
24192 var s = this.selections;
24193 s.each(function(r){
24194 this.deselectRow(ds.indexOfId(r.id));
24198 this.selections.clear();
24205 * Selects all rows.
24207 selectAll : function(){
24211 this.selections.clear();
24212 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24213 this.selectRow(i, true);
24218 * Returns True if there is a selection.
24219 * @return {Boolean}
24221 hasSelection : function(){
24222 return this.selections.length > 0;
24226 * Returns True if the specified row is selected.
24227 * @param {Number/Record} record The record or index of the record to check
24228 * @return {Boolean}
24230 isSelected : function(index){
24231 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24232 return (r && this.selections.key(r.id) ? true : false);
24236 * Returns True if the specified record id is selected.
24237 * @param {String} id The id of record to check
24238 * @return {Boolean}
24240 isIdSelected : function(id){
24241 return (this.selections.key(id) ? true : false);
24246 handleMouseDBClick : function(e, t){
24250 handleMouseDown : function(e, t)
24252 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24253 if(this.isLocked() || rowIndex < 0 ){
24256 if(e.shiftKey && this.last !== false){
24257 var last = this.last;
24258 this.selectRange(last, rowIndex, e.ctrlKey);
24259 this.last = last; // reset the last
24263 var isSelected = this.isSelected(rowIndex);
24264 //Roo.log("select row:" + rowIndex);
24266 this.deselectRow(rowIndex);
24268 this.selectRow(rowIndex, true);
24272 if(e.button !== 0 && isSelected){
24273 alert('rowIndex 2: ' + rowIndex);
24274 view.focusRow(rowIndex);
24275 }else if(e.ctrlKey && isSelected){
24276 this.deselectRow(rowIndex);
24277 }else if(!isSelected){
24278 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24279 view.focusRow(rowIndex);
24283 this.fireEvent("afterselectionchange", this);
24286 handleDragableRowClick : function(grid, rowIndex, e)
24288 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24289 this.selectRow(rowIndex, false);
24290 grid.view.focusRow(rowIndex);
24291 this.fireEvent("afterselectionchange", this);
24296 * Selects multiple rows.
24297 * @param {Array} rows Array of the indexes of the row to select
24298 * @param {Boolean} keepExisting (optional) True to keep existing selections
24300 selectRows : function(rows, keepExisting){
24302 this.clearSelections();
24304 for(var i = 0, len = rows.length; i < len; i++){
24305 this.selectRow(rows[i], true);
24310 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24311 * @param {Number} startRow The index of the first row in the range
24312 * @param {Number} endRow The index of the last row in the range
24313 * @param {Boolean} keepExisting (optional) True to retain existing selections
24315 selectRange : function(startRow, endRow, keepExisting){
24320 this.clearSelections();
24322 if(startRow <= endRow){
24323 for(var i = startRow; i <= endRow; i++){
24324 this.selectRow(i, true);
24327 for(var i = startRow; i >= endRow; i--){
24328 this.selectRow(i, true);
24334 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24335 * @param {Number} startRow The index of the first row in the range
24336 * @param {Number} endRow The index of the last row in the range
24338 deselectRange : function(startRow, endRow, preventViewNotify){
24342 for(var i = startRow; i <= endRow; i++){
24343 this.deselectRow(i, preventViewNotify);
24349 * @param {Number} row The index of the row to select
24350 * @param {Boolean} keepExisting (optional) True to keep existing selections
24352 selectRow : function(index, keepExisting, preventViewNotify)
24354 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24357 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24358 if(!keepExisting || this.singleSelect){
24359 this.clearSelections();
24362 var r = this.grid.store.getAt(index);
24363 //console.log('selectRow - record id :' + r.id);
24365 this.selections.add(r);
24366 this.last = this.lastActive = index;
24367 if(!preventViewNotify){
24368 var proxy = new Roo.Element(
24369 this.grid.getRowDom(index)
24371 proxy.addClass('bg-info info');
24373 this.fireEvent("rowselect", this, index, r);
24374 this.fireEvent("selectionchange", this);
24380 * @param {Number} row The index of the row to deselect
24382 deselectRow : function(index, preventViewNotify)
24387 if(this.last == index){
24390 if(this.lastActive == index){
24391 this.lastActive = false;
24394 var r = this.grid.store.getAt(index);
24399 this.selections.remove(r);
24400 //.console.log('deselectRow - record id :' + r.id);
24401 if(!preventViewNotify){
24403 var proxy = new Roo.Element(
24404 this.grid.getRowDom(index)
24406 proxy.removeClass('bg-info info');
24408 this.fireEvent("rowdeselect", this, index);
24409 this.fireEvent("selectionchange", this);
24413 restoreLast : function(){
24415 this.last = this._last;
24420 acceptsNav : function(row, col, cm){
24421 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24425 onEditorKey : function(field, e){
24426 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24431 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24433 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24435 }else if(k == e.ENTER && !e.ctrlKey){
24439 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24441 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24443 }else if(k == e.ESC){
24447 g.startEditing(newCell[0], newCell[1]);
24453 * Ext JS Library 1.1.1
24454 * Copyright(c) 2006-2007, Ext JS, LLC.
24456 * Originally Released Under LGPL - original licence link has changed is not relivant.
24459 * <script type="text/javascript">
24463 * @class Roo.bootstrap.PagingToolbar
24464 * @extends Roo.bootstrap.NavSimplebar
24465 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24467 * Create a new PagingToolbar
24468 * @param {Object} config The config object
24469 * @param {Roo.data.Store} store
24471 Roo.bootstrap.PagingToolbar = function(config)
24473 // old args format still supported... - xtype is prefered..
24474 // created from xtype...
24476 this.ds = config.dataSource;
24478 if (config.store && !this.ds) {
24479 this.store= Roo.factory(config.store, Roo.data);
24480 this.ds = this.store;
24481 this.ds.xmodule = this.xmodule || false;
24484 this.toolbarItems = [];
24485 if (config.items) {
24486 this.toolbarItems = config.items;
24489 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24494 this.bind(this.ds);
24497 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24501 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24503 * @cfg {Roo.data.Store} dataSource
24504 * The underlying data store providing the paged data
24507 * @cfg {String/HTMLElement/Element} container
24508 * container The id or element that will contain the toolbar
24511 * @cfg {Boolean} displayInfo
24512 * True to display the displayMsg (defaults to false)
24515 * @cfg {Number} pageSize
24516 * The number of records to display per page (defaults to 20)
24520 * @cfg {String} displayMsg
24521 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24523 displayMsg : 'Displaying {0} - {1} of {2}',
24525 * @cfg {String} emptyMsg
24526 * The message to display when no records are found (defaults to "No data to display")
24528 emptyMsg : 'No data to display',
24530 * Customizable piece of the default paging text (defaults to "Page")
24533 beforePageText : "Page",
24535 * Customizable piece of the default paging text (defaults to "of %0")
24538 afterPageText : "of {0}",
24540 * Customizable piece of the default paging text (defaults to "First Page")
24543 firstText : "First Page",
24545 * Customizable piece of the default paging text (defaults to "Previous Page")
24548 prevText : "Previous Page",
24550 * Customizable piece of the default paging text (defaults to "Next Page")
24553 nextText : "Next Page",
24555 * Customizable piece of the default paging text (defaults to "Last Page")
24558 lastText : "Last Page",
24560 * Customizable piece of the default paging text (defaults to "Refresh")
24563 refreshText : "Refresh",
24567 onRender : function(ct, position)
24569 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24570 this.navgroup.parentId = this.id;
24571 this.navgroup.onRender(this.el, null);
24572 // add the buttons to the navgroup
24574 if(this.displayInfo){
24575 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24576 this.displayEl = this.el.select('.x-paging-info', true).first();
24577 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24578 // this.displayEl = navel.el.select('span',true).first();
24584 Roo.each(_this.buttons, function(e){ // this might need to use render????
24585 Roo.factory(e).render(_this.el);
24589 Roo.each(_this.toolbarItems, function(e) {
24590 _this.navgroup.addItem(e);
24594 this.first = this.navgroup.addItem({
24595 tooltip: this.firstText,
24597 icon : 'fa fa-backward',
24599 preventDefault: true,
24600 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24603 this.prev = this.navgroup.addItem({
24604 tooltip: this.prevText,
24606 icon : 'fa fa-step-backward',
24608 preventDefault: true,
24609 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24611 //this.addSeparator();
24614 var field = this.navgroup.addItem( {
24616 cls : 'x-paging-position',
24618 html : this.beforePageText +
24619 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24620 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24623 this.field = field.el.select('input', true).first();
24624 this.field.on("keydown", this.onPagingKeydown, this);
24625 this.field.on("focus", function(){this.dom.select();});
24628 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24629 //this.field.setHeight(18);
24630 //this.addSeparator();
24631 this.next = this.navgroup.addItem({
24632 tooltip: this.nextText,
24634 html : ' <i class="fa fa-step-forward">',
24636 preventDefault: true,
24637 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24639 this.last = this.navgroup.addItem({
24640 tooltip: this.lastText,
24641 icon : 'fa fa-forward',
24644 preventDefault: true,
24645 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24647 //this.addSeparator();
24648 this.loading = this.navgroup.addItem({
24649 tooltip: this.refreshText,
24650 icon: 'fa fa-refresh',
24651 preventDefault: true,
24652 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24658 updateInfo : function(){
24659 if(this.displayEl){
24660 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24661 var msg = count == 0 ?
24665 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24667 this.displayEl.update(msg);
24672 onLoad : function(ds, r, o)
24674 this.cursor = o.params.start ? o.params.start : 0;
24676 var d = this.getPageData(),
24681 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24682 this.field.dom.value = ap;
24683 this.first.setDisabled(ap == 1);
24684 this.prev.setDisabled(ap == 1);
24685 this.next.setDisabled(ap == ps);
24686 this.last.setDisabled(ap == ps);
24687 this.loading.enable();
24692 getPageData : function(){
24693 var total = this.ds.getTotalCount();
24696 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24697 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24702 onLoadError : function(){
24703 this.loading.enable();
24707 onPagingKeydown : function(e){
24708 var k = e.getKey();
24709 var d = this.getPageData();
24711 var v = this.field.dom.value, pageNum;
24712 if(!v || isNaN(pageNum = parseInt(v, 10))){
24713 this.field.dom.value = d.activePage;
24716 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24717 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24720 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))
24722 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24723 this.field.dom.value = pageNum;
24724 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24727 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24729 var v = this.field.dom.value, pageNum;
24730 var increment = (e.shiftKey) ? 10 : 1;
24731 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24734 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24735 this.field.dom.value = d.activePage;
24738 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24740 this.field.dom.value = parseInt(v, 10) + increment;
24741 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24742 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24749 beforeLoad : function(){
24751 this.loading.disable();
24756 onClick : function(which){
24765 ds.load({params:{start: 0, limit: this.pageSize}});
24768 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24771 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24774 var total = ds.getTotalCount();
24775 var extra = total % this.pageSize;
24776 var lastStart = extra ? (total - extra) : total-this.pageSize;
24777 ds.load({params:{start: lastStart, limit: this.pageSize}});
24780 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24786 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24787 * @param {Roo.data.Store} store The data store to unbind
24789 unbind : function(ds){
24790 ds.un("beforeload", this.beforeLoad, this);
24791 ds.un("load", this.onLoad, this);
24792 ds.un("loadexception", this.onLoadError, this);
24793 ds.un("remove", this.updateInfo, this);
24794 ds.un("add", this.updateInfo, this);
24795 this.ds = undefined;
24799 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24800 * @param {Roo.data.Store} store The data store to bind
24802 bind : function(ds){
24803 ds.on("beforeload", this.beforeLoad, this);
24804 ds.on("load", this.onLoad, this);
24805 ds.on("loadexception", this.onLoadError, this);
24806 ds.on("remove", this.updateInfo, this);
24807 ds.on("add", this.updateInfo, this);
24818 * @class Roo.bootstrap.MessageBar
24819 * @extends Roo.bootstrap.Component
24820 * Bootstrap MessageBar class
24821 * @cfg {String} html contents of the MessageBar
24822 * @cfg {String} weight (info | success | warning | danger) default info
24823 * @cfg {String} beforeClass insert the bar before the given class
24824 * @cfg {Boolean} closable (true | false) default false
24825 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24828 * Create a new Element
24829 * @param {Object} config The config object
24832 Roo.bootstrap.MessageBar = function(config){
24833 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24836 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24842 beforeClass: 'bootstrap-sticky-wrap',
24844 getAutoCreate : function(){
24848 cls: 'alert alert-dismissable alert-' + this.weight,
24853 html: this.html || ''
24859 cfg.cls += ' alert-messages-fixed';
24873 onRender : function(ct, position)
24875 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24878 var cfg = Roo.apply({}, this.getAutoCreate());
24882 cfg.cls += ' ' + this.cls;
24885 cfg.style = this.style;
24887 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24889 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24892 this.el.select('>button.close').on('click', this.hide, this);
24898 if (!this.rendered) {
24904 this.fireEvent('show', this);
24910 if (!this.rendered) {
24916 this.fireEvent('hide', this);
24919 update : function()
24921 // var e = this.el.dom.firstChild;
24923 // if(this.closable){
24924 // e = e.nextSibling;
24927 // e.data = this.html || '';
24929 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24945 * @class Roo.bootstrap.Graph
24946 * @extends Roo.bootstrap.Component
24947 * Bootstrap Graph class
24951 @cfg {String} graphtype bar | vbar | pie
24952 @cfg {number} g_x coodinator | centre x (pie)
24953 @cfg {number} g_y coodinator | centre y (pie)
24954 @cfg {number} g_r radius (pie)
24955 @cfg {number} g_height height of the chart (respected by all elements in the set)
24956 @cfg {number} g_width width of the chart (respected by all elements in the set)
24957 @cfg {Object} title The title of the chart
24960 -opts (object) options for the chart
24962 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24963 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24965 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.
24966 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24968 o stretch (boolean)
24970 -opts (object) options for the pie
24973 o startAngle (number)
24974 o endAngle (number)
24978 * Create a new Input
24979 * @param {Object} config The config object
24982 Roo.bootstrap.Graph = function(config){
24983 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24989 * The img click event for the img.
24990 * @param {Roo.EventObject} e
24996 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25007 //g_colors: this.colors,
25014 getAutoCreate : function(){
25025 onRender : function(ct,position){
25028 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25030 if (typeof(Raphael) == 'undefined') {
25031 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25035 this.raphael = Raphael(this.el.dom);
25037 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25038 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25039 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25040 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25042 r.text(160, 10, "Single Series Chart").attr(txtattr);
25043 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25044 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25045 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25047 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25048 r.barchart(330, 10, 300, 220, data1);
25049 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25050 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25053 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25054 // r.barchart(30, 30, 560, 250, xdata, {
25055 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25056 // axis : "0 0 1 1",
25057 // axisxlabels : xdata
25058 // //yvalues : cols,
25061 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25063 // this.load(null,xdata,{
25064 // axis : "0 0 1 1",
25065 // axisxlabels : xdata
25070 load : function(graphtype,xdata,opts)
25072 this.raphael.clear();
25074 graphtype = this.graphtype;
25079 var r = this.raphael,
25080 fin = function () {
25081 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25083 fout = function () {
25084 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25086 pfin = function() {
25087 this.sector.stop();
25088 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25091 this.label[0].stop();
25092 this.label[0].attr({ r: 7.5 });
25093 this.label[1].attr({ "font-weight": 800 });
25096 pfout = function() {
25097 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25100 this.label[0].animate({ r: 5 }, 500, "bounce");
25101 this.label[1].attr({ "font-weight": 400 });
25107 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25110 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25113 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25114 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25116 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25123 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25128 setTitle: function(o)
25133 initEvents: function() {
25136 this.el.on('click', this.onClick, this);
25140 onClick : function(e)
25142 Roo.log('img onclick');
25143 this.fireEvent('click', this, e);
25155 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25158 * @class Roo.bootstrap.dash.NumberBox
25159 * @extends Roo.bootstrap.Component
25160 * Bootstrap NumberBox class
25161 * @cfg {String} headline Box headline
25162 * @cfg {String} content Box content
25163 * @cfg {String} icon Box icon
25164 * @cfg {String} footer Footer text
25165 * @cfg {String} fhref Footer href
25168 * Create a new NumberBox
25169 * @param {Object} config The config object
25173 Roo.bootstrap.dash.NumberBox = function(config){
25174 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25178 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25187 getAutoCreate : function(){
25191 cls : 'small-box ',
25199 cls : 'roo-headline',
25200 html : this.headline
25204 cls : 'roo-content',
25205 html : this.content
25219 cls : 'ion ' + this.icon
25228 cls : 'small-box-footer',
25229 href : this.fhref || '#',
25233 cfg.cn.push(footer);
25240 onRender : function(ct,position){
25241 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25248 setHeadline: function (value)
25250 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25253 setFooter: function (value, href)
25255 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25258 this.el.select('a.small-box-footer',true).first().attr('href', href);
25263 setContent: function (value)
25265 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25268 initEvents: function()
25282 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25285 * @class Roo.bootstrap.dash.TabBox
25286 * @extends Roo.bootstrap.Component
25287 * Bootstrap TabBox class
25288 * @cfg {String} title Title of the TabBox
25289 * @cfg {String} icon Icon of the TabBox
25290 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25291 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25294 * Create a new TabBox
25295 * @param {Object} config The config object
25299 Roo.bootstrap.dash.TabBox = function(config){
25300 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25305 * When a pane is added
25306 * @param {Roo.bootstrap.dash.TabPane} pane
25310 * @event activatepane
25311 * When a pane is activated
25312 * @param {Roo.bootstrap.dash.TabPane} pane
25314 "activatepane" : true
25322 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25327 tabScrollable : false,
25329 getChildContainer : function()
25331 return this.el.select('.tab-content', true).first();
25334 getAutoCreate : function(){
25338 cls: 'pull-left header',
25346 cls: 'fa ' + this.icon
25352 cls: 'nav nav-tabs pull-right',
25358 if(this.tabScrollable){
25365 cls: 'nav nav-tabs pull-right',
25376 cls: 'nav-tabs-custom',
25381 cls: 'tab-content no-padding',
25389 initEvents : function()
25391 //Roo.log('add add pane handler');
25392 this.on('addpane', this.onAddPane, this);
25395 * Updates the box title
25396 * @param {String} html to set the title to.
25398 setTitle : function(value)
25400 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25402 onAddPane : function(pane)
25404 this.panes.push(pane);
25405 //Roo.log('addpane');
25407 // tabs are rendere left to right..
25408 if(!this.showtabs){
25412 var ctr = this.el.select('.nav-tabs', true).first();
25415 var existing = ctr.select('.nav-tab',true);
25416 var qty = existing.getCount();;
25419 var tab = ctr.createChild({
25421 cls : 'nav-tab' + (qty ? '' : ' active'),
25429 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25432 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25434 pane.el.addClass('active');
25439 onTabClick : function(ev,un,ob,pane)
25441 //Roo.log('tab - prev default');
25442 ev.preventDefault();
25445 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25446 pane.tab.addClass('active');
25447 //Roo.log(pane.title);
25448 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25449 // technically we should have a deactivate event.. but maybe add later.
25450 // and it should not de-activate the selected tab...
25451 this.fireEvent('activatepane', pane);
25452 pane.el.addClass('active');
25453 pane.fireEvent('activate');
25458 getActivePane : function()
25461 Roo.each(this.panes, function(p) {
25462 if(p.el.hasClass('active')){
25483 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25485 * @class Roo.bootstrap.TabPane
25486 * @extends Roo.bootstrap.Component
25487 * Bootstrap TabPane class
25488 * @cfg {Boolean} active (false | true) Default false
25489 * @cfg {String} title title of panel
25493 * Create a new TabPane
25494 * @param {Object} config The config object
25497 Roo.bootstrap.dash.TabPane = function(config){
25498 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25504 * When a pane is activated
25505 * @param {Roo.bootstrap.dash.TabPane} pane
25512 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25517 // the tabBox that this is attached to.
25520 getAutoCreate : function()
25528 cfg.cls += ' active';
25533 initEvents : function()
25535 //Roo.log('trigger add pane handler');
25536 this.parent().fireEvent('addpane', this)
25540 * Updates the tab title
25541 * @param {String} html to set the title to.
25543 setTitle: function(str)
25549 this.tab.select('a', true).first().dom.innerHTML = str;
25566 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25569 * @class Roo.bootstrap.menu.Menu
25570 * @extends Roo.bootstrap.Component
25571 * Bootstrap Menu class - container for Menu
25572 * @cfg {String} html Text of the menu
25573 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25574 * @cfg {String} icon Font awesome icon
25575 * @cfg {String} pos Menu align to (top | bottom) default bottom
25579 * Create a new Menu
25580 * @param {Object} config The config object
25584 Roo.bootstrap.menu.Menu = function(config){
25585 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25589 * @event beforeshow
25590 * Fires before this menu is displayed
25591 * @param {Roo.bootstrap.menu.Menu} this
25595 * @event beforehide
25596 * Fires before this menu is hidden
25597 * @param {Roo.bootstrap.menu.Menu} this
25602 * Fires after this menu is displayed
25603 * @param {Roo.bootstrap.menu.Menu} this
25608 * Fires after this menu is hidden
25609 * @param {Roo.bootstrap.menu.Menu} this
25614 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25615 * @param {Roo.bootstrap.menu.Menu} this
25616 * @param {Roo.EventObject} e
25623 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25627 weight : 'default',
25632 getChildContainer : function() {
25633 if(this.isSubMenu){
25637 return this.el.select('ul.dropdown-menu', true).first();
25640 getAutoCreate : function()
25645 cls : 'roo-menu-text',
25653 cls : 'fa ' + this.icon
25664 cls : 'dropdown-button btn btn-' + this.weight,
25669 cls : 'dropdown-toggle btn btn-' + this.weight,
25679 cls : 'dropdown-menu'
25685 if(this.pos == 'top'){
25686 cfg.cls += ' dropup';
25689 if(this.isSubMenu){
25692 cls : 'dropdown-menu'
25699 onRender : function(ct, position)
25701 this.isSubMenu = ct.hasClass('dropdown-submenu');
25703 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25706 initEvents : function()
25708 if(this.isSubMenu){
25712 this.hidden = true;
25714 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25715 this.triggerEl.on('click', this.onTriggerPress, this);
25717 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25718 this.buttonEl.on('click', this.onClick, this);
25724 if(this.isSubMenu){
25728 return this.el.select('ul.dropdown-menu', true).first();
25731 onClick : function(e)
25733 this.fireEvent("click", this, e);
25736 onTriggerPress : function(e)
25738 if (this.isVisible()) {
25745 isVisible : function(){
25746 return !this.hidden;
25751 this.fireEvent("beforeshow", this);
25753 this.hidden = false;
25754 this.el.addClass('open');
25756 Roo.get(document).on("mouseup", this.onMouseUp, this);
25758 this.fireEvent("show", this);
25765 this.fireEvent("beforehide", this);
25767 this.hidden = true;
25768 this.el.removeClass('open');
25770 Roo.get(document).un("mouseup", this.onMouseUp);
25772 this.fireEvent("hide", this);
25775 onMouseUp : function()
25789 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25792 * @class Roo.bootstrap.menu.Item
25793 * @extends Roo.bootstrap.Component
25794 * Bootstrap MenuItem class
25795 * @cfg {Boolean} submenu (true | false) default false
25796 * @cfg {String} html text of the item
25797 * @cfg {String} href the link
25798 * @cfg {Boolean} disable (true | false) default false
25799 * @cfg {Boolean} preventDefault (true | false) default true
25800 * @cfg {String} icon Font awesome icon
25801 * @cfg {String} pos Submenu align to (left | right) default right
25805 * Create a new Item
25806 * @param {Object} config The config object
25810 Roo.bootstrap.menu.Item = function(config){
25811 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25815 * Fires when the mouse is hovering over this menu
25816 * @param {Roo.bootstrap.menu.Item} this
25817 * @param {Roo.EventObject} e
25822 * Fires when the mouse exits this menu
25823 * @param {Roo.bootstrap.menu.Item} this
25824 * @param {Roo.EventObject} e
25830 * The raw click event for the entire grid.
25831 * @param {Roo.EventObject} e
25837 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25842 preventDefault: true,
25847 getAutoCreate : function()
25852 cls : 'roo-menu-item-text',
25860 cls : 'fa ' + this.icon
25869 href : this.href || '#',
25876 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25880 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25882 if(this.pos == 'left'){
25883 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25890 initEvents : function()
25892 this.el.on('mouseover', this.onMouseOver, this);
25893 this.el.on('mouseout', this.onMouseOut, this);
25895 this.el.select('a', true).first().on('click', this.onClick, this);
25899 onClick : function(e)
25901 if(this.preventDefault){
25902 e.preventDefault();
25905 this.fireEvent("click", this, e);
25908 onMouseOver : function(e)
25910 if(this.submenu && this.pos == 'left'){
25911 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25914 this.fireEvent("mouseover", this, e);
25917 onMouseOut : function(e)
25919 this.fireEvent("mouseout", this, e);
25931 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25934 * @class Roo.bootstrap.menu.Separator
25935 * @extends Roo.bootstrap.Component
25936 * Bootstrap Separator class
25939 * Create a new Separator
25940 * @param {Object} config The config object
25944 Roo.bootstrap.menu.Separator = function(config){
25945 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25948 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25950 getAutoCreate : function(){
25971 * @class Roo.bootstrap.Tooltip
25972 * Bootstrap Tooltip class
25973 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25974 * to determine which dom element triggers the tooltip.
25976 * It needs to add support for additional attributes like tooltip-position
25979 * Create a new Toolti
25980 * @param {Object} config The config object
25983 Roo.bootstrap.Tooltip = function(config){
25984 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25986 this.alignment = Roo.bootstrap.Tooltip.alignment;
25988 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25989 this.alignment = config.alignment;
25994 Roo.apply(Roo.bootstrap.Tooltip, {
25996 * @function init initialize tooltip monitoring.
26000 currentTip : false,
26001 currentRegion : false,
26007 Roo.get(document).on('mouseover', this.enter ,this);
26008 Roo.get(document).on('mouseout', this.leave, this);
26011 this.currentTip = new Roo.bootstrap.Tooltip();
26014 enter : function(ev)
26016 var dom = ev.getTarget();
26018 //Roo.log(['enter',dom]);
26019 var el = Roo.fly(dom);
26020 if (this.currentEl) {
26022 //Roo.log(this.currentEl);
26023 //Roo.log(this.currentEl.contains(dom));
26024 if (this.currentEl == el) {
26027 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26033 if (this.currentTip.el) {
26034 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26038 if(!el || el.dom == document){
26044 // you can not look for children, as if el is the body.. then everythign is the child..
26045 if (!el.attr('tooltip')) { //
26046 if (!el.select("[tooltip]").elements.length) {
26049 // is the mouse over this child...?
26050 bindEl = el.select("[tooltip]").first();
26051 var xy = ev.getXY();
26052 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26053 //Roo.log("not in region.");
26056 //Roo.log("child element over..");
26059 this.currentEl = bindEl;
26060 this.currentTip.bind(bindEl);
26061 this.currentRegion = Roo.lib.Region.getRegion(dom);
26062 this.currentTip.enter();
26065 leave : function(ev)
26067 var dom = ev.getTarget();
26068 //Roo.log(['leave',dom]);
26069 if (!this.currentEl) {
26074 if (dom != this.currentEl.dom) {
26077 var xy = ev.getXY();
26078 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26081 // only activate leave if mouse cursor is outside... bounding box..
26086 if (this.currentTip) {
26087 this.currentTip.leave();
26089 //Roo.log('clear currentEl');
26090 this.currentEl = false;
26095 'left' : ['r-l', [-2,0], 'right'],
26096 'right' : ['l-r', [2,0], 'left'],
26097 'bottom' : ['t-b', [0,2], 'top'],
26098 'top' : [ 'b-t', [0,-2], 'bottom']
26104 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26109 delay : null, // can be { show : 300 , hide: 500}
26113 hoverState : null, //???
26115 placement : 'bottom',
26119 getAutoCreate : function(){
26126 cls : 'tooltip-arrow'
26129 cls : 'tooltip-inner'
26136 bind : function(el)
26142 enter : function () {
26144 if (this.timeout != null) {
26145 clearTimeout(this.timeout);
26148 this.hoverState = 'in';
26149 //Roo.log("enter - show");
26150 if (!this.delay || !this.delay.show) {
26155 this.timeout = setTimeout(function () {
26156 if (_t.hoverState == 'in') {
26159 }, this.delay.show);
26163 clearTimeout(this.timeout);
26165 this.hoverState = 'out';
26166 if (!this.delay || !this.delay.hide) {
26172 this.timeout = setTimeout(function () {
26173 //Roo.log("leave - timeout");
26175 if (_t.hoverState == 'out') {
26177 Roo.bootstrap.Tooltip.currentEl = false;
26182 show : function (msg)
26185 this.render(document.body);
26188 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26190 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26192 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26194 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26196 var placement = typeof this.placement == 'function' ?
26197 this.placement.call(this, this.el, on_el) :
26200 var autoToken = /\s?auto?\s?/i;
26201 var autoPlace = autoToken.test(placement);
26203 placement = placement.replace(autoToken, '') || 'top';
26207 //this.el.setXY([0,0]);
26209 //this.el.dom.style.display='block';
26211 //this.el.appendTo(on_el);
26213 var p = this.getPosition();
26214 var box = this.el.getBox();
26220 var align = this.alignment[placement];
26222 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26224 if(placement == 'top' || placement == 'bottom'){
26226 placement = 'right';
26229 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26230 placement = 'left';
26233 var scroll = Roo.select('body', true).first().getScroll();
26235 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26239 align = this.alignment[placement];
26242 this.el.alignTo(this.bindEl, align[0],align[1]);
26243 //var arrow = this.el.select('.arrow',true).first();
26244 //arrow.set(align[2],
26246 this.el.addClass(placement);
26248 this.el.addClass('in fade');
26250 this.hoverState = null;
26252 if (this.el.hasClass('fade')) {
26263 //this.el.setXY([0,0]);
26264 this.el.removeClass('in');
26280 * @class Roo.bootstrap.LocationPicker
26281 * @extends Roo.bootstrap.Component
26282 * Bootstrap LocationPicker class
26283 * @cfg {Number} latitude Position when init default 0
26284 * @cfg {Number} longitude Position when init default 0
26285 * @cfg {Number} zoom default 15
26286 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26287 * @cfg {Boolean} mapTypeControl default false
26288 * @cfg {Boolean} disableDoubleClickZoom default false
26289 * @cfg {Boolean} scrollwheel default true
26290 * @cfg {Boolean} streetViewControl default false
26291 * @cfg {Number} radius default 0
26292 * @cfg {String} locationName
26293 * @cfg {Boolean} draggable default true
26294 * @cfg {Boolean} enableAutocomplete default false
26295 * @cfg {Boolean} enableReverseGeocode default true
26296 * @cfg {String} markerTitle
26299 * Create a new LocationPicker
26300 * @param {Object} config The config object
26304 Roo.bootstrap.LocationPicker = function(config){
26306 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26311 * Fires when the picker initialized.
26312 * @param {Roo.bootstrap.LocationPicker} this
26313 * @param {Google Location} location
26317 * @event positionchanged
26318 * Fires when the picker position changed.
26319 * @param {Roo.bootstrap.LocationPicker} this
26320 * @param {Google Location} location
26322 positionchanged : true,
26325 * Fires when the map resize.
26326 * @param {Roo.bootstrap.LocationPicker} this
26331 * Fires when the map show.
26332 * @param {Roo.bootstrap.LocationPicker} this
26337 * Fires when the map hide.
26338 * @param {Roo.bootstrap.LocationPicker} this
26343 * Fires when click the map.
26344 * @param {Roo.bootstrap.LocationPicker} this
26345 * @param {Map event} e
26349 * @event mapRightClick
26350 * Fires when right click the map.
26351 * @param {Roo.bootstrap.LocationPicker} this
26352 * @param {Map event} e
26354 mapRightClick : true,
26356 * @event markerClick
26357 * Fires when click the marker.
26358 * @param {Roo.bootstrap.LocationPicker} this
26359 * @param {Map event} e
26361 markerClick : true,
26363 * @event markerRightClick
26364 * Fires when right click the marker.
26365 * @param {Roo.bootstrap.LocationPicker} this
26366 * @param {Map event} e
26368 markerRightClick : true,
26370 * @event OverlayViewDraw
26371 * Fires when OverlayView Draw
26372 * @param {Roo.bootstrap.LocationPicker} this
26374 OverlayViewDraw : true,
26376 * @event OverlayViewOnAdd
26377 * Fires when OverlayView Draw
26378 * @param {Roo.bootstrap.LocationPicker} this
26380 OverlayViewOnAdd : true,
26382 * @event OverlayViewOnRemove
26383 * Fires when OverlayView Draw
26384 * @param {Roo.bootstrap.LocationPicker} this
26386 OverlayViewOnRemove : true,
26388 * @event OverlayViewShow
26389 * Fires when OverlayView Draw
26390 * @param {Roo.bootstrap.LocationPicker} this
26391 * @param {Pixel} cpx
26393 OverlayViewShow : true,
26395 * @event OverlayViewHide
26396 * Fires when OverlayView Draw
26397 * @param {Roo.bootstrap.LocationPicker} this
26399 OverlayViewHide : true,
26401 * @event loadexception
26402 * Fires when load google lib failed.
26403 * @param {Roo.bootstrap.LocationPicker} this
26405 loadexception : true
26410 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26412 gMapContext: false,
26418 mapTypeControl: false,
26419 disableDoubleClickZoom: false,
26421 streetViewControl: false,
26425 enableAutocomplete: false,
26426 enableReverseGeocode: true,
26429 getAutoCreate: function()
26434 cls: 'roo-location-picker'
26440 initEvents: function(ct, position)
26442 if(!this.el.getWidth() || this.isApplied()){
26446 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26451 initial: function()
26453 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26454 this.fireEvent('loadexception', this);
26458 if(!this.mapTypeId){
26459 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26462 this.gMapContext = this.GMapContext();
26464 this.initOverlayView();
26466 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26470 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26471 _this.setPosition(_this.gMapContext.marker.position);
26474 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26475 _this.fireEvent('mapClick', this, event);
26479 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26480 _this.fireEvent('mapRightClick', this, event);
26484 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26485 _this.fireEvent('markerClick', this, event);
26489 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26490 _this.fireEvent('markerRightClick', this, event);
26494 this.setPosition(this.gMapContext.location);
26496 this.fireEvent('initial', this, this.gMapContext.location);
26499 initOverlayView: function()
26503 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26507 _this.fireEvent('OverlayViewDraw', _this);
26512 _this.fireEvent('OverlayViewOnAdd', _this);
26515 onRemove: function()
26517 _this.fireEvent('OverlayViewOnRemove', _this);
26520 show: function(cpx)
26522 _this.fireEvent('OverlayViewShow', _this, cpx);
26527 _this.fireEvent('OverlayViewHide', _this);
26533 fromLatLngToContainerPixel: function(event)
26535 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26538 isApplied: function()
26540 return this.getGmapContext() == false ? false : true;
26543 getGmapContext: function()
26545 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26548 GMapContext: function()
26550 var position = new google.maps.LatLng(this.latitude, this.longitude);
26552 var _map = new google.maps.Map(this.el.dom, {
26555 mapTypeId: this.mapTypeId,
26556 mapTypeControl: this.mapTypeControl,
26557 disableDoubleClickZoom: this.disableDoubleClickZoom,
26558 scrollwheel: this.scrollwheel,
26559 streetViewControl: this.streetViewControl,
26560 locationName: this.locationName,
26561 draggable: this.draggable,
26562 enableAutocomplete: this.enableAutocomplete,
26563 enableReverseGeocode: this.enableReverseGeocode
26566 var _marker = new google.maps.Marker({
26567 position: position,
26569 title: this.markerTitle,
26570 draggable: this.draggable
26577 location: position,
26578 radius: this.radius,
26579 locationName: this.locationName,
26580 addressComponents: {
26581 formatted_address: null,
26582 addressLine1: null,
26583 addressLine2: null,
26585 streetNumber: null,
26589 stateOrProvince: null
26592 domContainer: this.el.dom,
26593 geodecoder: new google.maps.Geocoder()
26597 drawCircle: function(center, radius, options)
26599 if (this.gMapContext.circle != null) {
26600 this.gMapContext.circle.setMap(null);
26604 options = Roo.apply({}, options, {
26605 strokeColor: "#0000FF",
26606 strokeOpacity: .35,
26608 fillColor: "#0000FF",
26612 options.map = this.gMapContext.map;
26613 options.radius = radius;
26614 options.center = center;
26615 this.gMapContext.circle = new google.maps.Circle(options);
26616 return this.gMapContext.circle;
26622 setPosition: function(location)
26624 this.gMapContext.location = location;
26625 this.gMapContext.marker.setPosition(location);
26626 this.gMapContext.map.panTo(location);
26627 this.drawCircle(location, this.gMapContext.radius, {});
26631 if (this.gMapContext.settings.enableReverseGeocode) {
26632 this.gMapContext.geodecoder.geocode({
26633 latLng: this.gMapContext.location
26634 }, function(results, status) {
26636 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26637 _this.gMapContext.locationName = results[0].formatted_address;
26638 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26640 _this.fireEvent('positionchanged', this, location);
26647 this.fireEvent('positionchanged', this, location);
26652 google.maps.event.trigger(this.gMapContext.map, "resize");
26654 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26656 this.fireEvent('resize', this);
26659 setPositionByLatLng: function(latitude, longitude)
26661 this.setPosition(new google.maps.LatLng(latitude, longitude));
26664 getCurrentPosition: function()
26667 latitude: this.gMapContext.location.lat(),
26668 longitude: this.gMapContext.location.lng()
26672 getAddressName: function()
26674 return this.gMapContext.locationName;
26677 getAddressComponents: function()
26679 return this.gMapContext.addressComponents;
26682 address_component_from_google_geocode: function(address_components)
26686 for (var i = 0; i < address_components.length; i++) {
26687 var component = address_components[i];
26688 if (component.types.indexOf("postal_code") >= 0) {
26689 result.postalCode = component.short_name;
26690 } else if (component.types.indexOf("street_number") >= 0) {
26691 result.streetNumber = component.short_name;
26692 } else if (component.types.indexOf("route") >= 0) {
26693 result.streetName = component.short_name;
26694 } else if (component.types.indexOf("neighborhood") >= 0) {
26695 result.city = component.short_name;
26696 } else if (component.types.indexOf("locality") >= 0) {
26697 result.city = component.short_name;
26698 } else if (component.types.indexOf("sublocality") >= 0) {
26699 result.district = component.short_name;
26700 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26701 result.stateOrProvince = component.short_name;
26702 } else if (component.types.indexOf("country") >= 0) {
26703 result.country = component.short_name;
26707 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26708 result.addressLine2 = "";
26712 setZoomLevel: function(zoom)
26714 this.gMapContext.map.setZoom(zoom);
26727 this.fireEvent('show', this);
26738 this.fireEvent('hide', this);
26743 Roo.apply(Roo.bootstrap.LocationPicker, {
26745 OverlayView : function(map, options)
26747 options = options || {};
26761 * @class Roo.bootstrap.Alert
26762 * @extends Roo.bootstrap.Component
26763 * Bootstrap Alert class
26764 * @cfg {String} title The title of alert
26765 * @cfg {String} html The content of alert
26766 * @cfg {String} weight ( success | info | warning | danger )
26767 * @cfg {String} faicon font-awesomeicon
26770 * Create a new alert
26771 * @param {Object} config The config object
26775 Roo.bootstrap.Alert = function(config){
26776 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26780 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26787 getAutoCreate : function()
26796 cls : 'roo-alert-icon'
26801 cls : 'roo-alert-title',
26806 cls : 'roo-alert-text',
26813 cfg.cn[0].cls += ' fa ' + this.faicon;
26817 cfg.cls += ' alert-' + this.weight;
26823 initEvents: function()
26825 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26828 setTitle : function(str)
26830 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26833 setText : function(str)
26835 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26838 setWeight : function(weight)
26841 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26844 this.weight = weight;
26846 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26849 setIcon : function(icon)
26852 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26855 this.faicon = icon;
26857 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26878 * @class Roo.bootstrap.UploadCropbox
26879 * @extends Roo.bootstrap.Component
26880 * Bootstrap UploadCropbox class
26881 * @cfg {String} emptyText show when image has been loaded
26882 * @cfg {String} rotateNotify show when image too small to rotate
26883 * @cfg {Number} errorTimeout default 3000
26884 * @cfg {Number} minWidth default 300
26885 * @cfg {Number} minHeight default 300
26886 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26887 * @cfg {Boolean} isDocument (true|false) default false
26888 * @cfg {String} url action url
26889 * @cfg {String} paramName default 'imageUpload'
26890 * @cfg {String} method default POST
26891 * @cfg {Boolean} loadMask (true|false) default true
26892 * @cfg {Boolean} loadingText default 'Loading...'
26895 * Create a new UploadCropbox
26896 * @param {Object} config The config object
26899 Roo.bootstrap.UploadCropbox = function(config){
26900 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26904 * @event beforeselectfile
26905 * Fire before select file
26906 * @param {Roo.bootstrap.UploadCropbox} this
26908 "beforeselectfile" : true,
26911 * Fire after initEvent
26912 * @param {Roo.bootstrap.UploadCropbox} this
26917 * Fire after initEvent
26918 * @param {Roo.bootstrap.UploadCropbox} this
26919 * @param {String} data
26924 * Fire when preparing the file data
26925 * @param {Roo.bootstrap.UploadCropbox} this
26926 * @param {Object} file
26931 * Fire when get exception
26932 * @param {Roo.bootstrap.UploadCropbox} this
26933 * @param {XMLHttpRequest} xhr
26935 "exception" : true,
26937 * @event beforeloadcanvas
26938 * Fire before load the canvas
26939 * @param {Roo.bootstrap.UploadCropbox} this
26940 * @param {String} src
26942 "beforeloadcanvas" : true,
26945 * Fire when trash image
26946 * @param {Roo.bootstrap.UploadCropbox} this
26951 * Fire when download the image
26952 * @param {Roo.bootstrap.UploadCropbox} this
26956 * @event footerbuttonclick
26957 * Fire when footerbuttonclick
26958 * @param {Roo.bootstrap.UploadCropbox} this
26959 * @param {String} type
26961 "footerbuttonclick" : true,
26965 * @param {Roo.bootstrap.UploadCropbox} this
26970 * Fire when rotate the image
26971 * @param {Roo.bootstrap.UploadCropbox} this
26972 * @param {String} pos
26977 * Fire when inspect the file
26978 * @param {Roo.bootstrap.UploadCropbox} this
26979 * @param {Object} file
26984 * Fire when xhr upload the file
26985 * @param {Roo.bootstrap.UploadCropbox} this
26986 * @param {Object} data
26991 * Fire when arrange the file data
26992 * @param {Roo.bootstrap.UploadCropbox} this
26993 * @param {Object} formData
26998 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27001 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27003 emptyText : 'Click to upload image',
27004 rotateNotify : 'Image is too small to rotate',
27005 errorTimeout : 3000,
27019 cropType : 'image/jpeg',
27021 canvasLoaded : false,
27022 isDocument : false,
27024 paramName : 'imageUpload',
27026 loadingText : 'Loading...',
27029 getAutoCreate : function()
27033 cls : 'roo-upload-cropbox',
27037 cls : 'roo-upload-cropbox-selector',
27042 cls : 'roo-upload-cropbox-body',
27043 style : 'cursor:pointer',
27047 cls : 'roo-upload-cropbox-preview'
27051 cls : 'roo-upload-cropbox-thumb'
27055 cls : 'roo-upload-cropbox-empty-notify',
27056 html : this.emptyText
27060 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27061 html : this.rotateNotify
27067 cls : 'roo-upload-cropbox-footer',
27070 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27080 onRender : function(ct, position)
27082 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27084 if (this.buttons.length) {
27086 Roo.each(this.buttons, function(bb) {
27088 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27090 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27096 this.maskEl = this.el;
27100 initEvents : function()
27102 this.urlAPI = (window.createObjectURL && window) ||
27103 (window.URL && URL.revokeObjectURL && URL) ||
27104 (window.webkitURL && webkitURL);
27106 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27107 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27109 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27110 this.selectorEl.hide();
27112 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27113 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27115 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27116 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27117 this.thumbEl.hide();
27119 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27120 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27122 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27123 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27124 this.errorEl.hide();
27126 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27127 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27128 this.footerEl.hide();
27130 this.setThumbBoxSize();
27136 this.fireEvent('initial', this);
27143 window.addEventListener("resize", function() { _this.resize(); } );
27145 this.bodyEl.on('click', this.beforeSelectFile, this);
27148 this.bodyEl.on('touchstart', this.onTouchStart, this);
27149 this.bodyEl.on('touchmove', this.onTouchMove, this);
27150 this.bodyEl.on('touchend', this.onTouchEnd, this);
27154 this.bodyEl.on('mousedown', this.onMouseDown, this);
27155 this.bodyEl.on('mousemove', this.onMouseMove, this);
27156 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27157 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27158 Roo.get(document).on('mouseup', this.onMouseUp, this);
27161 this.selectorEl.on('change', this.onFileSelected, this);
27167 this.baseScale = 1;
27169 this.baseRotate = 1;
27170 this.dragable = false;
27171 this.pinching = false;
27174 this.cropData = false;
27175 this.notifyEl.dom.innerHTML = this.emptyText;
27177 this.selectorEl.dom.value = '';
27181 resize : function()
27183 if(this.fireEvent('resize', this) != false){
27184 this.setThumbBoxPosition();
27185 this.setCanvasPosition();
27189 onFooterButtonClick : function(e, el, o, type)
27192 case 'rotate-left' :
27193 this.onRotateLeft(e);
27195 case 'rotate-right' :
27196 this.onRotateRight(e);
27199 this.beforeSelectFile(e);
27214 this.fireEvent('footerbuttonclick', this, type);
27217 beforeSelectFile : function(e)
27219 e.preventDefault();
27221 if(this.fireEvent('beforeselectfile', this) != false){
27222 this.selectorEl.dom.click();
27226 onFileSelected : function(e)
27228 e.preventDefault();
27230 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27234 var file = this.selectorEl.dom.files[0];
27236 if(this.fireEvent('inspect', this, file) != false){
27237 this.prepare(file);
27242 trash : function(e)
27244 this.fireEvent('trash', this);
27247 download : function(e)
27249 this.fireEvent('download', this);
27252 loadCanvas : function(src)
27254 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27258 this.imageEl = document.createElement('img');
27262 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27264 this.imageEl.src = src;
27268 onLoadCanvas : function()
27270 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27271 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27273 this.bodyEl.un('click', this.beforeSelectFile, this);
27275 this.notifyEl.hide();
27276 this.thumbEl.show();
27277 this.footerEl.show();
27279 this.baseRotateLevel();
27281 if(this.isDocument){
27282 this.setThumbBoxSize();
27285 this.setThumbBoxPosition();
27287 this.baseScaleLevel();
27293 this.canvasLoaded = true;
27296 this.maskEl.unmask();
27301 setCanvasPosition : function()
27303 if(!this.canvasEl){
27307 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27308 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27310 this.previewEl.setLeft(pw);
27311 this.previewEl.setTop(ph);
27315 onMouseDown : function(e)
27319 this.dragable = true;
27320 this.pinching = false;
27322 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27323 this.dragable = false;
27327 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27328 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27332 onMouseMove : function(e)
27336 if(!this.canvasLoaded){
27340 if (!this.dragable){
27344 var minX = Math.ceil(this.thumbEl.getLeft(true));
27345 var minY = Math.ceil(this.thumbEl.getTop(true));
27347 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27348 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27350 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27351 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27353 x = x - this.mouseX;
27354 y = y - this.mouseY;
27356 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27357 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27359 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27360 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27362 this.previewEl.setLeft(bgX);
27363 this.previewEl.setTop(bgY);
27365 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27366 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27369 onMouseUp : function(e)
27373 this.dragable = false;
27376 onMouseWheel : function(e)
27380 this.startScale = this.scale;
27382 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27384 if(!this.zoomable()){
27385 this.scale = this.startScale;
27394 zoomable : function()
27396 var minScale = this.thumbEl.getWidth() / this.minWidth;
27398 if(this.minWidth < this.minHeight){
27399 minScale = this.thumbEl.getHeight() / this.minHeight;
27402 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27403 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27407 (this.rotate == 0 || this.rotate == 180) &&
27409 width > this.imageEl.OriginWidth ||
27410 height > this.imageEl.OriginHeight ||
27411 (width < this.minWidth && height < this.minHeight)
27419 (this.rotate == 90 || this.rotate == 270) &&
27421 width > this.imageEl.OriginWidth ||
27422 height > this.imageEl.OriginHeight ||
27423 (width < this.minHeight && height < this.minWidth)
27430 !this.isDocument &&
27431 (this.rotate == 0 || this.rotate == 180) &&
27433 width < this.minWidth ||
27434 width > this.imageEl.OriginWidth ||
27435 height < this.minHeight ||
27436 height > this.imageEl.OriginHeight
27443 !this.isDocument &&
27444 (this.rotate == 90 || this.rotate == 270) &&
27446 width < this.minHeight ||
27447 width > this.imageEl.OriginWidth ||
27448 height < this.minWidth ||
27449 height > this.imageEl.OriginHeight
27459 onRotateLeft : function(e)
27461 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27463 var minScale = this.thumbEl.getWidth() / this.minWidth;
27465 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27466 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27468 this.startScale = this.scale;
27470 while (this.getScaleLevel() < minScale){
27472 this.scale = this.scale + 1;
27474 if(!this.zoomable()){
27479 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27480 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27485 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27492 this.scale = this.startScale;
27494 this.onRotateFail();
27499 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27501 if(this.isDocument){
27502 this.setThumbBoxSize();
27503 this.setThumbBoxPosition();
27504 this.setCanvasPosition();
27509 this.fireEvent('rotate', this, 'left');
27513 onRotateRight : function(e)
27515 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27517 var minScale = this.thumbEl.getWidth() / this.minWidth;
27519 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27520 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27522 this.startScale = this.scale;
27524 while (this.getScaleLevel() < minScale){
27526 this.scale = this.scale + 1;
27528 if(!this.zoomable()){
27533 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27534 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27539 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27546 this.scale = this.startScale;
27548 this.onRotateFail();
27553 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27555 if(this.isDocument){
27556 this.setThumbBoxSize();
27557 this.setThumbBoxPosition();
27558 this.setCanvasPosition();
27563 this.fireEvent('rotate', this, 'right');
27566 onRotateFail : function()
27568 this.errorEl.show(true);
27572 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27577 this.previewEl.dom.innerHTML = '';
27579 var canvasEl = document.createElement("canvas");
27581 var contextEl = canvasEl.getContext("2d");
27583 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27584 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27585 var center = this.imageEl.OriginWidth / 2;
27587 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27588 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27589 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27590 center = this.imageEl.OriginHeight / 2;
27593 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27595 contextEl.translate(center, center);
27596 contextEl.rotate(this.rotate * Math.PI / 180);
27598 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27600 this.canvasEl = document.createElement("canvas");
27602 this.contextEl = this.canvasEl.getContext("2d");
27604 switch (this.rotate) {
27607 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27608 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27610 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27615 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27616 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27618 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27619 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);
27623 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27628 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27629 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27631 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27632 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);
27636 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);
27641 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27642 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27644 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27645 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27649 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);
27656 this.previewEl.appendChild(this.canvasEl);
27658 this.setCanvasPosition();
27663 if(!this.canvasLoaded){
27667 var imageCanvas = document.createElement("canvas");
27669 var imageContext = imageCanvas.getContext("2d");
27671 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27672 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27674 var center = imageCanvas.width / 2;
27676 imageContext.translate(center, center);
27678 imageContext.rotate(this.rotate * Math.PI / 180);
27680 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27682 var canvas = document.createElement("canvas");
27684 var context = canvas.getContext("2d");
27686 canvas.width = this.minWidth;
27687 canvas.height = this.minHeight;
27689 switch (this.rotate) {
27692 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27693 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27695 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27696 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27698 var targetWidth = this.minWidth - 2 * x;
27699 var targetHeight = this.minHeight - 2 * y;
27703 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27704 scale = targetWidth / width;
27707 if(x > 0 && y == 0){
27708 scale = targetHeight / height;
27711 if(x > 0 && y > 0){
27712 scale = targetWidth / width;
27714 if(width < height){
27715 scale = targetHeight / height;
27719 context.scale(scale, scale);
27721 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27722 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27724 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27725 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27727 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27732 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27733 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27735 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27736 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27738 var targetWidth = this.minWidth - 2 * x;
27739 var targetHeight = this.minHeight - 2 * y;
27743 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27744 scale = targetWidth / width;
27747 if(x > 0 && y == 0){
27748 scale = targetHeight / height;
27751 if(x > 0 && y > 0){
27752 scale = targetWidth / width;
27754 if(width < height){
27755 scale = targetHeight / height;
27759 context.scale(scale, scale);
27761 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27762 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27764 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27765 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27767 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27769 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27774 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27775 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27777 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27778 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27780 var targetWidth = this.minWidth - 2 * x;
27781 var targetHeight = this.minHeight - 2 * y;
27785 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27786 scale = targetWidth / width;
27789 if(x > 0 && y == 0){
27790 scale = targetHeight / height;
27793 if(x > 0 && y > 0){
27794 scale = targetWidth / width;
27796 if(width < height){
27797 scale = targetHeight / height;
27801 context.scale(scale, scale);
27803 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27804 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27806 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27807 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27809 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27810 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27812 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27817 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27818 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27820 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27821 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27823 var targetWidth = this.minWidth - 2 * x;
27824 var targetHeight = this.minHeight - 2 * y;
27828 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27829 scale = targetWidth / width;
27832 if(x > 0 && y == 0){
27833 scale = targetHeight / height;
27836 if(x > 0 && y > 0){
27837 scale = targetWidth / width;
27839 if(width < height){
27840 scale = targetHeight / height;
27844 context.scale(scale, scale);
27846 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27847 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27849 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27850 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27852 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27854 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27861 this.cropData = canvas.toDataURL(this.cropType);
27863 if(this.fireEvent('crop', this, this.cropData) !== false){
27864 this.process(this.file, this.cropData);
27871 setThumbBoxSize : function()
27875 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27876 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27877 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27879 this.minWidth = width;
27880 this.minHeight = height;
27882 if(this.rotate == 90 || this.rotate == 270){
27883 this.minWidth = height;
27884 this.minHeight = width;
27889 width = Math.ceil(this.minWidth * height / this.minHeight);
27891 if(this.minWidth > this.minHeight){
27893 height = Math.ceil(this.minHeight * width / this.minWidth);
27896 this.thumbEl.setStyle({
27897 width : width + 'px',
27898 height : height + 'px'
27905 setThumbBoxPosition : function()
27907 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27908 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27910 this.thumbEl.setLeft(x);
27911 this.thumbEl.setTop(y);
27915 baseRotateLevel : function()
27917 this.baseRotate = 1;
27920 typeof(this.exif) != 'undefined' &&
27921 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27922 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27924 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27927 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27931 baseScaleLevel : function()
27935 if(this.isDocument){
27937 if(this.baseRotate == 6 || this.baseRotate == 8){
27939 height = this.thumbEl.getHeight();
27940 this.baseScale = height / this.imageEl.OriginWidth;
27942 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27943 width = this.thumbEl.getWidth();
27944 this.baseScale = width / this.imageEl.OriginHeight;
27950 height = this.thumbEl.getHeight();
27951 this.baseScale = height / this.imageEl.OriginHeight;
27953 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27954 width = this.thumbEl.getWidth();
27955 this.baseScale = width / this.imageEl.OriginWidth;
27961 if(this.baseRotate == 6 || this.baseRotate == 8){
27963 width = this.thumbEl.getHeight();
27964 this.baseScale = width / this.imageEl.OriginHeight;
27966 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27967 height = this.thumbEl.getWidth();
27968 this.baseScale = height / this.imageEl.OriginHeight;
27971 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27972 height = this.thumbEl.getWidth();
27973 this.baseScale = height / this.imageEl.OriginHeight;
27975 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27976 width = this.thumbEl.getHeight();
27977 this.baseScale = width / this.imageEl.OriginWidth;
27984 width = this.thumbEl.getWidth();
27985 this.baseScale = width / this.imageEl.OriginWidth;
27987 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27988 height = this.thumbEl.getHeight();
27989 this.baseScale = height / this.imageEl.OriginHeight;
27992 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27994 height = this.thumbEl.getHeight();
27995 this.baseScale = height / this.imageEl.OriginHeight;
27997 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27998 width = this.thumbEl.getWidth();
27999 this.baseScale = width / this.imageEl.OriginWidth;
28007 getScaleLevel : function()
28009 return this.baseScale * Math.pow(1.1, this.scale);
28012 onTouchStart : function(e)
28014 if(!this.canvasLoaded){
28015 this.beforeSelectFile(e);
28019 var touches = e.browserEvent.touches;
28025 if(touches.length == 1){
28026 this.onMouseDown(e);
28030 if(touches.length != 2){
28036 for(var i = 0, finger; finger = touches[i]; i++){
28037 coords.push(finger.pageX, finger.pageY);
28040 var x = Math.pow(coords[0] - coords[2], 2);
28041 var y = Math.pow(coords[1] - coords[3], 2);
28043 this.startDistance = Math.sqrt(x + y);
28045 this.startScale = this.scale;
28047 this.pinching = true;
28048 this.dragable = false;
28052 onTouchMove : function(e)
28054 if(!this.pinching && !this.dragable){
28058 var touches = e.browserEvent.touches;
28065 this.onMouseMove(e);
28071 for(var i = 0, finger; finger = touches[i]; i++){
28072 coords.push(finger.pageX, finger.pageY);
28075 var x = Math.pow(coords[0] - coords[2], 2);
28076 var y = Math.pow(coords[1] - coords[3], 2);
28078 this.endDistance = Math.sqrt(x + y);
28080 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28082 if(!this.zoomable()){
28083 this.scale = this.startScale;
28091 onTouchEnd : function(e)
28093 this.pinching = false;
28094 this.dragable = false;
28098 process : function(file, crop)
28101 this.maskEl.mask(this.loadingText);
28104 this.xhr = new XMLHttpRequest();
28106 file.xhr = this.xhr;
28108 this.xhr.open(this.method, this.url, true);
28111 "Accept": "application/json",
28112 "Cache-Control": "no-cache",
28113 "X-Requested-With": "XMLHttpRequest"
28116 for (var headerName in headers) {
28117 var headerValue = headers[headerName];
28119 this.xhr.setRequestHeader(headerName, headerValue);
28125 this.xhr.onload = function()
28127 _this.xhrOnLoad(_this.xhr);
28130 this.xhr.onerror = function()
28132 _this.xhrOnError(_this.xhr);
28135 var formData = new FormData();
28137 formData.append('returnHTML', 'NO');
28140 formData.append('crop', crop);
28143 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28144 formData.append(this.paramName, file, file.name);
28147 if(typeof(file.filename) != 'undefined'){
28148 formData.append('filename', file.filename);
28151 if(typeof(file.mimetype) != 'undefined'){
28152 formData.append('mimetype', file.mimetype);
28155 if(this.fireEvent('arrange', this, formData) != false){
28156 this.xhr.send(formData);
28160 xhrOnLoad : function(xhr)
28163 this.maskEl.unmask();
28166 if (xhr.readyState !== 4) {
28167 this.fireEvent('exception', this, xhr);
28171 var response = Roo.decode(xhr.responseText);
28173 if(!response.success){
28174 this.fireEvent('exception', this, xhr);
28178 var response = Roo.decode(xhr.responseText);
28180 this.fireEvent('upload', this, response);
28184 xhrOnError : function()
28187 this.maskEl.unmask();
28190 Roo.log('xhr on error');
28192 var response = Roo.decode(xhr.responseText);
28198 prepare : function(file)
28201 this.maskEl.mask(this.loadingText);
28207 if(typeof(file) === 'string'){
28208 this.loadCanvas(file);
28212 if(!file || !this.urlAPI){
28217 this.cropType = file.type;
28221 if(this.fireEvent('prepare', this, this.file) != false){
28223 var reader = new FileReader();
28225 reader.onload = function (e) {
28226 if (e.target.error) {
28227 Roo.log(e.target.error);
28231 var buffer = e.target.result,
28232 dataView = new DataView(buffer),
28234 maxOffset = dataView.byteLength - 4,
28238 if (dataView.getUint16(0) === 0xffd8) {
28239 while (offset < maxOffset) {
28240 markerBytes = dataView.getUint16(offset);
28242 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28243 markerLength = dataView.getUint16(offset + 2) + 2;
28244 if (offset + markerLength > dataView.byteLength) {
28245 Roo.log('Invalid meta data: Invalid segment size.');
28249 if(markerBytes == 0xffe1){
28250 _this.parseExifData(
28257 offset += markerLength;
28267 var url = _this.urlAPI.createObjectURL(_this.file);
28269 _this.loadCanvas(url);
28274 reader.readAsArrayBuffer(this.file);
28280 parseExifData : function(dataView, offset, length)
28282 var tiffOffset = offset + 10,
28286 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28287 // No Exif data, might be XMP data instead
28291 // Check for the ASCII code for "Exif" (0x45786966):
28292 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28293 // No Exif data, might be XMP data instead
28296 if (tiffOffset + 8 > dataView.byteLength) {
28297 Roo.log('Invalid Exif data: Invalid segment size.');
28300 // Check for the two null bytes:
28301 if (dataView.getUint16(offset + 8) !== 0x0000) {
28302 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28305 // Check the byte alignment:
28306 switch (dataView.getUint16(tiffOffset)) {
28308 littleEndian = true;
28311 littleEndian = false;
28314 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28317 // Check for the TIFF tag marker (0x002A):
28318 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28319 Roo.log('Invalid Exif data: Missing TIFF marker.');
28322 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28323 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28325 this.parseExifTags(
28328 tiffOffset + dirOffset,
28333 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28338 if (dirOffset + 6 > dataView.byteLength) {
28339 Roo.log('Invalid Exif data: Invalid directory offset.');
28342 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28343 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28344 if (dirEndOffset + 4 > dataView.byteLength) {
28345 Roo.log('Invalid Exif data: Invalid directory size.');
28348 for (i = 0; i < tagsNumber; i += 1) {
28352 dirOffset + 2 + 12 * i, // tag offset
28356 // Return the offset to the next directory:
28357 return dataView.getUint32(dirEndOffset, littleEndian);
28360 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28362 var tag = dataView.getUint16(offset, littleEndian);
28364 this.exif[tag] = this.getExifValue(
28368 dataView.getUint16(offset + 2, littleEndian), // tag type
28369 dataView.getUint32(offset + 4, littleEndian), // tag length
28374 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28376 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28385 Roo.log('Invalid Exif data: Invalid tag type.');
28389 tagSize = tagType.size * length;
28390 // Determine if the value is contained in the dataOffset bytes,
28391 // or if the value at the dataOffset is a pointer to the actual data:
28392 dataOffset = tagSize > 4 ?
28393 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28394 if (dataOffset + tagSize > dataView.byteLength) {
28395 Roo.log('Invalid Exif data: Invalid data offset.');
28398 if (length === 1) {
28399 return tagType.getValue(dataView, dataOffset, littleEndian);
28402 for (i = 0; i < length; i += 1) {
28403 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28406 if (tagType.ascii) {
28408 // Concatenate the chars:
28409 for (i = 0; i < values.length; i += 1) {
28411 // Ignore the terminating NULL byte(s):
28412 if (c === '\u0000') {
28424 Roo.apply(Roo.bootstrap.UploadCropbox, {
28426 'Orientation': 0x0112
28430 1: 0, //'top-left',
28432 3: 180, //'bottom-right',
28433 // 4: 'bottom-left',
28435 6: 90, //'right-top',
28436 // 7: 'right-bottom',
28437 8: 270 //'left-bottom'
28441 // byte, 8-bit unsigned int:
28443 getValue: function (dataView, dataOffset) {
28444 return dataView.getUint8(dataOffset);
28448 // ascii, 8-bit byte:
28450 getValue: function (dataView, dataOffset) {
28451 return String.fromCharCode(dataView.getUint8(dataOffset));
28456 // short, 16 bit int:
28458 getValue: function (dataView, dataOffset, littleEndian) {
28459 return dataView.getUint16(dataOffset, littleEndian);
28463 // long, 32 bit int:
28465 getValue: function (dataView, dataOffset, littleEndian) {
28466 return dataView.getUint32(dataOffset, littleEndian);
28470 // rational = two long values, first is numerator, second is denominator:
28472 getValue: function (dataView, dataOffset, littleEndian) {
28473 return dataView.getUint32(dataOffset, littleEndian) /
28474 dataView.getUint32(dataOffset + 4, littleEndian);
28478 // slong, 32 bit signed int:
28480 getValue: function (dataView, dataOffset, littleEndian) {
28481 return dataView.getInt32(dataOffset, littleEndian);
28485 // srational, two slongs, first is numerator, second is denominator:
28487 getValue: function (dataView, dataOffset, littleEndian) {
28488 return dataView.getInt32(dataOffset, littleEndian) /
28489 dataView.getInt32(dataOffset + 4, littleEndian);
28499 cls : 'btn-group roo-upload-cropbox-rotate-left',
28500 action : 'rotate-left',
28504 cls : 'btn btn-default',
28505 html : '<i class="fa fa-undo"></i>'
28511 cls : 'btn-group roo-upload-cropbox-picture',
28512 action : 'picture',
28516 cls : 'btn btn-default',
28517 html : '<i class="fa fa-picture-o"></i>'
28523 cls : 'btn-group roo-upload-cropbox-rotate-right',
28524 action : 'rotate-right',
28528 cls : 'btn btn-default',
28529 html : '<i class="fa fa-repeat"></i>'
28537 cls : 'btn-group roo-upload-cropbox-rotate-left',
28538 action : 'rotate-left',
28542 cls : 'btn btn-default',
28543 html : '<i class="fa fa-undo"></i>'
28549 cls : 'btn-group roo-upload-cropbox-download',
28550 action : 'download',
28554 cls : 'btn btn-default',
28555 html : '<i class="fa fa-download"></i>'
28561 cls : 'btn-group roo-upload-cropbox-crop',
28566 cls : 'btn btn-default',
28567 html : '<i class="fa fa-crop"></i>'
28573 cls : 'btn-group roo-upload-cropbox-trash',
28578 cls : 'btn btn-default',
28579 html : '<i class="fa fa-trash"></i>'
28585 cls : 'btn-group roo-upload-cropbox-rotate-right',
28586 action : 'rotate-right',
28590 cls : 'btn btn-default',
28591 html : '<i class="fa fa-repeat"></i>'
28599 cls : 'btn-group roo-upload-cropbox-rotate-left',
28600 action : 'rotate-left',
28604 cls : 'btn btn-default',
28605 html : '<i class="fa fa-undo"></i>'
28611 cls : 'btn-group roo-upload-cropbox-rotate-right',
28612 action : 'rotate-right',
28616 cls : 'btn btn-default',
28617 html : '<i class="fa fa-repeat"></i>'
28630 * @class Roo.bootstrap.DocumentManager
28631 * @extends Roo.bootstrap.Component
28632 * Bootstrap DocumentManager class
28633 * @cfg {String} paramName default 'imageUpload'
28634 * @cfg {String} toolTipName default 'filename'
28635 * @cfg {String} method default POST
28636 * @cfg {String} url action url
28637 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28638 * @cfg {Boolean} multiple multiple upload default true
28639 * @cfg {Number} thumbSize default 300
28640 * @cfg {String} fieldLabel
28641 * @cfg {Number} labelWidth default 4
28642 * @cfg {String} labelAlign (left|top) default left
28643 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28644 * @cfg {Number} labellg set the width of label (1-12)
28645 * @cfg {Number} labelmd set the width of label (1-12)
28646 * @cfg {Number} labelsm set the width of label (1-12)
28647 * @cfg {Number} labelxs set the width of label (1-12)
28650 * Create a new DocumentManager
28651 * @param {Object} config The config object
28654 Roo.bootstrap.DocumentManager = function(config){
28655 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28658 this.delegates = [];
28663 * Fire when initial the DocumentManager
28664 * @param {Roo.bootstrap.DocumentManager} this
28669 * inspect selected file
28670 * @param {Roo.bootstrap.DocumentManager} this
28671 * @param {File} file
28676 * Fire when xhr load exception
28677 * @param {Roo.bootstrap.DocumentManager} this
28678 * @param {XMLHttpRequest} xhr
28680 "exception" : true,
28682 * @event afterupload
28683 * Fire when xhr load exception
28684 * @param {Roo.bootstrap.DocumentManager} this
28685 * @param {XMLHttpRequest} xhr
28687 "afterupload" : true,
28690 * prepare the form data
28691 * @param {Roo.bootstrap.DocumentManager} this
28692 * @param {Object} formData
28697 * Fire when remove the file
28698 * @param {Roo.bootstrap.DocumentManager} this
28699 * @param {Object} file
28704 * Fire after refresh the file
28705 * @param {Roo.bootstrap.DocumentManager} this
28710 * Fire after click the image
28711 * @param {Roo.bootstrap.DocumentManager} this
28712 * @param {Object} file
28717 * Fire when upload a image and editable set to true
28718 * @param {Roo.bootstrap.DocumentManager} this
28719 * @param {Object} file
28723 * @event beforeselectfile
28724 * Fire before select file
28725 * @param {Roo.bootstrap.DocumentManager} this
28727 "beforeselectfile" : true,
28730 * Fire before process file
28731 * @param {Roo.bootstrap.DocumentManager} this
28732 * @param {Object} file
28736 * @event previewrendered
28737 * Fire when preview rendered
28738 * @param {Roo.bootstrap.DocumentManager} this
28739 * @param {Object} file
28741 "previewrendered" : true,
28744 "previewResize" : true
28749 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28758 paramName : 'imageUpload',
28759 toolTipName : 'filename',
28762 labelAlign : 'left',
28772 getAutoCreate : function()
28774 var managerWidget = {
28776 cls : 'roo-document-manager',
28780 cls : 'roo-document-manager-selector',
28785 cls : 'roo-document-manager-uploader',
28789 cls : 'roo-document-manager-upload-btn',
28790 html : '<i class="fa fa-plus"></i>'
28801 cls : 'column col-md-12',
28806 if(this.fieldLabel.length){
28811 cls : 'column col-md-12',
28812 html : this.fieldLabel
28816 cls : 'column col-md-12',
28821 if(this.labelAlign == 'left'){
28826 html : this.fieldLabel
28835 if(this.labelWidth > 12){
28836 content[0].style = "width: " + this.labelWidth + 'px';
28839 if(this.labelWidth < 13 && this.labelmd == 0){
28840 this.labelmd = this.labelWidth;
28843 if(this.labellg > 0){
28844 content[0].cls += ' col-lg-' + this.labellg;
28845 content[1].cls += ' col-lg-' + (12 - this.labellg);
28848 if(this.labelmd > 0){
28849 content[0].cls += ' col-md-' + this.labelmd;
28850 content[1].cls += ' col-md-' + (12 - this.labelmd);
28853 if(this.labelsm > 0){
28854 content[0].cls += ' col-sm-' + this.labelsm;
28855 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28858 if(this.labelxs > 0){
28859 content[0].cls += ' col-xs-' + this.labelxs;
28860 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28868 cls : 'row clearfix',
28876 initEvents : function()
28878 this.managerEl = this.el.select('.roo-document-manager', true).first();
28879 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28881 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28882 this.selectorEl.hide();
28885 this.selectorEl.attr('multiple', 'multiple');
28888 this.selectorEl.on('change', this.onFileSelected, this);
28890 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28891 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28893 this.uploader.on('click', this.onUploaderClick, this);
28895 this.renderProgressDialog();
28899 window.addEventListener("resize", function() { _this.refresh(); } );
28901 this.fireEvent('initial', this);
28904 renderProgressDialog : function()
28908 this.progressDialog = new Roo.bootstrap.Modal({
28909 cls : 'roo-document-manager-progress-dialog',
28910 allow_close : false,
28920 btnclick : function() {
28921 _this.uploadCancel();
28927 this.progressDialog.render(Roo.get(document.body));
28929 this.progress = new Roo.bootstrap.Progress({
28930 cls : 'roo-document-manager-progress',
28935 this.progress.render(this.progressDialog.getChildContainer());
28937 this.progressBar = new Roo.bootstrap.ProgressBar({
28938 cls : 'roo-document-manager-progress-bar',
28941 aria_valuemax : 12,
28945 this.progressBar.render(this.progress.getChildContainer());
28948 onUploaderClick : function(e)
28950 e.preventDefault();
28952 if(this.fireEvent('beforeselectfile', this) != false){
28953 this.selectorEl.dom.click();
28958 onFileSelected : function(e)
28960 e.preventDefault();
28962 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28966 Roo.each(this.selectorEl.dom.files, function(file){
28967 if(this.fireEvent('inspect', this, file) != false){
28968 this.files.push(file);
28978 this.selectorEl.dom.value = '';
28980 if(!this.files || !this.files.length){
28984 if(this.boxes > 0 && this.files.length > this.boxes){
28985 this.files = this.files.slice(0, this.boxes);
28988 this.uploader.show();
28990 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28991 this.uploader.hide();
29000 Roo.each(this.files, function(file){
29002 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29003 var f = this.renderPreview(file);
29008 if(file.type.indexOf('image') != -1){
29009 this.delegates.push(
29011 _this.process(file);
29012 }).createDelegate(this)
29020 _this.process(file);
29021 }).createDelegate(this)
29026 this.files = files;
29028 this.delegates = this.delegates.concat(docs);
29030 if(!this.delegates.length){
29035 this.progressBar.aria_valuemax = this.delegates.length;
29042 arrange : function()
29044 if(!this.delegates.length){
29045 this.progressDialog.hide();
29050 var delegate = this.delegates.shift();
29052 this.progressDialog.show();
29054 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29056 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29061 refresh : function()
29063 this.uploader.show();
29065 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29066 this.uploader.hide();
29069 Roo.isTouch ? this.closable(false) : this.closable(true);
29071 this.fireEvent('refresh', this);
29074 onRemove : function(e, el, o)
29076 e.preventDefault();
29078 this.fireEvent('remove', this, o);
29082 remove : function(o)
29086 Roo.each(this.files, function(file){
29087 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29096 this.files = files;
29103 Roo.each(this.files, function(file){
29108 file.target.remove();
29117 onClick : function(e, el, o)
29119 e.preventDefault();
29121 this.fireEvent('click', this, o);
29125 closable : function(closable)
29127 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29129 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29141 xhrOnLoad : function(xhr)
29143 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29147 if (xhr.readyState !== 4) {
29149 this.fireEvent('exception', this, xhr);
29153 var response = Roo.decode(xhr.responseText);
29155 if(!response.success){
29157 this.fireEvent('exception', this, xhr);
29161 var file = this.renderPreview(response.data);
29163 this.files.push(file);
29167 this.fireEvent('afterupload', this, xhr);
29171 xhrOnError : function(xhr)
29173 Roo.log('xhr on error');
29175 var response = Roo.decode(xhr.responseText);
29182 process : function(file)
29184 if(this.fireEvent('process', this, file) !== false){
29185 if(this.editable && file.type.indexOf('image') != -1){
29186 this.fireEvent('edit', this, file);
29190 this.uploadStart(file, false);
29197 uploadStart : function(file, crop)
29199 this.xhr = new XMLHttpRequest();
29201 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29206 file.xhr = this.xhr;
29208 this.managerEl.createChild({
29210 cls : 'roo-document-manager-loading',
29214 tooltip : file.name,
29215 cls : 'roo-document-manager-thumb',
29216 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29222 this.xhr.open(this.method, this.url, true);
29225 "Accept": "application/json",
29226 "Cache-Control": "no-cache",
29227 "X-Requested-With": "XMLHttpRequest"
29230 for (var headerName in headers) {
29231 var headerValue = headers[headerName];
29233 this.xhr.setRequestHeader(headerName, headerValue);
29239 this.xhr.onload = function()
29241 _this.xhrOnLoad(_this.xhr);
29244 this.xhr.onerror = function()
29246 _this.xhrOnError(_this.xhr);
29249 var formData = new FormData();
29251 formData.append('returnHTML', 'NO');
29254 formData.append('crop', crop);
29257 formData.append(this.paramName, file, file.name);
29264 if(this.fireEvent('prepare', this, formData, options) != false){
29266 if(options.manually){
29270 this.xhr.send(formData);
29274 this.uploadCancel();
29277 uploadCancel : function()
29283 this.delegates = [];
29285 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29292 renderPreview : function(file)
29294 if(typeof(file.target) != 'undefined' && file.target){
29298 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29300 var previewEl = this.managerEl.createChild({
29302 cls : 'roo-document-manager-preview',
29306 tooltip : file[this.toolTipName],
29307 cls : 'roo-document-manager-thumb',
29308 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29313 html : '<i class="fa fa-times-circle"></i>'
29318 var close = previewEl.select('button.close', true).first();
29320 close.on('click', this.onRemove, this, file);
29322 file.target = previewEl;
29324 var image = previewEl.select('img', true).first();
29328 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29330 image.on('click', this.onClick, this, file);
29332 this.fireEvent('previewrendered', this, file);
29338 onPreviewLoad : function(file, image)
29340 if(typeof(file.target) == 'undefined' || !file.target){
29344 var width = image.dom.naturalWidth || image.dom.width;
29345 var height = image.dom.naturalHeight || image.dom.height;
29347 if(!this.previewResize) {
29351 if(width > height){
29352 file.target.addClass('wide');
29356 file.target.addClass('tall');
29361 uploadFromSource : function(file, crop)
29363 this.xhr = new XMLHttpRequest();
29365 this.managerEl.createChild({
29367 cls : 'roo-document-manager-loading',
29371 tooltip : file.name,
29372 cls : 'roo-document-manager-thumb',
29373 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29379 this.xhr.open(this.method, this.url, true);
29382 "Accept": "application/json",
29383 "Cache-Control": "no-cache",
29384 "X-Requested-With": "XMLHttpRequest"
29387 for (var headerName in headers) {
29388 var headerValue = headers[headerName];
29390 this.xhr.setRequestHeader(headerName, headerValue);
29396 this.xhr.onload = function()
29398 _this.xhrOnLoad(_this.xhr);
29401 this.xhr.onerror = function()
29403 _this.xhrOnError(_this.xhr);
29406 var formData = new FormData();
29408 formData.append('returnHTML', 'NO');
29410 formData.append('crop', crop);
29412 if(typeof(file.filename) != 'undefined'){
29413 formData.append('filename', file.filename);
29416 if(typeof(file.mimetype) != 'undefined'){
29417 formData.append('mimetype', file.mimetype);
29422 if(this.fireEvent('prepare', this, formData) != false){
29423 this.xhr.send(formData);
29433 * @class Roo.bootstrap.DocumentViewer
29434 * @extends Roo.bootstrap.Component
29435 * Bootstrap DocumentViewer class
29436 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29437 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29440 * Create a new DocumentViewer
29441 * @param {Object} config The config object
29444 Roo.bootstrap.DocumentViewer = function(config){
29445 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29450 * Fire after initEvent
29451 * @param {Roo.bootstrap.DocumentViewer} this
29457 * @param {Roo.bootstrap.DocumentViewer} this
29462 * Fire after download button
29463 * @param {Roo.bootstrap.DocumentViewer} this
29468 * Fire after trash button
29469 * @param {Roo.bootstrap.DocumentViewer} this
29476 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29478 showDownload : true,
29482 getAutoCreate : function()
29486 cls : 'roo-document-viewer',
29490 cls : 'roo-document-viewer-body',
29494 cls : 'roo-document-viewer-thumb',
29498 cls : 'roo-document-viewer-image'
29506 cls : 'roo-document-viewer-footer',
29509 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29513 cls : 'btn-group roo-document-viewer-download',
29517 cls : 'btn btn-default',
29518 html : '<i class="fa fa-download"></i>'
29524 cls : 'btn-group roo-document-viewer-trash',
29528 cls : 'btn btn-default',
29529 html : '<i class="fa fa-trash"></i>'
29542 initEvents : function()
29544 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29545 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29547 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29548 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29550 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29551 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29553 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29554 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29556 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29557 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29559 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29560 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29562 this.bodyEl.on('click', this.onClick, this);
29563 this.downloadBtn.on('click', this.onDownload, this);
29564 this.trashBtn.on('click', this.onTrash, this);
29566 this.downloadBtn.hide();
29567 this.trashBtn.hide();
29569 if(this.showDownload){
29570 this.downloadBtn.show();
29573 if(this.showTrash){
29574 this.trashBtn.show();
29577 if(!this.showDownload && !this.showTrash) {
29578 this.footerEl.hide();
29583 initial : function()
29585 this.fireEvent('initial', this);
29589 onClick : function(e)
29591 e.preventDefault();
29593 this.fireEvent('click', this);
29596 onDownload : function(e)
29598 e.preventDefault();
29600 this.fireEvent('download', this);
29603 onTrash : function(e)
29605 e.preventDefault();
29607 this.fireEvent('trash', this);
29619 * @class Roo.bootstrap.NavProgressBar
29620 * @extends Roo.bootstrap.Component
29621 * Bootstrap NavProgressBar class
29624 * Create a new nav progress bar
29625 * @param {Object} config The config object
29628 Roo.bootstrap.NavProgressBar = function(config){
29629 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29631 this.bullets = this.bullets || [];
29633 // Roo.bootstrap.NavProgressBar.register(this);
29637 * Fires when the active item changes
29638 * @param {Roo.bootstrap.NavProgressBar} this
29639 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29640 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29647 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29652 getAutoCreate : function()
29654 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29658 cls : 'roo-navigation-bar-group',
29662 cls : 'roo-navigation-top-bar'
29666 cls : 'roo-navigation-bullets-bar',
29670 cls : 'roo-navigation-bar'
29677 cls : 'roo-navigation-bottom-bar'
29687 initEvents: function()
29692 onRender : function(ct, position)
29694 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29696 if(this.bullets.length){
29697 Roo.each(this.bullets, function(b){
29706 addItem : function(cfg)
29708 var item = new Roo.bootstrap.NavProgressItem(cfg);
29710 item.parentId = this.id;
29711 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29714 var top = new Roo.bootstrap.Element({
29716 cls : 'roo-navigation-bar-text'
29719 var bottom = new Roo.bootstrap.Element({
29721 cls : 'roo-navigation-bar-text'
29724 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29725 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29727 var topText = new Roo.bootstrap.Element({
29729 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29732 var bottomText = new Roo.bootstrap.Element({
29734 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29737 topText.onRender(top.el, null);
29738 bottomText.onRender(bottom.el, null);
29741 item.bottomEl = bottom;
29744 this.barItems.push(item);
29749 getActive : function()
29751 var active = false;
29753 Roo.each(this.barItems, function(v){
29755 if (!v.isActive()) {
29767 setActiveItem : function(item)
29771 Roo.each(this.barItems, function(v){
29772 if (v.rid == item.rid) {
29776 if (v.isActive()) {
29777 v.setActive(false);
29782 item.setActive(true);
29784 this.fireEvent('changed', this, item, prev);
29787 getBarItem: function(rid)
29791 Roo.each(this.barItems, function(e) {
29792 if (e.rid != rid) {
29803 indexOfItem : function(item)
29807 Roo.each(this.barItems, function(v, i){
29809 if (v.rid != item.rid) {
29820 setActiveNext : function()
29822 var i = this.indexOfItem(this.getActive());
29824 if (i > this.barItems.length) {
29828 this.setActiveItem(this.barItems[i+1]);
29831 setActivePrev : function()
29833 var i = this.indexOfItem(this.getActive());
29839 this.setActiveItem(this.barItems[i-1]);
29842 format : function()
29844 if(!this.barItems.length){
29848 var width = 100 / this.barItems.length;
29850 Roo.each(this.barItems, function(i){
29851 i.el.setStyle('width', width + '%');
29852 i.topEl.el.setStyle('width', width + '%');
29853 i.bottomEl.el.setStyle('width', width + '%');
29862 * Nav Progress Item
29867 * @class Roo.bootstrap.NavProgressItem
29868 * @extends Roo.bootstrap.Component
29869 * Bootstrap NavProgressItem class
29870 * @cfg {String} rid the reference id
29871 * @cfg {Boolean} active (true|false) Is item active default false
29872 * @cfg {Boolean} disabled (true|false) Is item active default false
29873 * @cfg {String} html
29874 * @cfg {String} position (top|bottom) text position default bottom
29875 * @cfg {String} icon show icon instead of number
29878 * Create a new NavProgressItem
29879 * @param {Object} config The config object
29881 Roo.bootstrap.NavProgressItem = function(config){
29882 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29887 * The raw click event for the entire grid.
29888 * @param {Roo.bootstrap.NavProgressItem} this
29889 * @param {Roo.EventObject} e
29896 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29902 position : 'bottom',
29905 getAutoCreate : function()
29907 var iconCls = 'roo-navigation-bar-item-icon';
29909 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29913 cls: 'roo-navigation-bar-item',
29923 cfg.cls += ' active';
29926 cfg.cls += ' disabled';
29932 disable : function()
29934 this.setDisabled(true);
29937 enable : function()
29939 this.setDisabled(false);
29942 initEvents: function()
29944 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29946 this.iconEl.on('click', this.onClick, this);
29949 onClick : function(e)
29951 e.preventDefault();
29957 if(this.fireEvent('click', this, e) === false){
29961 this.parent().setActiveItem(this);
29964 isActive: function ()
29966 return this.active;
29969 setActive : function(state)
29971 if(this.active == state){
29975 this.active = state;
29978 this.el.addClass('active');
29982 this.el.removeClass('active');
29987 setDisabled : function(state)
29989 if(this.disabled == state){
29993 this.disabled = state;
29996 this.el.addClass('disabled');
30000 this.el.removeClass('disabled');
30003 tooltipEl : function()
30005 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30018 * @class Roo.bootstrap.FieldLabel
30019 * @extends Roo.bootstrap.Component
30020 * Bootstrap FieldLabel class
30021 * @cfg {String} html contents of the element
30022 * @cfg {String} tag tag of the element default label
30023 * @cfg {String} cls class of the element
30024 * @cfg {String} target label target
30025 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30026 * @cfg {String} invalidClass default "text-warning"
30027 * @cfg {String} validClass default "text-success"
30028 * @cfg {String} iconTooltip default "This field is required"
30029 * @cfg {String} indicatorpos (left|right) default left
30032 * Create a new FieldLabel
30033 * @param {Object} config The config object
30036 Roo.bootstrap.FieldLabel = function(config){
30037 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30042 * Fires after the field has been marked as invalid.
30043 * @param {Roo.form.FieldLabel} this
30044 * @param {String} msg The validation message
30049 * Fires after the field has been validated with no errors.
30050 * @param {Roo.form.FieldLabel} this
30056 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30063 invalidClass : 'has-warning',
30064 validClass : 'has-success',
30065 iconTooltip : 'This field is required',
30066 indicatorpos : 'left',
30068 getAutoCreate : function(){
30071 if (!this.allowBlank) {
30077 cls : 'roo-bootstrap-field-label ' + this.cls,
30082 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30083 tooltip : this.iconTooltip
30092 if(this.indicatorpos == 'right'){
30095 cls : 'roo-bootstrap-field-label ' + this.cls,
30104 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30105 tooltip : this.iconTooltip
30114 initEvents: function()
30116 Roo.bootstrap.Element.superclass.initEvents.call(this);
30118 this.indicator = this.indicatorEl();
30120 if(this.indicator){
30121 this.indicator.removeClass('visible');
30122 this.indicator.addClass('invisible');
30125 Roo.bootstrap.FieldLabel.register(this);
30128 indicatorEl : function()
30130 var indicator = this.el.select('i.roo-required-indicator',true).first();
30141 * Mark this field as valid
30143 markValid : function()
30145 if(this.indicator){
30146 this.indicator.removeClass('visible');
30147 this.indicator.addClass('invisible');
30150 this.el.removeClass(this.invalidClass);
30152 this.el.addClass(this.validClass);
30154 this.fireEvent('valid', this);
30158 * Mark this field as invalid
30159 * @param {String} msg The validation message
30161 markInvalid : function(msg)
30163 if(this.indicator){
30164 this.indicator.removeClass('invisible');
30165 this.indicator.addClass('visible');
30168 this.el.removeClass(this.validClass);
30170 this.el.addClass(this.invalidClass);
30172 this.fireEvent('invalid', this, msg);
30178 Roo.apply(Roo.bootstrap.FieldLabel, {
30183 * register a FieldLabel Group
30184 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30186 register : function(label)
30188 if(this.groups.hasOwnProperty(label.target)){
30192 this.groups[label.target] = label;
30196 * fetch a FieldLabel Group based on the target
30197 * @param {string} target
30198 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30200 get: function(target) {
30201 if (typeof(this.groups[target]) == 'undefined') {
30205 return this.groups[target] ;
30214 * page DateSplitField.
30220 * @class Roo.bootstrap.DateSplitField
30221 * @extends Roo.bootstrap.Component
30222 * Bootstrap DateSplitField class
30223 * @cfg {string} fieldLabel - the label associated
30224 * @cfg {Number} labelWidth set the width of label (0-12)
30225 * @cfg {String} labelAlign (top|left)
30226 * @cfg {Boolean} dayAllowBlank (true|false) default false
30227 * @cfg {Boolean} monthAllowBlank (true|false) default false
30228 * @cfg {Boolean} yearAllowBlank (true|false) default false
30229 * @cfg {string} dayPlaceholder
30230 * @cfg {string} monthPlaceholder
30231 * @cfg {string} yearPlaceholder
30232 * @cfg {string} dayFormat default 'd'
30233 * @cfg {string} monthFormat default 'm'
30234 * @cfg {string} yearFormat default 'Y'
30235 * @cfg {Number} labellg set the width of label (1-12)
30236 * @cfg {Number} labelmd set the width of label (1-12)
30237 * @cfg {Number} labelsm set the width of label (1-12)
30238 * @cfg {Number} labelxs set the width of label (1-12)
30242 * Create a new DateSplitField
30243 * @param {Object} config The config object
30246 Roo.bootstrap.DateSplitField = function(config){
30247 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30253 * getting the data of years
30254 * @param {Roo.bootstrap.DateSplitField} this
30255 * @param {Object} years
30260 * getting the data of days
30261 * @param {Roo.bootstrap.DateSplitField} this
30262 * @param {Object} days
30267 * Fires after the field has been marked as invalid.
30268 * @param {Roo.form.Field} this
30269 * @param {String} msg The validation message
30274 * Fires after the field has been validated with no errors.
30275 * @param {Roo.form.Field} this
30281 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30284 labelAlign : 'top',
30286 dayAllowBlank : false,
30287 monthAllowBlank : false,
30288 yearAllowBlank : false,
30289 dayPlaceholder : '',
30290 monthPlaceholder : '',
30291 yearPlaceholder : '',
30295 isFormField : true,
30301 getAutoCreate : function()
30305 cls : 'row roo-date-split-field-group',
30310 cls : 'form-hidden-field roo-date-split-field-group-value',
30316 var labelCls = 'col-md-12';
30317 var contentCls = 'col-md-4';
30319 if(this.fieldLabel){
30323 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30327 html : this.fieldLabel
30332 if(this.labelAlign == 'left'){
30334 if(this.labelWidth > 12){
30335 label.style = "width: " + this.labelWidth + 'px';
30338 if(this.labelWidth < 13 && this.labelmd == 0){
30339 this.labelmd = this.labelWidth;
30342 if(this.labellg > 0){
30343 labelCls = ' col-lg-' + this.labellg;
30344 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30347 if(this.labelmd > 0){
30348 labelCls = ' col-md-' + this.labelmd;
30349 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30352 if(this.labelsm > 0){
30353 labelCls = ' col-sm-' + this.labelsm;
30354 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30357 if(this.labelxs > 0){
30358 labelCls = ' col-xs-' + this.labelxs;
30359 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30363 label.cls += ' ' + labelCls;
30365 cfg.cn.push(label);
30368 Roo.each(['day', 'month', 'year'], function(t){
30371 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30378 inputEl: function ()
30380 return this.el.select('.roo-date-split-field-group-value', true).first();
30383 onRender : function(ct, position)
30387 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30389 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30391 this.dayField = new Roo.bootstrap.ComboBox({
30392 allowBlank : this.dayAllowBlank,
30393 alwaysQuery : true,
30394 displayField : 'value',
30397 forceSelection : true,
30399 placeholder : this.dayPlaceholder,
30400 selectOnFocus : true,
30401 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30402 triggerAction : 'all',
30404 valueField : 'value',
30405 store : new Roo.data.SimpleStore({
30406 data : (function() {
30408 _this.fireEvent('days', _this, days);
30411 fields : [ 'value' ]
30414 select : function (_self, record, index)
30416 _this.setValue(_this.getValue());
30421 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30423 this.monthField = new Roo.bootstrap.MonthField({
30424 after : '<i class=\"fa fa-calendar\"></i>',
30425 allowBlank : this.monthAllowBlank,
30426 placeholder : this.monthPlaceholder,
30429 render : function (_self)
30431 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30432 e.preventDefault();
30436 select : function (_self, oldvalue, newvalue)
30438 _this.setValue(_this.getValue());
30443 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30445 this.yearField = new Roo.bootstrap.ComboBox({
30446 allowBlank : this.yearAllowBlank,
30447 alwaysQuery : true,
30448 displayField : 'value',
30451 forceSelection : true,
30453 placeholder : this.yearPlaceholder,
30454 selectOnFocus : true,
30455 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30456 triggerAction : 'all',
30458 valueField : 'value',
30459 store : new Roo.data.SimpleStore({
30460 data : (function() {
30462 _this.fireEvent('years', _this, years);
30465 fields : [ 'value' ]
30468 select : function (_self, record, index)
30470 _this.setValue(_this.getValue());
30475 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30478 setValue : function(v, format)
30480 this.inputEl.dom.value = v;
30482 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30484 var d = Date.parseDate(v, f);
30491 this.setDay(d.format(this.dayFormat));
30492 this.setMonth(d.format(this.monthFormat));
30493 this.setYear(d.format(this.yearFormat));
30500 setDay : function(v)
30502 this.dayField.setValue(v);
30503 this.inputEl.dom.value = this.getValue();
30508 setMonth : function(v)
30510 this.monthField.setValue(v, true);
30511 this.inputEl.dom.value = this.getValue();
30516 setYear : function(v)
30518 this.yearField.setValue(v);
30519 this.inputEl.dom.value = this.getValue();
30524 getDay : function()
30526 return this.dayField.getValue();
30529 getMonth : function()
30531 return this.monthField.getValue();
30534 getYear : function()
30536 return this.yearField.getValue();
30539 getValue : function()
30541 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30543 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30553 this.inputEl.dom.value = '';
30558 validate : function()
30560 var d = this.dayField.validate();
30561 var m = this.monthField.validate();
30562 var y = this.yearField.validate();
30567 (!this.dayAllowBlank && !d) ||
30568 (!this.monthAllowBlank && !m) ||
30569 (!this.yearAllowBlank && !y)
30574 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30583 this.markInvalid();
30588 markValid : function()
30591 var label = this.el.select('label', true).first();
30592 var icon = this.el.select('i.fa-star', true).first();
30598 this.fireEvent('valid', this);
30602 * Mark this field as invalid
30603 * @param {String} msg The validation message
30605 markInvalid : function(msg)
30608 var label = this.el.select('label', true).first();
30609 var icon = this.el.select('i.fa-star', true).first();
30611 if(label && !icon){
30612 this.el.select('.roo-date-split-field-label', true).createChild({
30614 cls : 'text-danger fa fa-lg fa-star',
30615 tooltip : 'This field is required',
30616 style : 'margin-right:5px;'
30620 this.fireEvent('invalid', this, msg);
30623 clearInvalid : function()
30625 var label = this.el.select('label', true).first();
30626 var icon = this.el.select('i.fa-star', true).first();
30632 this.fireEvent('valid', this);
30635 getName: function()
30645 * http://masonry.desandro.com
30647 * The idea is to render all the bricks based on vertical width...
30649 * The original code extends 'outlayer' - we might need to use that....
30655 * @class Roo.bootstrap.LayoutMasonry
30656 * @extends Roo.bootstrap.Component
30657 * Bootstrap Layout Masonry class
30660 * Create a new Element
30661 * @param {Object} config The config object
30664 Roo.bootstrap.LayoutMasonry = function(config){
30666 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30670 Roo.bootstrap.LayoutMasonry.register(this);
30676 * Fire after layout the items
30677 * @param {Roo.bootstrap.LayoutMasonry} this
30678 * @param {Roo.EventObject} e
30685 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30688 * @cfg {Boolean} isLayoutInstant = no animation?
30690 isLayoutInstant : false, // needed?
30693 * @cfg {Number} boxWidth width of the columns
30698 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30703 * @cfg {Number} padWidth padding below box..
30708 * @cfg {Number} gutter gutter width..
30713 * @cfg {Number} maxCols maximum number of columns
30719 * @cfg {Boolean} isAutoInitial defalut true
30721 isAutoInitial : true,
30726 * @cfg {Boolean} isHorizontal defalut false
30728 isHorizontal : false,
30730 currentSize : null,
30736 bricks: null, //CompositeElement
30740 _isLayoutInited : false,
30742 // isAlternative : false, // only use for vertical layout...
30745 * @cfg {Number} alternativePadWidth padding below box..
30747 alternativePadWidth : 50,
30749 selectedBrick : [],
30751 getAutoCreate : function(){
30753 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30757 cls: 'blog-masonary-wrapper ' + this.cls,
30759 cls : 'mas-boxes masonary'
30766 getChildContainer: function( )
30768 if (this.boxesEl) {
30769 return this.boxesEl;
30772 this.boxesEl = this.el.select('.mas-boxes').first();
30774 return this.boxesEl;
30778 initEvents : function()
30782 if(this.isAutoInitial){
30783 Roo.log('hook children rendered');
30784 this.on('childrenrendered', function() {
30785 Roo.log('children rendered');
30791 initial : function()
30793 this.selectedBrick = [];
30795 this.currentSize = this.el.getBox(true);
30797 Roo.EventManager.onWindowResize(this.resize, this);
30799 if(!this.isAutoInitial){
30807 //this.layout.defer(500,this);
30811 resize : function()
30813 var cs = this.el.getBox(true);
30816 this.currentSize.width == cs.width &&
30817 this.currentSize.x == cs.x &&
30818 this.currentSize.height == cs.height &&
30819 this.currentSize.y == cs.y
30821 Roo.log("no change in with or X or Y");
30825 this.currentSize = cs;
30831 layout : function()
30833 this._resetLayout();
30835 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30837 this.layoutItems( isInstant );
30839 this._isLayoutInited = true;
30841 this.fireEvent('layout', this);
30845 _resetLayout : function()
30847 if(this.isHorizontal){
30848 this.horizontalMeasureColumns();
30852 this.verticalMeasureColumns();
30856 verticalMeasureColumns : function()
30858 this.getContainerWidth();
30860 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30861 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30865 var boxWidth = this.boxWidth + this.padWidth;
30867 if(this.containerWidth < this.boxWidth){
30868 boxWidth = this.containerWidth
30871 var containerWidth = this.containerWidth;
30873 var cols = Math.floor(containerWidth / boxWidth);
30875 this.cols = Math.max( cols, 1 );
30877 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30879 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30881 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30883 this.colWidth = boxWidth + avail - this.padWidth;
30885 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30886 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30889 horizontalMeasureColumns : function()
30891 this.getContainerWidth();
30893 var boxWidth = this.boxWidth;
30895 if(this.containerWidth < boxWidth){
30896 boxWidth = this.containerWidth;
30899 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30901 this.el.setHeight(boxWidth);
30905 getContainerWidth : function()
30907 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30910 layoutItems : function( isInstant )
30912 Roo.log(this.bricks);
30914 var items = Roo.apply([], this.bricks);
30916 if(this.isHorizontal){
30917 this._horizontalLayoutItems( items , isInstant );
30921 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30922 // this._verticalAlternativeLayoutItems( items , isInstant );
30926 this._verticalLayoutItems( items , isInstant );
30930 _verticalLayoutItems : function ( items , isInstant)
30932 if ( !items || !items.length ) {
30937 ['xs', 'xs', 'xs', 'tall'],
30938 ['xs', 'xs', 'tall'],
30939 ['xs', 'xs', 'sm'],
30940 ['xs', 'xs', 'xs'],
30946 ['sm', 'xs', 'xs'],
30950 ['tall', 'xs', 'xs', 'xs'],
30951 ['tall', 'xs', 'xs'],
30963 Roo.each(items, function(item, k){
30965 switch (item.size) {
30966 // these layouts take up a full box,
30977 boxes.push([item]);
31000 var filterPattern = function(box, length)
31008 var pattern = box.slice(0, length);
31012 Roo.each(pattern, function(i){
31013 format.push(i.size);
31016 Roo.each(standard, function(s){
31018 if(String(s) != String(format)){
31027 if(!match && length == 1){
31032 filterPattern(box, length - 1);
31036 queue.push(pattern);
31038 box = box.slice(length, box.length);
31040 filterPattern(box, 4);
31046 Roo.each(boxes, function(box, k){
31052 if(box.length == 1){
31057 filterPattern(box, 4);
31061 this._processVerticalLayoutQueue( queue, isInstant );
31065 // _verticalAlternativeLayoutItems : function( items , isInstant )
31067 // if ( !items || !items.length ) {
31071 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31075 _horizontalLayoutItems : function ( items , isInstant)
31077 if ( !items || !items.length || items.length < 3) {
31083 var eItems = items.slice(0, 3);
31085 items = items.slice(3, items.length);
31088 ['xs', 'xs', 'xs', 'wide'],
31089 ['xs', 'xs', 'wide'],
31090 ['xs', 'xs', 'sm'],
31091 ['xs', 'xs', 'xs'],
31097 ['sm', 'xs', 'xs'],
31101 ['wide', 'xs', 'xs', 'xs'],
31102 ['wide', 'xs', 'xs'],
31115 Roo.each(items, function(item, k){
31117 switch (item.size) {
31128 boxes.push([item]);
31152 var filterPattern = function(box, length)
31160 var pattern = box.slice(0, length);
31164 Roo.each(pattern, function(i){
31165 format.push(i.size);
31168 Roo.each(standard, function(s){
31170 if(String(s) != String(format)){
31179 if(!match && length == 1){
31184 filterPattern(box, length - 1);
31188 queue.push(pattern);
31190 box = box.slice(length, box.length);
31192 filterPattern(box, 4);
31198 Roo.each(boxes, function(box, k){
31204 if(box.length == 1){
31209 filterPattern(box, 4);
31216 var pos = this.el.getBox(true);
31220 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31222 var hit_end = false;
31224 Roo.each(queue, function(box){
31228 Roo.each(box, function(b){
31230 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31240 Roo.each(box, function(b){
31242 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31245 mx = Math.max(mx, b.x);
31249 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31253 Roo.each(box, function(b){
31255 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31269 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31272 /** Sets position of item in DOM
31273 * @param {Element} item
31274 * @param {Number} x - horizontal position
31275 * @param {Number} y - vertical position
31276 * @param {Boolean} isInstant - disables transitions
31278 _processVerticalLayoutQueue : function( queue, isInstant )
31280 var pos = this.el.getBox(true);
31285 for (var i = 0; i < this.cols; i++){
31289 Roo.each(queue, function(box, k){
31291 var col = k % this.cols;
31293 Roo.each(box, function(b,kk){
31295 b.el.position('absolute');
31297 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31298 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31300 if(b.size == 'md-left' || b.size == 'md-right'){
31301 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31302 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31305 b.el.setWidth(width);
31306 b.el.setHeight(height);
31308 b.el.select('iframe',true).setSize(width,height);
31312 for (var i = 0; i < this.cols; i++){
31314 if(maxY[i] < maxY[col]){
31319 col = Math.min(col, i);
31323 x = pos.x + col * (this.colWidth + this.padWidth);
31327 var positions = [];
31329 switch (box.length){
31331 positions = this.getVerticalOneBoxColPositions(x, y, box);
31334 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31337 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31340 positions = this.getVerticalFourBoxColPositions(x, y, box);
31346 Roo.each(box, function(b,kk){
31348 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31350 var sz = b.el.getSize();
31352 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31360 for (var i = 0; i < this.cols; i++){
31361 mY = Math.max(mY, maxY[i]);
31364 this.el.setHeight(mY - pos.y);
31368 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31370 // var pos = this.el.getBox(true);
31373 // var maxX = pos.right;
31375 // var maxHeight = 0;
31377 // Roo.each(items, function(item, k){
31381 // item.el.position('absolute');
31383 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31385 // item.el.setWidth(width);
31387 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31389 // item.el.setHeight(height);
31392 // item.el.setXY([x, y], isInstant ? false : true);
31394 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31397 // y = y + height + this.alternativePadWidth;
31399 // maxHeight = maxHeight + height + this.alternativePadWidth;
31403 // this.el.setHeight(maxHeight);
31407 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31409 var pos = this.el.getBox(true);
31414 var maxX = pos.right;
31416 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31418 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31420 Roo.each(queue, function(box, k){
31422 Roo.each(box, function(b, kk){
31424 b.el.position('absolute');
31426 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31427 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31429 if(b.size == 'md-left' || b.size == 'md-right'){
31430 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31431 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31434 b.el.setWidth(width);
31435 b.el.setHeight(height);
31443 var positions = [];
31445 switch (box.length){
31447 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31450 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31453 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31456 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31462 Roo.each(box, function(b,kk){
31464 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31466 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31474 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31476 Roo.each(eItems, function(b,k){
31478 b.size = (k == 0) ? 'sm' : 'xs';
31479 b.x = (k == 0) ? 2 : 1;
31480 b.y = (k == 0) ? 2 : 1;
31482 b.el.position('absolute');
31484 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31486 b.el.setWidth(width);
31488 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31490 b.el.setHeight(height);
31494 var positions = [];
31497 x : maxX - this.unitWidth * 2 - this.gutter,
31502 x : maxX - this.unitWidth,
31503 y : minY + (this.unitWidth + this.gutter) * 2
31507 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31511 Roo.each(eItems, function(b,k){
31513 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31519 getVerticalOneBoxColPositions : function(x, y, box)
31523 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31525 if(box[0].size == 'md-left'){
31529 if(box[0].size == 'md-right'){
31534 x : x + (this.unitWidth + this.gutter) * rand,
31541 getVerticalTwoBoxColPositions : function(x, y, box)
31545 if(box[0].size == 'xs'){
31549 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31553 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31567 x : x + (this.unitWidth + this.gutter) * 2,
31568 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31575 getVerticalThreeBoxColPositions : function(x, y, box)
31579 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31587 x : x + (this.unitWidth + this.gutter) * 1,
31592 x : x + (this.unitWidth + this.gutter) * 2,
31600 if(box[0].size == 'xs' && box[1].size == 'xs'){
31609 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31613 x : x + (this.unitWidth + this.gutter) * 1,
31627 x : x + (this.unitWidth + this.gutter) * 2,
31632 x : x + (this.unitWidth + this.gutter) * 2,
31633 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31640 getVerticalFourBoxColPositions : function(x, y, box)
31644 if(box[0].size == 'xs'){
31653 y : y + (this.unitHeight + this.gutter) * 1
31658 y : y + (this.unitHeight + this.gutter) * 2
31662 x : x + (this.unitWidth + this.gutter) * 1,
31676 x : x + (this.unitWidth + this.gutter) * 2,
31681 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31682 y : y + (this.unitHeight + this.gutter) * 1
31686 x : x + (this.unitWidth + this.gutter) * 2,
31687 y : y + (this.unitWidth + this.gutter) * 2
31694 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31698 if(box[0].size == 'md-left'){
31700 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31707 if(box[0].size == 'md-right'){
31709 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31710 y : minY + (this.unitWidth + this.gutter) * 1
31716 var rand = Math.floor(Math.random() * (4 - box[0].y));
31719 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31720 y : minY + (this.unitWidth + this.gutter) * rand
31727 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31731 if(box[0].size == 'xs'){
31734 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31739 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31748 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31753 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31754 y : minY + (this.unitWidth + this.gutter) * 2
31761 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31765 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31768 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31773 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31774 y : minY + (this.unitWidth + this.gutter) * 1
31778 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31779 y : minY + (this.unitWidth + this.gutter) * 2
31786 if(box[0].size == 'xs' && box[1].size == 'xs'){
31789 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31794 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31799 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31800 y : minY + (this.unitWidth + this.gutter) * 1
31808 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31813 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31814 y : minY + (this.unitWidth + this.gutter) * 2
31818 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31819 y : minY + (this.unitWidth + this.gutter) * 2
31826 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31830 if(box[0].size == 'xs'){
31833 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31838 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31843 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),
31848 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31849 y : minY + (this.unitWidth + this.gutter) * 1
31857 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31862 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31863 y : minY + (this.unitWidth + this.gutter) * 2
31867 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31868 y : minY + (this.unitWidth + this.gutter) * 2
31872 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),
31873 y : minY + (this.unitWidth + this.gutter) * 2
31881 * remove a Masonry Brick
31882 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31884 removeBrick : function(brick_id)
31890 for (var i = 0; i<this.bricks.length; i++) {
31891 if (this.bricks[i].id == brick_id) {
31892 this.bricks.splice(i,1);
31893 this.el.dom.removeChild(Roo.get(brick_id).dom);
31900 * adds a Masonry Brick
31901 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31903 addBrick : function(cfg)
31905 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31906 //this.register(cn);
31907 cn.parentId = this.id;
31908 cn.onRender(this.el, null);
31913 * register a Masonry Brick
31914 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31917 register : function(brick)
31919 this.bricks.push(brick);
31920 brick.masonryId = this.id;
31924 * clear all the Masonry Brick
31926 clearAll : function()
31929 //this.getChildContainer().dom.innerHTML = "";
31930 this.el.dom.innerHTML = '';
31933 getSelected : function()
31935 if (!this.selectedBrick) {
31939 return this.selectedBrick;
31943 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31947 * register a Masonry Layout
31948 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31951 register : function(layout)
31953 this.groups[layout.id] = layout;
31956 * fetch a Masonry Layout based on the masonry layout ID
31957 * @param {string} the masonry layout to add
31958 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31961 get: function(layout_id) {
31962 if (typeof(this.groups[layout_id]) == 'undefined') {
31965 return this.groups[layout_id] ;
31977 * http://masonry.desandro.com
31979 * The idea is to render all the bricks based on vertical width...
31981 * The original code extends 'outlayer' - we might need to use that....
31987 * @class Roo.bootstrap.LayoutMasonryAuto
31988 * @extends Roo.bootstrap.Component
31989 * Bootstrap Layout Masonry class
31992 * Create a new Element
31993 * @param {Object} config The config object
31996 Roo.bootstrap.LayoutMasonryAuto = function(config){
31997 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32000 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32003 * @cfg {Boolean} isFitWidth - resize the width..
32005 isFitWidth : false, // options..
32007 * @cfg {Boolean} isOriginLeft = left align?
32009 isOriginLeft : true,
32011 * @cfg {Boolean} isOriginTop = top align?
32013 isOriginTop : false,
32015 * @cfg {Boolean} isLayoutInstant = no animation?
32017 isLayoutInstant : false, // needed?
32019 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32021 isResizingContainer : true,
32023 * @cfg {Number} columnWidth width of the columns
32029 * @cfg {Number} maxCols maximum number of columns
32034 * @cfg {Number} padHeight padding below box..
32040 * @cfg {Boolean} isAutoInitial defalut true
32043 isAutoInitial : true,
32049 initialColumnWidth : 0,
32050 currentSize : null,
32052 colYs : null, // array.
32059 bricks: null, //CompositeElement
32060 cols : 0, // array?
32061 // element : null, // wrapped now this.el
32062 _isLayoutInited : null,
32065 getAutoCreate : function(){
32069 cls: 'blog-masonary-wrapper ' + this.cls,
32071 cls : 'mas-boxes masonary'
32078 getChildContainer: function( )
32080 if (this.boxesEl) {
32081 return this.boxesEl;
32084 this.boxesEl = this.el.select('.mas-boxes').first();
32086 return this.boxesEl;
32090 initEvents : function()
32094 if(this.isAutoInitial){
32095 Roo.log('hook children rendered');
32096 this.on('childrenrendered', function() {
32097 Roo.log('children rendered');
32104 initial : function()
32106 this.reloadItems();
32108 this.currentSize = this.el.getBox(true);
32110 /// was window resize... - let's see if this works..
32111 Roo.EventManager.onWindowResize(this.resize, this);
32113 if(!this.isAutoInitial){
32118 this.layout.defer(500,this);
32121 reloadItems: function()
32123 this.bricks = this.el.select('.masonry-brick', true);
32125 this.bricks.each(function(b) {
32126 //Roo.log(b.getSize());
32127 if (!b.attr('originalwidth')) {
32128 b.attr('originalwidth', b.getSize().width);
32133 Roo.log(this.bricks.elements.length);
32136 resize : function()
32139 var cs = this.el.getBox(true);
32141 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32142 Roo.log("no change in with or X");
32145 this.currentSize = cs;
32149 layout : function()
32152 this._resetLayout();
32153 //this._manageStamps();
32155 // don't animate first layout
32156 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32157 this.layoutItems( isInstant );
32159 // flag for initalized
32160 this._isLayoutInited = true;
32163 layoutItems : function( isInstant )
32165 //var items = this._getItemsForLayout( this.items );
32166 // original code supports filtering layout items.. we just ignore it..
32168 this._layoutItems( this.bricks , isInstant );
32170 this._postLayout();
32172 _layoutItems : function ( items , isInstant)
32174 //this.fireEvent( 'layout', this, items );
32177 if ( !items || !items.elements.length ) {
32178 // no items, emit event with empty array
32183 items.each(function(item) {
32184 Roo.log("layout item");
32186 // get x/y object from method
32187 var position = this._getItemLayoutPosition( item );
32189 position.item = item;
32190 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32191 queue.push( position );
32194 this._processLayoutQueue( queue );
32196 /** Sets position of item in DOM
32197 * @param {Element} item
32198 * @param {Number} x - horizontal position
32199 * @param {Number} y - vertical position
32200 * @param {Boolean} isInstant - disables transitions
32202 _processLayoutQueue : function( queue )
32204 for ( var i=0, len = queue.length; i < len; i++ ) {
32205 var obj = queue[i];
32206 obj.item.position('absolute');
32207 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32213 * Any logic you want to do after each layout,
32214 * i.e. size the container
32216 _postLayout : function()
32218 this.resizeContainer();
32221 resizeContainer : function()
32223 if ( !this.isResizingContainer ) {
32226 var size = this._getContainerSize();
32228 this.el.setSize(size.width,size.height);
32229 this.boxesEl.setSize(size.width,size.height);
32235 _resetLayout : function()
32237 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32238 this.colWidth = this.el.getWidth();
32239 //this.gutter = this.el.getWidth();
32241 this.measureColumns();
32247 this.colYs.push( 0 );
32253 measureColumns : function()
32255 this.getContainerWidth();
32256 // if columnWidth is 0, default to outerWidth of first item
32257 if ( !this.columnWidth ) {
32258 var firstItem = this.bricks.first();
32259 Roo.log(firstItem);
32260 this.columnWidth = this.containerWidth;
32261 if (firstItem && firstItem.attr('originalwidth') ) {
32262 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32264 // columnWidth fall back to item of first element
32265 Roo.log("set column width?");
32266 this.initialColumnWidth = this.columnWidth ;
32268 // if first elem has no width, default to size of container
32273 if (this.initialColumnWidth) {
32274 this.columnWidth = this.initialColumnWidth;
32279 // column width is fixed at the top - however if container width get's smaller we should
32282 // this bit calcs how man columns..
32284 var columnWidth = this.columnWidth += this.gutter;
32286 // calculate columns
32287 var containerWidth = this.containerWidth + this.gutter;
32289 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32290 // fix rounding errors, typically with gutters
32291 var excess = columnWidth - containerWidth % columnWidth;
32294 // if overshoot is less than a pixel, round up, otherwise floor it
32295 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32296 cols = Math[ mathMethod ]( cols );
32297 this.cols = Math.max( cols, 1 );
32298 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32300 // padding positioning..
32301 var totalColWidth = this.cols * this.columnWidth;
32302 var padavail = this.containerWidth - totalColWidth;
32303 // so for 2 columns - we need 3 'pads'
32305 var padNeeded = (1+this.cols) * this.padWidth;
32307 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32309 this.columnWidth += padExtra
32310 //this.padWidth = Math.floor(padavail / ( this.cols));
32312 // adjust colum width so that padding is fixed??
32314 // we have 3 columns ... total = width * 3
32315 // we have X left over... that should be used by
32317 //if (this.expandC) {
32325 getContainerWidth : function()
32327 /* // container is parent if fit width
32328 var container = this.isFitWidth ? this.element.parentNode : this.element;
32329 // check that this.size and size are there
32330 // IE8 triggers resize on body size change, so they might not be
32332 var size = getSize( container ); //FIXME
32333 this.containerWidth = size && size.innerWidth; //FIXME
32336 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32340 _getItemLayoutPosition : function( item ) // what is item?
32342 // we resize the item to our columnWidth..
32344 item.setWidth(this.columnWidth);
32345 item.autoBoxAdjust = false;
32347 var sz = item.getSize();
32349 // how many columns does this brick span
32350 var remainder = this.containerWidth % this.columnWidth;
32352 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32353 // round if off by 1 pixel, otherwise use ceil
32354 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32355 colSpan = Math.min( colSpan, this.cols );
32357 // normally this should be '1' as we dont' currently allow multi width columns..
32359 var colGroup = this._getColGroup( colSpan );
32360 // get the minimum Y value from the columns
32361 var minimumY = Math.min.apply( Math, colGroup );
32362 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32364 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32366 // position the brick
32368 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32369 y: this.currentSize.y + minimumY + this.padHeight
32373 // apply setHeight to necessary columns
32374 var setHeight = minimumY + sz.height + this.padHeight;
32375 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32377 var setSpan = this.cols + 1 - colGroup.length;
32378 for ( var i = 0; i < setSpan; i++ ) {
32379 this.colYs[ shortColIndex + i ] = setHeight ;
32386 * @param {Number} colSpan - number of columns the element spans
32387 * @returns {Array} colGroup
32389 _getColGroup : function( colSpan )
32391 if ( colSpan < 2 ) {
32392 // if brick spans only one column, use all the column Ys
32397 // how many different places could this brick fit horizontally
32398 var groupCount = this.cols + 1 - colSpan;
32399 // for each group potential horizontal position
32400 for ( var i = 0; i < groupCount; i++ ) {
32401 // make an array of colY values for that one group
32402 var groupColYs = this.colYs.slice( i, i + colSpan );
32403 // and get the max value of the array
32404 colGroup[i] = Math.max.apply( Math, groupColYs );
32409 _manageStamp : function( stamp )
32411 var stampSize = stamp.getSize();
32412 var offset = stamp.getBox();
32413 // get the columns that this stamp affects
32414 var firstX = this.isOriginLeft ? offset.x : offset.right;
32415 var lastX = firstX + stampSize.width;
32416 var firstCol = Math.floor( firstX / this.columnWidth );
32417 firstCol = Math.max( 0, firstCol );
32419 var lastCol = Math.floor( lastX / this.columnWidth );
32420 // lastCol should not go over if multiple of columnWidth #425
32421 lastCol -= lastX % this.columnWidth ? 0 : 1;
32422 lastCol = Math.min( this.cols - 1, lastCol );
32424 // set colYs to bottom of the stamp
32425 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32428 for ( var i = firstCol; i <= lastCol; i++ ) {
32429 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32434 _getContainerSize : function()
32436 this.maxY = Math.max.apply( Math, this.colYs );
32441 if ( this.isFitWidth ) {
32442 size.width = this._getContainerFitWidth();
32448 _getContainerFitWidth : function()
32450 var unusedCols = 0;
32451 // count unused columns
32454 if ( this.colYs[i] !== 0 ) {
32459 // fit container to columns that have been used
32460 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32463 needsResizeLayout : function()
32465 var previousWidth = this.containerWidth;
32466 this.getContainerWidth();
32467 return previousWidth !== this.containerWidth;
32482 * @class Roo.bootstrap.MasonryBrick
32483 * @extends Roo.bootstrap.Component
32484 * Bootstrap MasonryBrick class
32487 * Create a new MasonryBrick
32488 * @param {Object} config The config object
32491 Roo.bootstrap.MasonryBrick = function(config){
32493 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32495 Roo.bootstrap.MasonryBrick.register(this);
32501 * When a MasonryBrick is clcik
32502 * @param {Roo.bootstrap.MasonryBrick} this
32503 * @param {Roo.EventObject} e
32509 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32512 * @cfg {String} title
32516 * @cfg {String} html
32520 * @cfg {String} bgimage
32524 * @cfg {String} videourl
32528 * @cfg {String} cls
32532 * @cfg {String} href
32536 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32541 * @cfg {String} placetitle (center|bottom)
32546 * @cfg {Boolean} isFitContainer defalut true
32548 isFitContainer : true,
32551 * @cfg {Boolean} preventDefault defalut false
32553 preventDefault : false,
32556 * @cfg {Boolean} inverse defalut false
32558 maskInverse : false,
32560 getAutoCreate : function()
32562 if(!this.isFitContainer){
32563 return this.getSplitAutoCreate();
32566 var cls = 'masonry-brick masonry-brick-full';
32568 if(this.href.length){
32569 cls += ' masonry-brick-link';
32572 if(this.bgimage.length){
32573 cls += ' masonry-brick-image';
32576 if(this.maskInverse){
32577 cls += ' mask-inverse';
32580 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32581 cls += ' enable-mask';
32585 cls += ' masonry-' + this.size + '-brick';
32588 if(this.placetitle.length){
32590 switch (this.placetitle) {
32592 cls += ' masonry-center-title';
32595 cls += ' masonry-bottom-title';
32602 if(!this.html.length && !this.bgimage.length){
32603 cls += ' masonry-center-title';
32606 if(!this.html.length && this.bgimage.length){
32607 cls += ' masonry-bottom-title';
32612 cls += ' ' + this.cls;
32616 tag: (this.href.length) ? 'a' : 'div',
32621 cls: 'masonry-brick-mask'
32625 cls: 'masonry-brick-paragraph',
32631 if(this.href.length){
32632 cfg.href = this.href;
32635 var cn = cfg.cn[1].cn;
32637 if(this.title.length){
32640 cls: 'masonry-brick-title',
32645 if(this.html.length){
32648 cls: 'masonry-brick-text',
32653 if (!this.title.length && !this.html.length) {
32654 cfg.cn[1].cls += ' hide';
32657 if(this.bgimage.length){
32660 cls: 'masonry-brick-image-view',
32665 if(this.videourl.length){
32666 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32667 // youtube support only?
32670 cls: 'masonry-brick-image-view',
32673 allowfullscreen : true
32681 getSplitAutoCreate : function()
32683 var cls = 'masonry-brick masonry-brick-split';
32685 if(this.href.length){
32686 cls += ' masonry-brick-link';
32689 if(this.bgimage.length){
32690 cls += ' masonry-brick-image';
32694 cls += ' masonry-' + this.size + '-brick';
32697 switch (this.placetitle) {
32699 cls += ' masonry-center-title';
32702 cls += ' masonry-bottom-title';
32705 if(!this.bgimage.length){
32706 cls += ' masonry-center-title';
32709 if(this.bgimage.length){
32710 cls += ' masonry-bottom-title';
32716 cls += ' ' + this.cls;
32720 tag: (this.href.length) ? 'a' : 'div',
32725 cls: 'masonry-brick-split-head',
32729 cls: 'masonry-brick-paragraph',
32736 cls: 'masonry-brick-split-body',
32742 if(this.href.length){
32743 cfg.href = this.href;
32746 if(this.title.length){
32747 cfg.cn[0].cn[0].cn.push({
32749 cls: 'masonry-brick-title',
32754 if(this.html.length){
32755 cfg.cn[1].cn.push({
32757 cls: 'masonry-brick-text',
32762 if(this.bgimage.length){
32763 cfg.cn[0].cn.push({
32765 cls: 'masonry-brick-image-view',
32770 if(this.videourl.length){
32771 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32772 // youtube support only?
32773 cfg.cn[0].cn.cn.push({
32775 cls: 'masonry-brick-image-view',
32778 allowfullscreen : true
32785 initEvents: function()
32787 switch (this.size) {
32820 this.el.on('touchstart', this.onTouchStart, this);
32821 this.el.on('touchmove', this.onTouchMove, this);
32822 this.el.on('touchend', this.onTouchEnd, this);
32823 this.el.on('contextmenu', this.onContextMenu, this);
32825 this.el.on('mouseenter' ,this.enter, this);
32826 this.el.on('mouseleave', this.leave, this);
32827 this.el.on('click', this.onClick, this);
32830 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32831 this.parent().bricks.push(this);
32836 onClick: function(e, el)
32838 var time = this.endTimer - this.startTimer;
32839 // Roo.log(e.preventDefault());
32842 e.preventDefault();
32847 if(!this.preventDefault){
32851 e.preventDefault();
32853 if (this.activeClass != '') {
32854 this.selectBrick();
32857 this.fireEvent('click', this, e);
32860 enter: function(e, el)
32862 e.preventDefault();
32864 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32868 if(this.bgimage.length && this.html.length){
32869 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32873 leave: function(e, el)
32875 e.preventDefault();
32877 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32881 if(this.bgimage.length && this.html.length){
32882 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32886 onTouchStart: function(e, el)
32888 // e.preventDefault();
32890 this.touchmoved = false;
32892 if(!this.isFitContainer){
32896 if(!this.bgimage.length || !this.html.length){
32900 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32902 this.timer = new Date().getTime();
32906 onTouchMove: function(e, el)
32908 this.touchmoved = true;
32911 onContextMenu : function(e,el)
32913 e.preventDefault();
32914 e.stopPropagation();
32918 onTouchEnd: function(e, el)
32920 // e.preventDefault();
32922 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32929 if(!this.bgimage.length || !this.html.length){
32931 if(this.href.length){
32932 window.location.href = this.href;
32938 if(!this.isFitContainer){
32942 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32944 window.location.href = this.href;
32947 //selection on single brick only
32948 selectBrick : function() {
32950 if (!this.parentId) {
32954 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32955 var index = m.selectedBrick.indexOf(this.id);
32958 m.selectedBrick.splice(index,1);
32959 this.el.removeClass(this.activeClass);
32963 for(var i = 0; i < m.selectedBrick.length; i++) {
32964 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32965 b.el.removeClass(b.activeClass);
32968 m.selectedBrick = [];
32970 m.selectedBrick.push(this.id);
32971 this.el.addClass(this.activeClass);
32975 isSelected : function(){
32976 return this.el.hasClass(this.activeClass);
32981 Roo.apply(Roo.bootstrap.MasonryBrick, {
32984 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32986 * register a Masonry Brick
32987 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32990 register : function(brick)
32992 //this.groups[brick.id] = brick;
32993 this.groups.add(brick.id, brick);
32996 * fetch a masonry brick based on the masonry brick ID
32997 * @param {string} the masonry brick to add
32998 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33001 get: function(brick_id)
33003 // if (typeof(this.groups[brick_id]) == 'undefined') {
33006 // return this.groups[brick_id] ;
33008 if(this.groups.key(brick_id)) {
33009 return this.groups.key(brick_id);
33027 * @class Roo.bootstrap.Brick
33028 * @extends Roo.bootstrap.Component
33029 * Bootstrap Brick class
33032 * Create a new Brick
33033 * @param {Object} config The config object
33036 Roo.bootstrap.Brick = function(config){
33037 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33043 * When a Brick is click
33044 * @param {Roo.bootstrap.Brick} this
33045 * @param {Roo.EventObject} e
33051 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33054 * @cfg {String} title
33058 * @cfg {String} html
33062 * @cfg {String} bgimage
33066 * @cfg {String} cls
33070 * @cfg {String} href
33074 * @cfg {String} video
33078 * @cfg {Boolean} square
33082 getAutoCreate : function()
33084 var cls = 'roo-brick';
33086 if(this.href.length){
33087 cls += ' roo-brick-link';
33090 if(this.bgimage.length){
33091 cls += ' roo-brick-image';
33094 if(!this.html.length && !this.bgimage.length){
33095 cls += ' roo-brick-center-title';
33098 if(!this.html.length && this.bgimage.length){
33099 cls += ' roo-brick-bottom-title';
33103 cls += ' ' + this.cls;
33107 tag: (this.href.length) ? 'a' : 'div',
33112 cls: 'roo-brick-paragraph',
33118 if(this.href.length){
33119 cfg.href = this.href;
33122 var cn = cfg.cn[0].cn;
33124 if(this.title.length){
33127 cls: 'roo-brick-title',
33132 if(this.html.length){
33135 cls: 'roo-brick-text',
33142 if(this.bgimage.length){
33145 cls: 'roo-brick-image-view',
33153 initEvents: function()
33155 if(this.title.length || this.html.length){
33156 this.el.on('mouseenter' ,this.enter, this);
33157 this.el.on('mouseleave', this.leave, this);
33160 Roo.EventManager.onWindowResize(this.resize, this);
33162 if(this.bgimage.length){
33163 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33164 this.imageEl.on('load', this.onImageLoad, this);
33171 onImageLoad : function()
33176 resize : function()
33178 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33180 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33182 if(this.bgimage.length){
33183 var image = this.el.select('.roo-brick-image-view', true).first();
33185 image.setWidth(paragraph.getWidth());
33188 image.setHeight(paragraph.getWidth());
33191 this.el.setHeight(image.getHeight());
33192 paragraph.setHeight(image.getHeight());
33198 enter: function(e, el)
33200 e.preventDefault();
33202 if(this.bgimage.length){
33203 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33204 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33208 leave: function(e, el)
33210 e.preventDefault();
33212 if(this.bgimage.length){
33213 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33214 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33229 * @class Roo.bootstrap.NumberField
33230 * @extends Roo.bootstrap.Input
33231 * Bootstrap NumberField class
33237 * Create a new NumberField
33238 * @param {Object} config The config object
33241 Roo.bootstrap.NumberField = function(config){
33242 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33245 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33248 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33250 allowDecimals : true,
33252 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33254 decimalSeparator : ".",
33256 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33258 decimalPrecision : 2,
33260 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33262 allowNegative : true,
33265 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33269 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33271 minValue : Number.NEGATIVE_INFINITY,
33273 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33275 maxValue : Number.MAX_VALUE,
33277 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33279 minText : "The minimum value for this field is {0}",
33281 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33283 maxText : "The maximum value for this field is {0}",
33285 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33286 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33288 nanText : "{0} is not a valid number",
33290 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33292 thousandsDelimiter : false,
33294 * @cfg {String} valueAlign alignment of value
33296 valueAlign : "left",
33298 getAutoCreate : function()
33300 var hiddenInput = {
33304 cls: 'hidden-number-input'
33308 hiddenInput.name = this.name;
33313 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33315 this.name = hiddenInput.name;
33317 if(cfg.cn.length > 0) {
33318 cfg.cn.push(hiddenInput);
33325 initEvents : function()
33327 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33329 var allowed = "0123456789";
33331 if(this.allowDecimals){
33332 allowed += this.decimalSeparator;
33335 if(this.allowNegative){
33339 if(this.thousandsDelimiter) {
33343 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33345 var keyPress = function(e){
33347 var k = e.getKey();
33349 var c = e.getCharCode();
33352 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33353 allowed.indexOf(String.fromCharCode(c)) === -1
33359 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33363 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33368 this.el.on("keypress", keyPress, this);
33371 validateValue : function(value)
33374 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33378 var num = this.parseValue(value);
33381 this.markInvalid(String.format(this.nanText, value));
33385 if(num < this.minValue){
33386 this.markInvalid(String.format(this.minText, this.minValue));
33390 if(num > this.maxValue){
33391 this.markInvalid(String.format(this.maxText, this.maxValue));
33398 getValue : function()
33400 var v = this.hiddenEl().getValue();
33402 return this.fixPrecision(this.parseValue(v));
33405 parseValue : function(value)
33407 if(this.thousandsDelimiter) {
33409 r = new RegExp(",", "g");
33410 value = value.replace(r, "");
33413 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33414 return isNaN(value) ? '' : value;
33417 fixPrecision : function(value)
33419 if(this.thousandsDelimiter) {
33421 r = new RegExp(",", "g");
33422 value = value.replace(r, "");
33425 var nan = isNaN(value);
33427 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33428 return nan ? '' : value;
33430 return parseFloat(value).toFixed(this.decimalPrecision);
33433 setValue : function(v)
33435 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33441 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33443 this.inputEl().dom.value = (v == '') ? '' :
33444 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33446 if(!this.allowZero && v === '0') {
33447 this.hiddenEl().dom.value = '';
33448 this.inputEl().dom.value = '';
33455 decimalPrecisionFcn : function(v)
33457 return Math.floor(v);
33460 beforeBlur : function()
33462 var v = this.parseValue(this.getRawValue());
33464 if(v || v === 0 || v === ''){
33469 hiddenEl : function()
33471 return this.el.select('input.hidden-number-input',true).first();
33483 * @class Roo.bootstrap.DocumentSlider
33484 * @extends Roo.bootstrap.Component
33485 * Bootstrap DocumentSlider class
33488 * Create a new DocumentViewer
33489 * @param {Object} config The config object
33492 Roo.bootstrap.DocumentSlider = function(config){
33493 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33500 * Fire after initEvent
33501 * @param {Roo.bootstrap.DocumentSlider} this
33506 * Fire after update
33507 * @param {Roo.bootstrap.DocumentSlider} this
33513 * @param {Roo.bootstrap.DocumentSlider} this
33519 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33525 getAutoCreate : function()
33529 cls : 'roo-document-slider',
33533 cls : 'roo-document-slider-header',
33537 cls : 'roo-document-slider-header-title'
33543 cls : 'roo-document-slider-body',
33547 cls : 'roo-document-slider-prev',
33551 cls : 'fa fa-chevron-left'
33557 cls : 'roo-document-slider-thumb',
33561 cls : 'roo-document-slider-image'
33567 cls : 'roo-document-slider-next',
33571 cls : 'fa fa-chevron-right'
33583 initEvents : function()
33585 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33586 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33588 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33589 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33591 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33592 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33594 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33595 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33597 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33598 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33600 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33601 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33603 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33604 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33606 this.thumbEl.on('click', this.onClick, this);
33608 this.prevIndicator.on('click', this.prev, this);
33610 this.nextIndicator.on('click', this.next, this);
33614 initial : function()
33616 if(this.files.length){
33617 this.indicator = 1;
33621 this.fireEvent('initial', this);
33624 update : function()
33626 this.imageEl.attr('src', this.files[this.indicator - 1]);
33628 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33630 this.prevIndicator.show();
33632 if(this.indicator == 1){
33633 this.prevIndicator.hide();
33636 this.nextIndicator.show();
33638 if(this.indicator == this.files.length){
33639 this.nextIndicator.hide();
33642 this.thumbEl.scrollTo('top');
33644 this.fireEvent('update', this);
33647 onClick : function(e)
33649 e.preventDefault();
33651 this.fireEvent('click', this);
33656 e.preventDefault();
33658 this.indicator = Math.max(1, this.indicator - 1);
33665 e.preventDefault();
33667 this.indicator = Math.min(this.files.length, this.indicator + 1);
33681 * @class Roo.bootstrap.RadioSet
33682 * @extends Roo.bootstrap.Input
33683 * Bootstrap RadioSet class
33684 * @cfg {String} indicatorpos (left|right) default left
33685 * @cfg {Boolean} inline (true|false) inline the element (default true)
33686 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33688 * Create a new RadioSet
33689 * @param {Object} config The config object
33692 Roo.bootstrap.RadioSet = function(config){
33694 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33698 Roo.bootstrap.RadioSet.register(this);
33703 * Fires when the element is checked or unchecked.
33704 * @param {Roo.bootstrap.RadioSet} this This radio
33705 * @param {Roo.bootstrap.Radio} item The checked item
33710 * Fires when the element is click.
33711 * @param {Roo.bootstrap.RadioSet} this This radio set
33712 * @param {Roo.bootstrap.Radio} item The checked item
33713 * @param {Roo.EventObject} e The event object
33720 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33728 indicatorpos : 'left',
33730 getAutoCreate : function()
33734 cls : 'roo-radio-set-label',
33738 html : this.fieldLabel
33743 if(this.indicatorpos == 'left'){
33746 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33747 tooltip : 'This field is required'
33752 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33753 tooltip : 'This field is required'
33759 cls : 'roo-radio-set-items'
33762 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33764 if (align === 'left' && this.fieldLabel.length) {
33767 cls : "roo-radio-set-right",
33773 if(this.labelWidth > 12){
33774 label.style = "width: " + this.labelWidth + 'px';
33777 if(this.labelWidth < 13 && this.labelmd == 0){
33778 this.labelmd = this.labelWidth;
33781 if(this.labellg > 0){
33782 label.cls += ' col-lg-' + this.labellg;
33783 items.cls += ' col-lg-' + (12 - this.labellg);
33786 if(this.labelmd > 0){
33787 label.cls += ' col-md-' + this.labelmd;
33788 items.cls += ' col-md-' + (12 - this.labelmd);
33791 if(this.labelsm > 0){
33792 label.cls += ' col-sm-' + this.labelsm;
33793 items.cls += ' col-sm-' + (12 - this.labelsm);
33796 if(this.labelxs > 0){
33797 label.cls += ' col-xs-' + this.labelxs;
33798 items.cls += ' col-xs-' + (12 - this.labelxs);
33804 cls : 'roo-radio-set',
33808 cls : 'roo-radio-set-input',
33811 value : this.value ? this.value : ''
33818 if(this.weight.length){
33819 cfg.cls += ' roo-radio-' + this.weight;
33823 cfg.cls += ' roo-radio-set-inline';
33827 ['xs','sm','md','lg'].map(function(size){
33828 if (settings[size]) {
33829 cfg.cls += ' col-' + size + '-' + settings[size];
33837 initEvents : function()
33839 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33840 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33842 if(!this.fieldLabel.length){
33843 this.labelEl.hide();
33846 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33847 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33849 this.indicator = this.indicatorEl();
33851 if(this.indicator){
33852 this.indicator.addClass('invisible');
33855 this.originalValue = this.getValue();
33859 inputEl: function ()
33861 return this.el.select('.roo-radio-set-input', true).first();
33864 getChildContainer : function()
33866 return this.itemsEl;
33869 register : function(item)
33871 this.radioes.push(item);
33875 validate : function()
33877 if(this.getVisibilityEl().hasClass('hidden')){
33883 Roo.each(this.radioes, function(i){
33892 if(this.allowBlank) {
33896 if(this.disabled || valid){
33901 this.markInvalid();
33906 markValid : function()
33908 if(this.labelEl.isVisible(true)){
33909 this.indicatorEl().removeClass('visible');
33910 this.indicatorEl().addClass('invisible');
33913 this.el.removeClass([this.invalidClass, this.validClass]);
33914 this.el.addClass(this.validClass);
33916 this.fireEvent('valid', this);
33919 markInvalid : function(msg)
33921 if(this.allowBlank || this.disabled){
33925 if(this.labelEl.isVisible(true)){
33926 this.indicatorEl().removeClass('invisible');
33927 this.indicatorEl().addClass('visible');
33930 this.el.removeClass([this.invalidClass, this.validClass]);
33931 this.el.addClass(this.invalidClass);
33933 this.fireEvent('invalid', this, msg);
33937 setValue : function(v, suppressEvent)
33939 if(this.value === v){
33946 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33949 Roo.each(this.radioes, function(i){
33951 i.el.removeClass('checked');
33954 Roo.each(this.radioes, function(i){
33956 if(i.value === v || i.value.toString() === v.toString()){
33958 i.el.addClass('checked');
33960 if(suppressEvent !== true){
33961 this.fireEvent('check', this, i);
33972 clearInvalid : function(){
33974 if(!this.el || this.preventMark){
33978 this.el.removeClass([this.invalidClass]);
33980 this.fireEvent('valid', this);
33985 Roo.apply(Roo.bootstrap.RadioSet, {
33989 register : function(set)
33991 this.groups[set.name] = set;
33994 get: function(name)
33996 if (typeof(this.groups[name]) == 'undefined') {
34000 return this.groups[name] ;
34006 * Ext JS Library 1.1.1
34007 * Copyright(c) 2006-2007, Ext JS, LLC.
34009 * Originally Released Under LGPL - original licence link has changed is not relivant.
34012 * <script type="text/javascript">
34017 * @class Roo.bootstrap.SplitBar
34018 * @extends Roo.util.Observable
34019 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34023 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34024 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34025 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34026 split.minSize = 100;
34027 split.maxSize = 600;
34028 split.animate = true;
34029 split.on('moved', splitterMoved);
34032 * Create a new SplitBar
34033 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34034 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34035 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34036 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34037 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34038 position of the SplitBar).
34040 Roo.bootstrap.SplitBar = function(cfg){
34045 // dragElement : elm
34046 // resizingElement: el,
34048 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34049 // placement : Roo.bootstrap.SplitBar.LEFT ,
34050 // existingProxy ???
34053 this.el = Roo.get(cfg.dragElement, true);
34054 this.el.dom.unselectable = "on";
34056 this.resizingEl = Roo.get(cfg.resizingElement, true);
34060 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34061 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34064 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34067 * The minimum size of the resizing element. (Defaults to 0)
34073 * The maximum size of the resizing element. (Defaults to 2000)
34076 this.maxSize = 2000;
34079 * Whether to animate the transition to the new size
34082 this.animate = false;
34085 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34088 this.useShim = false;
34093 if(!cfg.existingProxy){
34095 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34097 this.proxy = Roo.get(cfg.existingProxy).dom;
34100 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34103 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34106 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34109 this.dragSpecs = {};
34112 * @private The adapter to use to positon and resize elements
34114 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34115 this.adapter.init(this);
34117 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34119 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34120 this.el.addClass("roo-splitbar-h");
34123 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34124 this.el.addClass("roo-splitbar-v");
34130 * Fires when the splitter is moved (alias for {@link #event-moved})
34131 * @param {Roo.bootstrap.SplitBar} this
34132 * @param {Number} newSize the new width or height
34137 * Fires when the splitter is moved
34138 * @param {Roo.bootstrap.SplitBar} this
34139 * @param {Number} newSize the new width or height
34143 * @event beforeresize
34144 * Fires before the splitter is dragged
34145 * @param {Roo.bootstrap.SplitBar} this
34147 "beforeresize" : true,
34149 "beforeapply" : true
34152 Roo.util.Observable.call(this);
34155 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34156 onStartProxyDrag : function(x, y){
34157 this.fireEvent("beforeresize", this);
34159 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34161 o.enableDisplayMode("block");
34162 // all splitbars share the same overlay
34163 Roo.bootstrap.SplitBar.prototype.overlay = o;
34165 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34166 this.overlay.show();
34167 Roo.get(this.proxy).setDisplayed("block");
34168 var size = this.adapter.getElementSize(this);
34169 this.activeMinSize = this.getMinimumSize();;
34170 this.activeMaxSize = this.getMaximumSize();;
34171 var c1 = size - this.activeMinSize;
34172 var c2 = Math.max(this.activeMaxSize - size, 0);
34173 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34174 this.dd.resetConstraints();
34175 this.dd.setXConstraint(
34176 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34177 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34179 this.dd.setYConstraint(0, 0);
34181 this.dd.resetConstraints();
34182 this.dd.setXConstraint(0, 0);
34183 this.dd.setYConstraint(
34184 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34185 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34188 this.dragSpecs.startSize = size;
34189 this.dragSpecs.startPoint = [x, y];
34190 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34194 * @private Called after the drag operation by the DDProxy
34196 onEndProxyDrag : function(e){
34197 Roo.get(this.proxy).setDisplayed(false);
34198 var endPoint = Roo.lib.Event.getXY(e);
34200 this.overlay.hide();
34203 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34204 newSize = this.dragSpecs.startSize +
34205 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34206 endPoint[0] - this.dragSpecs.startPoint[0] :
34207 this.dragSpecs.startPoint[0] - endPoint[0]
34210 newSize = this.dragSpecs.startSize +
34211 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34212 endPoint[1] - this.dragSpecs.startPoint[1] :
34213 this.dragSpecs.startPoint[1] - endPoint[1]
34216 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34217 if(newSize != this.dragSpecs.startSize){
34218 if(this.fireEvent('beforeapply', this, newSize) !== false){
34219 this.adapter.setElementSize(this, newSize);
34220 this.fireEvent("moved", this, newSize);
34221 this.fireEvent("resize", this, newSize);
34227 * Get the adapter this SplitBar uses
34228 * @return The adapter object
34230 getAdapter : function(){
34231 return this.adapter;
34235 * Set the adapter this SplitBar uses
34236 * @param {Object} adapter A SplitBar adapter object
34238 setAdapter : function(adapter){
34239 this.adapter = adapter;
34240 this.adapter.init(this);
34244 * Gets the minimum size for the resizing element
34245 * @return {Number} The minimum size
34247 getMinimumSize : function(){
34248 return this.minSize;
34252 * Sets the minimum size for the resizing element
34253 * @param {Number} minSize The minimum size
34255 setMinimumSize : function(minSize){
34256 this.minSize = minSize;
34260 * Gets the maximum size for the resizing element
34261 * @return {Number} The maximum size
34263 getMaximumSize : function(){
34264 return this.maxSize;
34268 * Sets the maximum size for the resizing element
34269 * @param {Number} maxSize The maximum size
34271 setMaximumSize : function(maxSize){
34272 this.maxSize = maxSize;
34276 * Sets the initialize size for the resizing element
34277 * @param {Number} size The initial size
34279 setCurrentSize : function(size){
34280 var oldAnimate = this.animate;
34281 this.animate = false;
34282 this.adapter.setElementSize(this, size);
34283 this.animate = oldAnimate;
34287 * Destroy this splitbar.
34288 * @param {Boolean} removeEl True to remove the element
34290 destroy : function(removeEl){
34292 this.shim.remove();
34295 this.proxy.parentNode.removeChild(this.proxy);
34303 * @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.
34305 Roo.bootstrap.SplitBar.createProxy = function(dir){
34306 var proxy = new Roo.Element(document.createElement("div"));
34307 proxy.unselectable();
34308 var cls = 'roo-splitbar-proxy';
34309 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34310 document.body.appendChild(proxy.dom);
34315 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34316 * Default Adapter. It assumes the splitter and resizing element are not positioned
34317 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34319 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34322 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34323 // do nothing for now
34324 init : function(s){
34328 * Called before drag operations to get the current size of the resizing element.
34329 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34331 getElementSize : function(s){
34332 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34333 return s.resizingEl.getWidth();
34335 return s.resizingEl.getHeight();
34340 * Called after drag operations to set the size of the resizing element.
34341 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34342 * @param {Number} newSize The new size to set
34343 * @param {Function} onComplete A function to be invoked when resizing is complete
34345 setElementSize : function(s, newSize, onComplete){
34346 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34348 s.resizingEl.setWidth(newSize);
34350 onComplete(s, newSize);
34353 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34358 s.resizingEl.setHeight(newSize);
34360 onComplete(s, newSize);
34363 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34370 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34371 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34372 * Adapter that moves the splitter element to align with the resized sizing element.
34373 * Used with an absolute positioned SplitBar.
34374 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34375 * document.body, make sure you assign an id to the body element.
34377 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34378 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34379 this.container = Roo.get(container);
34382 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34383 init : function(s){
34384 this.basic.init(s);
34387 getElementSize : function(s){
34388 return this.basic.getElementSize(s);
34391 setElementSize : function(s, newSize, onComplete){
34392 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34395 moveSplitter : function(s){
34396 var yes = Roo.bootstrap.SplitBar;
34397 switch(s.placement){
34399 s.el.setX(s.resizingEl.getRight());
34402 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34405 s.el.setY(s.resizingEl.getBottom());
34408 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34415 * Orientation constant - Create a vertical SplitBar
34419 Roo.bootstrap.SplitBar.VERTICAL = 1;
34422 * Orientation constant - Create a horizontal SplitBar
34426 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34429 * Placement constant - The resizing element is to the left of the splitter element
34433 Roo.bootstrap.SplitBar.LEFT = 1;
34436 * Placement constant - The resizing element is to the right of the splitter element
34440 Roo.bootstrap.SplitBar.RIGHT = 2;
34443 * Placement constant - The resizing element is positioned above the splitter element
34447 Roo.bootstrap.SplitBar.TOP = 3;
34450 * Placement constant - The resizing element is positioned under splitter element
34454 Roo.bootstrap.SplitBar.BOTTOM = 4;
34455 Roo.namespace("Roo.bootstrap.layout");/*
34457 * Ext JS Library 1.1.1
34458 * Copyright(c) 2006-2007, Ext JS, LLC.
34460 * Originally Released Under LGPL - original licence link has changed is not relivant.
34463 * <script type="text/javascript">
34467 * @class Roo.bootstrap.layout.Manager
34468 * @extends Roo.bootstrap.Component
34469 * Base class for layout managers.
34471 Roo.bootstrap.layout.Manager = function(config)
34473 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34479 /** false to disable window resize monitoring @type Boolean */
34480 this.monitorWindowResize = true;
34485 * Fires when a layout is performed.
34486 * @param {Roo.LayoutManager} this
34490 * @event regionresized
34491 * Fires when the user resizes a region.
34492 * @param {Roo.LayoutRegion} region The resized region
34493 * @param {Number} newSize The new size (width for east/west, height for north/south)
34495 "regionresized" : true,
34497 * @event regioncollapsed
34498 * Fires when a region is collapsed.
34499 * @param {Roo.LayoutRegion} region The collapsed region
34501 "regioncollapsed" : true,
34503 * @event regionexpanded
34504 * Fires when a region is expanded.
34505 * @param {Roo.LayoutRegion} region The expanded region
34507 "regionexpanded" : true
34509 this.updating = false;
34512 this.el = Roo.get(config.el);
34518 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34523 monitorWindowResize : true,
34529 onRender : function(ct, position)
34532 this.el = Roo.get(ct);
34535 //this.fireEvent('render',this);
34539 initEvents: function()
34543 // ie scrollbar fix
34544 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34545 document.body.scroll = "no";
34546 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34547 this.el.position('relative');
34549 this.id = this.el.id;
34550 this.el.addClass("roo-layout-container");
34551 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34552 if(this.el.dom != document.body ) {
34553 this.el.on('resize', this.layout,this);
34554 this.el.on('show', this.layout,this);
34560 * Returns true if this layout is currently being updated
34561 * @return {Boolean}
34563 isUpdating : function(){
34564 return this.updating;
34568 * Suspend the LayoutManager from doing auto-layouts while
34569 * making multiple add or remove calls
34571 beginUpdate : function(){
34572 this.updating = true;
34576 * Restore auto-layouts and optionally disable the manager from performing a layout
34577 * @param {Boolean} noLayout true to disable a layout update
34579 endUpdate : function(noLayout){
34580 this.updating = false;
34586 layout: function(){
34590 onRegionResized : function(region, newSize){
34591 this.fireEvent("regionresized", region, newSize);
34595 onRegionCollapsed : function(region){
34596 this.fireEvent("regioncollapsed", region);
34599 onRegionExpanded : function(region){
34600 this.fireEvent("regionexpanded", region);
34604 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34605 * performs box-model adjustments.
34606 * @return {Object} The size as an object {width: (the width), height: (the height)}
34608 getViewSize : function()
34611 if(this.el.dom != document.body){
34612 size = this.el.getSize();
34614 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34616 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34617 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34622 * Returns the Element this layout is bound to.
34623 * @return {Roo.Element}
34625 getEl : function(){
34630 * Returns the specified region.
34631 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34632 * @return {Roo.LayoutRegion}
34634 getRegion : function(target){
34635 return this.regions[target.toLowerCase()];
34638 onWindowResize : function(){
34639 if(this.monitorWindowResize){
34646 * Ext JS Library 1.1.1
34647 * Copyright(c) 2006-2007, Ext JS, LLC.
34649 * Originally Released Under LGPL - original licence link has changed is not relivant.
34652 * <script type="text/javascript">
34655 * @class Roo.bootstrap.layout.Border
34656 * @extends Roo.bootstrap.layout.Manager
34657 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34658 * please see: examples/bootstrap/nested.html<br><br>
34660 <b>The container the layout is rendered into can be either the body element or any other element.
34661 If it is not the body element, the container needs to either be an absolute positioned element,
34662 or you will need to add "position:relative" to the css of the container. You will also need to specify
34663 the container size if it is not the body element.</b>
34666 * Create a new Border
34667 * @param {Object} config Configuration options
34669 Roo.bootstrap.layout.Border = function(config){
34670 config = config || {};
34671 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34675 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34676 if(config[region]){
34677 config[region].region = region;
34678 this.addRegion(config[region]);
34684 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34686 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34688 * Creates and adds a new region if it doesn't already exist.
34689 * @param {String} target The target region key (north, south, east, west or center).
34690 * @param {Object} config The regions config object
34691 * @return {BorderLayoutRegion} The new region
34693 addRegion : function(config)
34695 if(!this.regions[config.region]){
34696 var r = this.factory(config);
34697 this.bindRegion(r);
34699 return this.regions[config.region];
34703 bindRegion : function(r){
34704 this.regions[r.config.region] = r;
34706 r.on("visibilitychange", this.layout, this);
34707 r.on("paneladded", this.layout, this);
34708 r.on("panelremoved", this.layout, this);
34709 r.on("invalidated", this.layout, this);
34710 r.on("resized", this.onRegionResized, this);
34711 r.on("collapsed", this.onRegionCollapsed, this);
34712 r.on("expanded", this.onRegionExpanded, this);
34716 * Performs a layout update.
34718 layout : function()
34720 if(this.updating) {
34724 // render all the rebions if they have not been done alreayd?
34725 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34726 if(this.regions[region] && !this.regions[region].bodyEl){
34727 this.regions[region].onRender(this.el)
34731 var size = this.getViewSize();
34732 var w = size.width;
34733 var h = size.height;
34738 //var x = 0, y = 0;
34740 var rs = this.regions;
34741 var north = rs["north"];
34742 var south = rs["south"];
34743 var west = rs["west"];
34744 var east = rs["east"];
34745 var center = rs["center"];
34746 //if(this.hideOnLayout){ // not supported anymore
34747 //c.el.setStyle("display", "none");
34749 if(north && north.isVisible()){
34750 var b = north.getBox();
34751 var m = north.getMargins();
34752 b.width = w - (m.left+m.right);
34755 centerY = b.height + b.y + m.bottom;
34756 centerH -= centerY;
34757 north.updateBox(this.safeBox(b));
34759 if(south && south.isVisible()){
34760 var b = south.getBox();
34761 var m = south.getMargins();
34762 b.width = w - (m.left+m.right);
34764 var totalHeight = (b.height + m.top + m.bottom);
34765 b.y = h - totalHeight + m.top;
34766 centerH -= totalHeight;
34767 south.updateBox(this.safeBox(b));
34769 if(west && west.isVisible()){
34770 var b = west.getBox();
34771 var m = west.getMargins();
34772 b.height = centerH - (m.top+m.bottom);
34774 b.y = centerY + m.top;
34775 var totalWidth = (b.width + m.left + m.right);
34776 centerX += totalWidth;
34777 centerW -= totalWidth;
34778 west.updateBox(this.safeBox(b));
34780 if(east && east.isVisible()){
34781 var b = east.getBox();
34782 var m = east.getMargins();
34783 b.height = centerH - (m.top+m.bottom);
34784 var totalWidth = (b.width + m.left + m.right);
34785 b.x = w - totalWidth + m.left;
34786 b.y = centerY + m.top;
34787 centerW -= totalWidth;
34788 east.updateBox(this.safeBox(b));
34791 var m = center.getMargins();
34793 x: centerX + m.left,
34794 y: centerY + m.top,
34795 width: centerW - (m.left+m.right),
34796 height: centerH - (m.top+m.bottom)
34798 //if(this.hideOnLayout){
34799 //center.el.setStyle("display", "block");
34801 center.updateBox(this.safeBox(centerBox));
34804 this.fireEvent("layout", this);
34808 safeBox : function(box){
34809 box.width = Math.max(0, box.width);
34810 box.height = Math.max(0, box.height);
34815 * Adds a ContentPanel (or subclass) to this layout.
34816 * @param {String} target The target region key (north, south, east, west or center).
34817 * @param {Roo.ContentPanel} panel The panel to add
34818 * @return {Roo.ContentPanel} The added panel
34820 add : function(target, panel){
34822 target = target.toLowerCase();
34823 return this.regions[target].add(panel);
34827 * Remove a ContentPanel (or subclass) to this layout.
34828 * @param {String} target The target region key (north, south, east, west or center).
34829 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34830 * @return {Roo.ContentPanel} The removed panel
34832 remove : function(target, panel){
34833 target = target.toLowerCase();
34834 return this.regions[target].remove(panel);
34838 * Searches all regions for a panel with the specified id
34839 * @param {String} panelId
34840 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34842 findPanel : function(panelId){
34843 var rs = this.regions;
34844 for(var target in rs){
34845 if(typeof rs[target] != "function"){
34846 var p = rs[target].getPanel(panelId);
34856 * Searches all regions for a panel with the specified id and activates (shows) it.
34857 * @param {String/ContentPanel} panelId The panels id or the panel itself
34858 * @return {Roo.ContentPanel} The shown panel or null
34860 showPanel : function(panelId) {
34861 var rs = this.regions;
34862 for(var target in rs){
34863 var r = rs[target];
34864 if(typeof r != "function"){
34865 if(r.hasPanel(panelId)){
34866 return r.showPanel(panelId);
34874 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34875 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34878 restoreState : function(provider){
34880 provider = Roo.state.Manager;
34882 var sm = new Roo.LayoutStateManager();
34883 sm.init(this, provider);
34889 * Adds a xtype elements to the layout.
34893 xtype : 'ContentPanel',
34900 xtype : 'NestedLayoutPanel',
34906 items : [ ... list of content panels or nested layout panels.. ]
34910 * @param {Object} cfg Xtype definition of item to add.
34912 addxtype : function(cfg)
34914 // basically accepts a pannel...
34915 // can accept a layout region..!?!?
34916 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34919 // theory? children can only be panels??
34921 //if (!cfg.xtype.match(/Panel$/)) {
34926 if (typeof(cfg.region) == 'undefined') {
34927 Roo.log("Failed to add Panel, region was not set");
34931 var region = cfg.region;
34937 xitems = cfg.items;
34944 case 'Content': // ContentPanel (el, cfg)
34945 case 'Scroll': // ContentPanel (el, cfg)
34947 cfg.autoCreate = true;
34948 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34950 // var el = this.el.createChild();
34951 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34954 this.add(region, ret);
34958 case 'TreePanel': // our new panel!
34959 cfg.el = this.el.createChild();
34960 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34961 this.add(region, ret);
34966 // create a new Layout (which is a Border Layout...
34968 var clayout = cfg.layout;
34969 clayout.el = this.el.createChild();
34970 clayout.items = clayout.items || [];
34974 // replace this exitems with the clayout ones..
34975 xitems = clayout.items;
34977 // force background off if it's in center...
34978 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34979 cfg.background = false;
34981 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34984 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34985 //console.log('adding nested layout panel ' + cfg.toSource());
34986 this.add(region, ret);
34987 nb = {}; /// find first...
34992 // needs grid and region
34994 //var el = this.getRegion(region).el.createChild();
34996 *var el = this.el.createChild();
34997 // create the grid first...
34998 cfg.grid.container = el;
34999 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35002 if (region == 'center' && this.active ) {
35003 cfg.background = false;
35006 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35008 this.add(region, ret);
35010 if (cfg.background) {
35011 // render grid on panel activation (if panel background)
35012 ret.on('activate', function(gp) {
35013 if (!gp.grid.rendered) {
35014 // gp.grid.render(el);
35018 // cfg.grid.render(el);
35024 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35025 // it was the old xcomponent building that caused this before.
35026 // espeically if border is the top element in the tree.
35036 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35038 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35039 this.add(region, ret);
35043 throw "Can not add '" + cfg.xtype + "' to Border";
35049 this.beginUpdate();
35053 Roo.each(xitems, function(i) {
35054 region = nb && i.region ? i.region : false;
35056 var add = ret.addxtype(i);
35059 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35060 if (!i.background) {
35061 abn[region] = nb[region] ;
35068 // make the last non-background panel active..
35069 //if (nb) { Roo.log(abn); }
35072 for(var r in abn) {
35073 region = this.getRegion(r);
35075 // tried using nb[r], but it does not work..
35077 region.showPanel(abn[r]);
35088 factory : function(cfg)
35091 var validRegions = Roo.bootstrap.layout.Border.regions;
35093 var target = cfg.region;
35096 var r = Roo.bootstrap.layout;
35100 return new r.North(cfg);
35102 return new r.South(cfg);
35104 return new r.East(cfg);
35106 return new r.West(cfg);
35108 return new r.Center(cfg);
35110 throw 'Layout region "'+target+'" not supported.';
35117 * Ext JS Library 1.1.1
35118 * Copyright(c) 2006-2007, Ext JS, LLC.
35120 * Originally Released Under LGPL - original licence link has changed is not relivant.
35123 * <script type="text/javascript">
35127 * @class Roo.bootstrap.layout.Basic
35128 * @extends Roo.util.Observable
35129 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35130 * and does not have a titlebar, tabs or any other features. All it does is size and position
35131 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35132 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35133 * @cfg {string} region the region that it inhabits..
35134 * @cfg {bool} skipConfig skip config?
35138 Roo.bootstrap.layout.Basic = function(config){
35140 this.mgr = config.mgr;
35142 this.position = config.region;
35144 var skipConfig = config.skipConfig;
35148 * @scope Roo.BasicLayoutRegion
35152 * @event beforeremove
35153 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35154 * @param {Roo.LayoutRegion} this
35155 * @param {Roo.ContentPanel} panel The panel
35156 * @param {Object} e The cancel event object
35158 "beforeremove" : true,
35160 * @event invalidated
35161 * Fires when the layout for this region is changed.
35162 * @param {Roo.LayoutRegion} this
35164 "invalidated" : true,
35166 * @event visibilitychange
35167 * Fires when this region is shown or hidden
35168 * @param {Roo.LayoutRegion} this
35169 * @param {Boolean} visibility true or false
35171 "visibilitychange" : true,
35173 * @event paneladded
35174 * Fires when a panel is added.
35175 * @param {Roo.LayoutRegion} this
35176 * @param {Roo.ContentPanel} panel The panel
35178 "paneladded" : true,
35180 * @event panelremoved
35181 * Fires when a panel is removed.
35182 * @param {Roo.LayoutRegion} this
35183 * @param {Roo.ContentPanel} panel The panel
35185 "panelremoved" : true,
35187 * @event beforecollapse
35188 * Fires when this region before collapse.
35189 * @param {Roo.LayoutRegion} this
35191 "beforecollapse" : true,
35194 * Fires when this region is collapsed.
35195 * @param {Roo.LayoutRegion} this
35197 "collapsed" : true,
35200 * Fires when this region is expanded.
35201 * @param {Roo.LayoutRegion} this
35206 * Fires when this region is slid into view.
35207 * @param {Roo.LayoutRegion} this
35209 "slideshow" : true,
35212 * Fires when this region slides out of view.
35213 * @param {Roo.LayoutRegion} this
35215 "slidehide" : true,
35217 * @event panelactivated
35218 * Fires when a panel is activated.
35219 * @param {Roo.LayoutRegion} this
35220 * @param {Roo.ContentPanel} panel The activated panel
35222 "panelactivated" : true,
35225 * Fires when the user resizes this region.
35226 * @param {Roo.LayoutRegion} this
35227 * @param {Number} newSize The new size (width for east/west, height for north/south)
35231 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35232 this.panels = new Roo.util.MixedCollection();
35233 this.panels.getKey = this.getPanelId.createDelegate(this);
35235 this.activePanel = null;
35236 // ensure listeners are added...
35238 if (config.listeners || config.events) {
35239 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35240 listeners : config.listeners || {},
35241 events : config.events || {}
35245 if(skipConfig !== true){
35246 this.applyConfig(config);
35250 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35252 getPanelId : function(p){
35256 applyConfig : function(config){
35257 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35258 this.config = config;
35263 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35264 * the width, for horizontal (north, south) the height.
35265 * @param {Number} newSize The new width or height
35267 resizeTo : function(newSize){
35268 var el = this.el ? this.el :
35269 (this.activePanel ? this.activePanel.getEl() : null);
35271 switch(this.position){
35274 el.setWidth(newSize);
35275 this.fireEvent("resized", this, newSize);
35279 el.setHeight(newSize);
35280 this.fireEvent("resized", this, newSize);
35286 getBox : function(){
35287 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35290 getMargins : function(){
35291 return this.margins;
35294 updateBox : function(box){
35296 var el = this.activePanel.getEl();
35297 el.dom.style.left = box.x + "px";
35298 el.dom.style.top = box.y + "px";
35299 this.activePanel.setSize(box.width, box.height);
35303 * Returns the container element for this region.
35304 * @return {Roo.Element}
35306 getEl : function(){
35307 return this.activePanel;
35311 * Returns true if this region is currently visible.
35312 * @return {Boolean}
35314 isVisible : function(){
35315 return this.activePanel ? true : false;
35318 setActivePanel : function(panel){
35319 panel = this.getPanel(panel);
35320 if(this.activePanel && this.activePanel != panel){
35321 this.activePanel.setActiveState(false);
35322 this.activePanel.getEl().setLeftTop(-10000,-10000);
35324 this.activePanel = panel;
35325 panel.setActiveState(true);
35327 panel.setSize(this.box.width, this.box.height);
35329 this.fireEvent("panelactivated", this, panel);
35330 this.fireEvent("invalidated");
35334 * Show the specified panel.
35335 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35336 * @return {Roo.ContentPanel} The shown panel or null
35338 showPanel : function(panel){
35339 panel = this.getPanel(panel);
35341 this.setActivePanel(panel);
35347 * Get the active panel for this region.
35348 * @return {Roo.ContentPanel} The active panel or null
35350 getActivePanel : function(){
35351 return this.activePanel;
35355 * Add the passed ContentPanel(s)
35356 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35357 * @return {Roo.ContentPanel} The panel added (if only one was added)
35359 add : function(panel){
35360 if(arguments.length > 1){
35361 for(var i = 0, len = arguments.length; i < len; i++) {
35362 this.add(arguments[i]);
35366 if(this.hasPanel(panel)){
35367 this.showPanel(panel);
35370 var el = panel.getEl();
35371 if(el.dom.parentNode != this.mgr.el.dom){
35372 this.mgr.el.dom.appendChild(el.dom);
35374 if(panel.setRegion){
35375 panel.setRegion(this);
35377 this.panels.add(panel);
35378 el.setStyle("position", "absolute");
35379 if(!panel.background){
35380 this.setActivePanel(panel);
35381 if(this.config.initialSize && this.panels.getCount()==1){
35382 this.resizeTo(this.config.initialSize);
35385 this.fireEvent("paneladded", this, panel);
35390 * Returns true if the panel is in this region.
35391 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35392 * @return {Boolean}
35394 hasPanel : function(panel){
35395 if(typeof panel == "object"){ // must be panel obj
35396 panel = panel.getId();
35398 return this.getPanel(panel) ? true : false;
35402 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35403 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35404 * @param {Boolean} preservePanel Overrides the config preservePanel option
35405 * @return {Roo.ContentPanel} The panel that was removed
35407 remove : function(panel, preservePanel){
35408 panel = this.getPanel(panel);
35413 this.fireEvent("beforeremove", this, panel, e);
35414 if(e.cancel === true){
35417 var panelId = panel.getId();
35418 this.panels.removeKey(panelId);
35423 * Returns the panel specified or null if it's not in this region.
35424 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35425 * @return {Roo.ContentPanel}
35427 getPanel : function(id){
35428 if(typeof id == "object"){ // must be panel obj
35431 return this.panels.get(id);
35435 * Returns this regions position (north/south/east/west/center).
35438 getPosition: function(){
35439 return this.position;
35443 * Ext JS Library 1.1.1
35444 * Copyright(c) 2006-2007, Ext JS, LLC.
35446 * Originally Released Under LGPL - original licence link has changed is not relivant.
35449 * <script type="text/javascript">
35453 * @class Roo.bootstrap.layout.Region
35454 * @extends Roo.bootstrap.layout.Basic
35455 * This class represents a region in a layout manager.
35457 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35458 * @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})
35459 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35460 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35461 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35462 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35463 * @cfg {String} title The title for the region (overrides panel titles)
35464 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35465 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35466 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35467 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35468 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35469 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35470 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35471 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35472 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35473 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35475 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35476 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35477 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35478 * @cfg {Number} width For East/West panels
35479 * @cfg {Number} height For North/South panels
35480 * @cfg {Boolean} split To show the splitter
35481 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35483 * @cfg {string} cls Extra CSS classes to add to region
35485 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35486 * @cfg {string} region the region that it inhabits..
35489 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35490 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35492 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35493 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35494 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35496 Roo.bootstrap.layout.Region = function(config)
35498 this.applyConfig(config);
35500 var mgr = config.mgr;
35501 var pos = config.region;
35502 config.skipConfig = true;
35503 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35506 this.onRender(mgr.el);
35509 this.visible = true;
35510 this.collapsed = false;
35511 this.unrendered_panels = [];
35514 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35516 position: '', // set by wrapper (eg. north/south etc..)
35517 unrendered_panels : null, // unrendered panels.
35518 createBody : function(){
35519 /** This region's body element
35520 * @type Roo.Element */
35521 this.bodyEl = this.el.createChild({
35523 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35527 onRender: function(ctr, pos)
35529 var dh = Roo.DomHelper;
35530 /** This region's container element
35531 * @type Roo.Element */
35532 this.el = dh.append(ctr.dom, {
35534 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35536 /** This region's title element
35537 * @type Roo.Element */
35539 this.titleEl = dh.append(this.el.dom,
35542 unselectable: "on",
35543 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35545 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35546 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35549 this.titleEl.enableDisplayMode();
35550 /** This region's title text element
35551 * @type HTMLElement */
35552 this.titleTextEl = this.titleEl.dom.firstChild;
35553 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35555 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35556 this.closeBtn.enableDisplayMode();
35557 this.closeBtn.on("click", this.closeClicked, this);
35558 this.closeBtn.hide();
35560 this.createBody(this.config);
35561 if(this.config.hideWhenEmpty){
35563 this.on("paneladded", this.validateVisibility, this);
35564 this.on("panelremoved", this.validateVisibility, this);
35566 if(this.autoScroll){
35567 this.bodyEl.setStyle("overflow", "auto");
35569 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35571 //if(c.titlebar !== false){
35572 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35573 this.titleEl.hide();
35575 this.titleEl.show();
35576 if(this.config.title){
35577 this.titleTextEl.innerHTML = this.config.title;
35581 if(this.config.collapsed){
35582 this.collapse(true);
35584 if(this.config.hidden){
35588 if (this.unrendered_panels && this.unrendered_panels.length) {
35589 for (var i =0;i< this.unrendered_panels.length; i++) {
35590 this.add(this.unrendered_panels[i]);
35592 this.unrendered_panels = null;
35598 applyConfig : function(c)
35601 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35602 var dh = Roo.DomHelper;
35603 if(c.titlebar !== false){
35604 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35605 this.collapseBtn.on("click", this.collapse, this);
35606 this.collapseBtn.enableDisplayMode();
35608 if(c.showPin === true || this.showPin){
35609 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35610 this.stickBtn.enableDisplayMode();
35611 this.stickBtn.on("click", this.expand, this);
35612 this.stickBtn.hide();
35617 /** This region's collapsed element
35618 * @type Roo.Element */
35621 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35622 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35625 if(c.floatable !== false){
35626 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35627 this.collapsedEl.on("click", this.collapseClick, this);
35630 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35631 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35632 id: "message", unselectable: "on", style:{"float":"left"}});
35633 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35635 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35636 this.expandBtn.on("click", this.expand, this);
35640 if(this.collapseBtn){
35641 this.collapseBtn.setVisible(c.collapsible == true);
35644 this.cmargins = c.cmargins || this.cmargins ||
35645 (this.position == "west" || this.position == "east" ?
35646 {top: 0, left: 2, right:2, bottom: 0} :
35647 {top: 2, left: 0, right:0, bottom: 2});
35649 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35652 this.bottomTabs = c.tabPosition != "top";
35654 this.autoScroll = c.autoScroll || false;
35659 this.duration = c.duration || .30;
35660 this.slideDuration = c.slideDuration || .45;
35665 * Returns true if this region is currently visible.
35666 * @return {Boolean}
35668 isVisible : function(){
35669 return this.visible;
35673 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35674 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35676 //setCollapsedTitle : function(title){
35677 // title = title || " ";
35678 // if(this.collapsedTitleTextEl){
35679 // this.collapsedTitleTextEl.innerHTML = title;
35683 getBox : function(){
35685 // if(!this.collapsed){
35686 b = this.el.getBox(false, true);
35688 // b = this.collapsedEl.getBox(false, true);
35693 getMargins : function(){
35694 return this.margins;
35695 //return this.collapsed ? this.cmargins : this.margins;
35698 highlight : function(){
35699 this.el.addClass("x-layout-panel-dragover");
35702 unhighlight : function(){
35703 this.el.removeClass("x-layout-panel-dragover");
35706 updateBox : function(box)
35708 if (!this.bodyEl) {
35709 return; // not rendered yet..
35713 if(!this.collapsed){
35714 this.el.dom.style.left = box.x + "px";
35715 this.el.dom.style.top = box.y + "px";
35716 this.updateBody(box.width, box.height);
35718 this.collapsedEl.dom.style.left = box.x + "px";
35719 this.collapsedEl.dom.style.top = box.y + "px";
35720 this.collapsedEl.setSize(box.width, box.height);
35723 this.tabs.autoSizeTabs();
35727 updateBody : function(w, h)
35730 this.el.setWidth(w);
35731 w -= this.el.getBorderWidth("rl");
35732 if(this.config.adjustments){
35733 w += this.config.adjustments[0];
35736 if(h !== null && h > 0){
35737 this.el.setHeight(h);
35738 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35739 h -= this.el.getBorderWidth("tb");
35740 if(this.config.adjustments){
35741 h += this.config.adjustments[1];
35743 this.bodyEl.setHeight(h);
35745 h = this.tabs.syncHeight(h);
35748 if(this.panelSize){
35749 w = w !== null ? w : this.panelSize.width;
35750 h = h !== null ? h : this.panelSize.height;
35752 if(this.activePanel){
35753 var el = this.activePanel.getEl();
35754 w = w !== null ? w : el.getWidth();
35755 h = h !== null ? h : el.getHeight();
35756 this.panelSize = {width: w, height: h};
35757 this.activePanel.setSize(w, h);
35759 if(Roo.isIE && this.tabs){
35760 this.tabs.el.repaint();
35765 * Returns the container element for this region.
35766 * @return {Roo.Element}
35768 getEl : function(){
35773 * Hides this region.
35776 //if(!this.collapsed){
35777 this.el.dom.style.left = "-2000px";
35780 // this.collapsedEl.dom.style.left = "-2000px";
35781 // this.collapsedEl.hide();
35783 this.visible = false;
35784 this.fireEvent("visibilitychange", this, false);
35788 * Shows this region if it was previously hidden.
35791 //if(!this.collapsed){
35794 // this.collapsedEl.show();
35796 this.visible = true;
35797 this.fireEvent("visibilitychange", this, true);
35800 closeClicked : function(){
35801 if(this.activePanel){
35802 this.remove(this.activePanel);
35806 collapseClick : function(e){
35808 e.stopPropagation();
35811 e.stopPropagation();
35817 * Collapses this region.
35818 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35821 collapse : function(skipAnim, skipCheck = false){
35822 if(this.collapsed) {
35826 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35828 this.collapsed = true;
35830 this.split.el.hide();
35832 if(this.config.animate && skipAnim !== true){
35833 this.fireEvent("invalidated", this);
35834 this.animateCollapse();
35836 this.el.setLocation(-20000,-20000);
35838 this.collapsedEl.show();
35839 this.fireEvent("collapsed", this);
35840 this.fireEvent("invalidated", this);
35846 animateCollapse : function(){
35851 * Expands this region if it was previously collapsed.
35852 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35853 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35856 expand : function(e, skipAnim){
35858 e.stopPropagation();
35860 if(!this.collapsed || this.el.hasActiveFx()) {
35864 this.afterSlideIn();
35867 this.collapsed = false;
35868 if(this.config.animate && skipAnim !== true){
35869 this.animateExpand();
35873 this.split.el.show();
35875 this.collapsedEl.setLocation(-2000,-2000);
35876 this.collapsedEl.hide();
35877 this.fireEvent("invalidated", this);
35878 this.fireEvent("expanded", this);
35882 animateExpand : function(){
35886 initTabs : function()
35888 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35890 var ts = new Roo.bootstrap.panel.Tabs({
35891 el: this.bodyEl.dom,
35892 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35893 disableTooltips: this.config.disableTabTips,
35894 toolbar : this.config.toolbar
35897 if(this.config.hideTabs){
35898 ts.stripWrap.setDisplayed(false);
35901 ts.resizeTabs = this.config.resizeTabs === true;
35902 ts.minTabWidth = this.config.minTabWidth || 40;
35903 ts.maxTabWidth = this.config.maxTabWidth || 250;
35904 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35905 ts.monitorResize = false;
35906 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35907 ts.bodyEl.addClass('roo-layout-tabs-body');
35908 this.panels.each(this.initPanelAsTab, this);
35911 initPanelAsTab : function(panel){
35912 var ti = this.tabs.addTab(
35916 this.config.closeOnTab && panel.isClosable(),
35919 if(panel.tabTip !== undefined){
35920 ti.setTooltip(panel.tabTip);
35922 ti.on("activate", function(){
35923 this.setActivePanel(panel);
35926 if(this.config.closeOnTab){
35927 ti.on("beforeclose", function(t, e){
35929 this.remove(panel);
35933 panel.tabItem = ti;
35938 updatePanelTitle : function(panel, title)
35940 if(this.activePanel == panel){
35941 this.updateTitle(title);
35944 var ti = this.tabs.getTab(panel.getEl().id);
35946 if(panel.tabTip !== undefined){
35947 ti.setTooltip(panel.tabTip);
35952 updateTitle : function(title){
35953 if(this.titleTextEl && !this.config.title){
35954 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35958 setActivePanel : function(panel)
35960 panel = this.getPanel(panel);
35961 if(this.activePanel && this.activePanel != panel){
35962 if(this.activePanel.setActiveState(false) === false){
35966 this.activePanel = panel;
35967 panel.setActiveState(true);
35968 if(this.panelSize){
35969 panel.setSize(this.panelSize.width, this.panelSize.height);
35972 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35974 this.updateTitle(panel.getTitle());
35976 this.fireEvent("invalidated", this);
35978 this.fireEvent("panelactivated", this, panel);
35982 * Shows the specified panel.
35983 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35984 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35986 showPanel : function(panel)
35988 panel = this.getPanel(panel);
35991 var tab = this.tabs.getTab(panel.getEl().id);
35992 if(tab.isHidden()){
35993 this.tabs.unhideTab(tab.id);
35997 this.setActivePanel(panel);
36004 * Get the active panel for this region.
36005 * @return {Roo.ContentPanel} The active panel or null
36007 getActivePanel : function(){
36008 return this.activePanel;
36011 validateVisibility : function(){
36012 if(this.panels.getCount() < 1){
36013 this.updateTitle(" ");
36014 this.closeBtn.hide();
36017 if(!this.isVisible()){
36024 * Adds the passed ContentPanel(s) to this region.
36025 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36026 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36028 add : function(panel)
36030 if(arguments.length > 1){
36031 for(var i = 0, len = arguments.length; i < len; i++) {
36032 this.add(arguments[i]);
36037 // if we have not been rendered yet, then we can not really do much of this..
36038 if (!this.bodyEl) {
36039 this.unrendered_panels.push(panel);
36046 if(this.hasPanel(panel)){
36047 this.showPanel(panel);
36050 panel.setRegion(this);
36051 this.panels.add(panel);
36052 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36053 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36054 // and hide them... ???
36055 this.bodyEl.dom.appendChild(panel.getEl().dom);
36056 if(panel.background !== true){
36057 this.setActivePanel(panel);
36059 this.fireEvent("paneladded", this, panel);
36066 this.initPanelAsTab(panel);
36070 if(panel.background !== true){
36071 this.tabs.activate(panel.getEl().id);
36073 this.fireEvent("paneladded", this, panel);
36078 * Hides the tab for the specified panel.
36079 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36081 hidePanel : function(panel){
36082 if(this.tabs && (panel = this.getPanel(panel))){
36083 this.tabs.hideTab(panel.getEl().id);
36088 * Unhides the tab for a previously hidden panel.
36089 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36091 unhidePanel : function(panel){
36092 if(this.tabs && (panel = this.getPanel(panel))){
36093 this.tabs.unhideTab(panel.getEl().id);
36097 clearPanels : function(){
36098 while(this.panels.getCount() > 0){
36099 this.remove(this.panels.first());
36104 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36105 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36106 * @param {Boolean} preservePanel Overrides the config preservePanel option
36107 * @return {Roo.ContentPanel} The panel that was removed
36109 remove : function(panel, preservePanel)
36111 panel = this.getPanel(panel);
36116 this.fireEvent("beforeremove", this, panel, e);
36117 if(e.cancel === true){
36120 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36121 var panelId = panel.getId();
36122 this.panels.removeKey(panelId);
36124 document.body.appendChild(panel.getEl().dom);
36127 this.tabs.removeTab(panel.getEl().id);
36128 }else if (!preservePanel){
36129 this.bodyEl.dom.removeChild(panel.getEl().dom);
36131 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36132 var p = this.panels.first();
36133 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36134 tempEl.appendChild(p.getEl().dom);
36135 this.bodyEl.update("");
36136 this.bodyEl.dom.appendChild(p.getEl().dom);
36138 this.updateTitle(p.getTitle());
36140 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36141 this.setActivePanel(p);
36143 panel.setRegion(null);
36144 if(this.activePanel == panel){
36145 this.activePanel = null;
36147 if(this.config.autoDestroy !== false && preservePanel !== true){
36148 try{panel.destroy();}catch(e){}
36150 this.fireEvent("panelremoved", this, panel);
36155 * Returns the TabPanel component used by this region
36156 * @return {Roo.TabPanel}
36158 getTabs : function(){
36162 createTool : function(parentEl, className){
36163 var btn = Roo.DomHelper.append(parentEl, {
36165 cls: "x-layout-tools-button",
36168 cls: "roo-layout-tools-button-inner " + className,
36172 btn.addClassOnOver("roo-layout-tools-button-over");
36177 * Ext JS Library 1.1.1
36178 * Copyright(c) 2006-2007, Ext JS, LLC.
36180 * Originally Released Under LGPL - original licence link has changed is not relivant.
36183 * <script type="text/javascript">
36189 * @class Roo.SplitLayoutRegion
36190 * @extends Roo.LayoutRegion
36191 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36193 Roo.bootstrap.layout.Split = function(config){
36194 this.cursor = config.cursor;
36195 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36198 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36200 splitTip : "Drag to resize.",
36201 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36202 useSplitTips : false,
36204 applyConfig : function(config){
36205 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36208 onRender : function(ctr,pos) {
36210 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36211 if(!this.config.split){
36216 var splitEl = Roo.DomHelper.append(ctr.dom, {
36218 id: this.el.id + "-split",
36219 cls: "roo-layout-split roo-layout-split-"+this.position,
36222 /** The SplitBar for this region
36223 * @type Roo.SplitBar */
36224 // does not exist yet...
36225 Roo.log([this.position, this.orientation]);
36227 this.split = new Roo.bootstrap.SplitBar({
36228 dragElement : splitEl,
36229 resizingElement: this.el,
36230 orientation : this.orientation
36233 this.split.on("moved", this.onSplitMove, this);
36234 this.split.useShim = this.config.useShim === true;
36235 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36236 if(this.useSplitTips){
36237 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36239 //if(config.collapsible){
36240 // this.split.el.on("dblclick", this.collapse, this);
36243 if(typeof this.config.minSize != "undefined"){
36244 this.split.minSize = this.config.minSize;
36246 if(typeof this.config.maxSize != "undefined"){
36247 this.split.maxSize = this.config.maxSize;
36249 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36250 this.hideSplitter();
36255 getHMaxSize : function(){
36256 var cmax = this.config.maxSize || 10000;
36257 var center = this.mgr.getRegion("center");
36258 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36261 getVMaxSize : function(){
36262 var cmax = this.config.maxSize || 10000;
36263 var center = this.mgr.getRegion("center");
36264 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36267 onSplitMove : function(split, newSize){
36268 this.fireEvent("resized", this, newSize);
36272 * Returns the {@link Roo.SplitBar} for this region.
36273 * @return {Roo.SplitBar}
36275 getSplitBar : function(){
36280 this.hideSplitter();
36281 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36284 hideSplitter : function(){
36286 this.split.el.setLocation(-2000,-2000);
36287 this.split.el.hide();
36293 this.split.el.show();
36295 Roo.bootstrap.layout.Split.superclass.show.call(this);
36298 beforeSlide: function(){
36299 if(Roo.isGecko){// firefox overflow auto bug workaround
36300 this.bodyEl.clip();
36302 this.tabs.bodyEl.clip();
36304 if(this.activePanel){
36305 this.activePanel.getEl().clip();
36307 if(this.activePanel.beforeSlide){
36308 this.activePanel.beforeSlide();
36314 afterSlide : function(){
36315 if(Roo.isGecko){// firefox overflow auto bug workaround
36316 this.bodyEl.unclip();
36318 this.tabs.bodyEl.unclip();
36320 if(this.activePanel){
36321 this.activePanel.getEl().unclip();
36322 if(this.activePanel.afterSlide){
36323 this.activePanel.afterSlide();
36329 initAutoHide : function(){
36330 if(this.autoHide !== false){
36331 if(!this.autoHideHd){
36332 var st = new Roo.util.DelayedTask(this.slideIn, this);
36333 this.autoHideHd = {
36334 "mouseout": function(e){
36335 if(!e.within(this.el, true)){
36339 "mouseover" : function(e){
36345 this.el.on(this.autoHideHd);
36349 clearAutoHide : function(){
36350 if(this.autoHide !== false){
36351 this.el.un("mouseout", this.autoHideHd.mouseout);
36352 this.el.un("mouseover", this.autoHideHd.mouseover);
36356 clearMonitor : function(){
36357 Roo.get(document).un("click", this.slideInIf, this);
36360 // these names are backwards but not changed for compat
36361 slideOut : function(){
36362 if(this.isSlid || this.el.hasActiveFx()){
36365 this.isSlid = true;
36366 if(this.collapseBtn){
36367 this.collapseBtn.hide();
36369 this.closeBtnState = this.closeBtn.getStyle('display');
36370 this.closeBtn.hide();
36372 this.stickBtn.show();
36375 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36376 this.beforeSlide();
36377 this.el.setStyle("z-index", 10001);
36378 this.el.slideIn(this.getSlideAnchor(), {
36379 callback: function(){
36381 this.initAutoHide();
36382 Roo.get(document).on("click", this.slideInIf, this);
36383 this.fireEvent("slideshow", this);
36390 afterSlideIn : function(){
36391 this.clearAutoHide();
36392 this.isSlid = false;
36393 this.clearMonitor();
36394 this.el.setStyle("z-index", "");
36395 if(this.collapseBtn){
36396 this.collapseBtn.show();
36398 this.closeBtn.setStyle('display', this.closeBtnState);
36400 this.stickBtn.hide();
36402 this.fireEvent("slidehide", this);
36405 slideIn : function(cb){
36406 if(!this.isSlid || this.el.hasActiveFx()){
36410 this.isSlid = false;
36411 this.beforeSlide();
36412 this.el.slideOut(this.getSlideAnchor(), {
36413 callback: function(){
36414 this.el.setLeftTop(-10000, -10000);
36416 this.afterSlideIn();
36424 slideInIf : function(e){
36425 if(!e.within(this.el)){
36430 animateCollapse : function(){
36431 this.beforeSlide();
36432 this.el.setStyle("z-index", 20000);
36433 var anchor = this.getSlideAnchor();
36434 this.el.slideOut(anchor, {
36435 callback : function(){
36436 this.el.setStyle("z-index", "");
36437 this.collapsedEl.slideIn(anchor, {duration:.3});
36439 this.el.setLocation(-10000,-10000);
36441 this.fireEvent("collapsed", this);
36448 animateExpand : function(){
36449 this.beforeSlide();
36450 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36451 this.el.setStyle("z-index", 20000);
36452 this.collapsedEl.hide({
36455 this.el.slideIn(this.getSlideAnchor(), {
36456 callback : function(){
36457 this.el.setStyle("z-index", "");
36460 this.split.el.show();
36462 this.fireEvent("invalidated", this);
36463 this.fireEvent("expanded", this);
36491 getAnchor : function(){
36492 return this.anchors[this.position];
36495 getCollapseAnchor : function(){
36496 return this.canchors[this.position];
36499 getSlideAnchor : function(){
36500 return this.sanchors[this.position];
36503 getAlignAdj : function(){
36504 var cm = this.cmargins;
36505 switch(this.position){
36521 getExpandAdj : function(){
36522 var c = this.collapsedEl, cm = this.cmargins;
36523 switch(this.position){
36525 return [-(cm.right+c.getWidth()+cm.left), 0];
36528 return [cm.right+c.getWidth()+cm.left, 0];
36531 return [0, -(cm.top+cm.bottom+c.getHeight())];
36534 return [0, cm.top+cm.bottom+c.getHeight()];
36540 * Ext JS Library 1.1.1
36541 * Copyright(c) 2006-2007, Ext JS, LLC.
36543 * Originally Released Under LGPL - original licence link has changed is not relivant.
36546 * <script type="text/javascript">
36549 * These classes are private internal classes
36551 Roo.bootstrap.layout.Center = function(config){
36552 config.region = "center";
36553 Roo.bootstrap.layout.Region.call(this, config);
36554 this.visible = true;
36555 this.minWidth = config.minWidth || 20;
36556 this.minHeight = config.minHeight || 20;
36559 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36561 // center panel can't be hidden
36565 // center panel can't be hidden
36568 getMinWidth: function(){
36569 return this.minWidth;
36572 getMinHeight: function(){
36573 return this.minHeight;
36586 Roo.bootstrap.layout.North = function(config)
36588 config.region = 'north';
36589 config.cursor = 'n-resize';
36591 Roo.bootstrap.layout.Split.call(this, config);
36595 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36596 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36597 this.split.el.addClass("roo-layout-split-v");
36599 var size = config.initialSize || config.height;
36600 if(typeof size != "undefined"){
36601 this.el.setHeight(size);
36604 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36606 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36610 getBox : function(){
36611 if(this.collapsed){
36612 return this.collapsedEl.getBox();
36614 var box = this.el.getBox();
36616 box.height += this.split.el.getHeight();
36621 updateBox : function(box){
36622 if(this.split && !this.collapsed){
36623 box.height -= this.split.el.getHeight();
36624 this.split.el.setLeft(box.x);
36625 this.split.el.setTop(box.y+box.height);
36626 this.split.el.setWidth(box.width);
36628 if(this.collapsed){
36629 this.updateBody(box.width, null);
36631 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36639 Roo.bootstrap.layout.South = function(config){
36640 config.region = 'south';
36641 config.cursor = 's-resize';
36642 Roo.bootstrap.layout.Split.call(this, config);
36644 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36645 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36646 this.split.el.addClass("roo-layout-split-v");
36648 var size = config.initialSize || config.height;
36649 if(typeof size != "undefined"){
36650 this.el.setHeight(size);
36654 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36655 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36656 getBox : function(){
36657 if(this.collapsed){
36658 return this.collapsedEl.getBox();
36660 var box = this.el.getBox();
36662 var sh = this.split.el.getHeight();
36669 updateBox : function(box){
36670 if(this.split && !this.collapsed){
36671 var sh = this.split.el.getHeight();
36674 this.split.el.setLeft(box.x);
36675 this.split.el.setTop(box.y-sh);
36676 this.split.el.setWidth(box.width);
36678 if(this.collapsed){
36679 this.updateBody(box.width, null);
36681 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36685 Roo.bootstrap.layout.East = function(config){
36686 config.region = "east";
36687 config.cursor = "e-resize";
36688 Roo.bootstrap.layout.Split.call(this, config);
36690 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36691 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36692 this.split.el.addClass("roo-layout-split-h");
36694 var size = config.initialSize || config.width;
36695 if(typeof size != "undefined"){
36696 this.el.setWidth(size);
36699 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36700 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36701 getBox : function(){
36702 if(this.collapsed){
36703 return this.collapsedEl.getBox();
36705 var box = this.el.getBox();
36707 var sw = this.split.el.getWidth();
36714 updateBox : function(box){
36715 if(this.split && !this.collapsed){
36716 var sw = this.split.el.getWidth();
36718 this.split.el.setLeft(box.x);
36719 this.split.el.setTop(box.y);
36720 this.split.el.setHeight(box.height);
36723 if(this.collapsed){
36724 this.updateBody(null, box.height);
36726 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36730 Roo.bootstrap.layout.West = function(config){
36731 config.region = "west";
36732 config.cursor = "w-resize";
36734 Roo.bootstrap.layout.Split.call(this, config);
36736 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36737 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36738 this.split.el.addClass("roo-layout-split-h");
36742 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36743 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36745 onRender: function(ctr, pos)
36747 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36748 var size = this.config.initialSize || this.config.width;
36749 if(typeof size != "undefined"){
36750 this.el.setWidth(size);
36754 getBox : function(){
36755 if(this.collapsed){
36756 return this.collapsedEl.getBox();
36758 var box = this.el.getBox();
36760 box.width += this.split.el.getWidth();
36765 updateBox : function(box){
36766 if(this.split && !this.collapsed){
36767 var sw = this.split.el.getWidth();
36769 this.split.el.setLeft(box.x+box.width);
36770 this.split.el.setTop(box.y);
36771 this.split.el.setHeight(box.height);
36773 if(this.collapsed){
36774 this.updateBody(null, box.height);
36776 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36779 Roo.namespace("Roo.bootstrap.panel");/*
36781 * Ext JS Library 1.1.1
36782 * Copyright(c) 2006-2007, Ext JS, LLC.
36784 * Originally Released Under LGPL - original licence link has changed is not relivant.
36787 * <script type="text/javascript">
36790 * @class Roo.ContentPanel
36791 * @extends Roo.util.Observable
36792 * A basic ContentPanel element.
36793 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36794 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36795 * @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
36796 * @cfg {Boolean} closable True if the panel can be closed/removed
36797 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36798 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36799 * @cfg {Toolbar} toolbar A toolbar for this panel
36800 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36801 * @cfg {String} title The title for this panel
36802 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36803 * @cfg {String} url Calls {@link #setUrl} with this value
36804 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36805 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36806 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36807 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36808 * @cfg {Boolean} badges render the badges
36811 * Create a new ContentPanel.
36812 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36813 * @param {String/Object} config A string to set only the title or a config object
36814 * @param {String} content (optional) Set the HTML content for this panel
36815 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36817 Roo.bootstrap.panel.Content = function( config){
36819 this.tpl = config.tpl || false;
36821 var el = config.el;
36822 var content = config.content;
36824 if(config.autoCreate){ // xtype is available if this is called from factory
36827 this.el = Roo.get(el);
36828 if(!this.el && config && config.autoCreate){
36829 if(typeof config.autoCreate == "object"){
36830 if(!config.autoCreate.id){
36831 config.autoCreate.id = config.id||el;
36833 this.el = Roo.DomHelper.append(document.body,
36834 config.autoCreate, true);
36836 var elcfg = { tag: "div",
36837 cls: "roo-layout-inactive-content",
36841 elcfg.html = config.html;
36845 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36848 this.closable = false;
36849 this.loaded = false;
36850 this.active = false;
36853 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36855 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36857 this.wrapEl = this.el; //this.el.wrap();
36859 if (config.toolbar.items) {
36860 ti = config.toolbar.items ;
36861 delete config.toolbar.items ;
36865 this.toolbar.render(this.wrapEl, 'before');
36866 for(var i =0;i < ti.length;i++) {
36867 // Roo.log(['add child', items[i]]);
36868 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36870 this.toolbar.items = nitems;
36871 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36872 delete config.toolbar;
36876 // xtype created footer. - not sure if will work as we normally have to render first..
36877 if (this.footer && !this.footer.el && this.footer.xtype) {
36878 if (!this.wrapEl) {
36879 this.wrapEl = this.el.wrap();
36882 this.footer.container = this.wrapEl.createChild();
36884 this.footer = Roo.factory(this.footer, Roo);
36889 if(typeof config == "string"){
36890 this.title = config;
36892 Roo.apply(this, config);
36896 this.resizeEl = Roo.get(this.resizeEl, true);
36898 this.resizeEl = this.el;
36900 // handle view.xtype
36908 * Fires when this panel is activated.
36909 * @param {Roo.ContentPanel} this
36913 * @event deactivate
36914 * Fires when this panel is activated.
36915 * @param {Roo.ContentPanel} this
36917 "deactivate" : true,
36921 * Fires when this panel is resized if fitToFrame is true.
36922 * @param {Roo.ContentPanel} this
36923 * @param {Number} width The width after any component adjustments
36924 * @param {Number} height The height after any component adjustments
36930 * Fires when this tab is created
36931 * @param {Roo.ContentPanel} this
36942 if(this.autoScroll){
36943 this.resizeEl.setStyle("overflow", "auto");
36945 // fix randome scrolling
36946 //this.el.on('scroll', function() {
36947 // Roo.log('fix random scolling');
36948 // this.scrollTo('top',0);
36951 content = content || this.content;
36953 this.setContent(content);
36955 if(config && config.url){
36956 this.setUrl(this.url, this.params, this.loadOnce);
36961 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36963 if (this.view && typeof(this.view.xtype) != 'undefined') {
36964 this.view.el = this.el.appendChild(document.createElement("div"));
36965 this.view = Roo.factory(this.view);
36966 this.view.render && this.view.render(false, '');
36970 this.fireEvent('render', this);
36973 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36977 setRegion : function(region){
36978 this.region = region;
36979 this.setActiveClass(region && !this.background);
36983 setActiveClass: function(state)
36986 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36987 this.el.setStyle('position','relative');
36989 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36990 this.el.setStyle('position', 'absolute');
36995 * Returns the toolbar for this Panel if one was configured.
36996 * @return {Roo.Toolbar}
36998 getToolbar : function(){
36999 return this.toolbar;
37002 setActiveState : function(active)
37004 this.active = active;
37005 this.setActiveClass(active);
37007 if(this.fireEvent("deactivate", this) === false){
37012 this.fireEvent("activate", this);
37016 * Updates this panel's element
37017 * @param {String} content The new content
37018 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37020 setContent : function(content, loadScripts){
37021 this.el.update(content, loadScripts);
37024 ignoreResize : function(w, h){
37025 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37028 this.lastSize = {width: w, height: h};
37033 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37034 * @return {Roo.UpdateManager} The UpdateManager
37036 getUpdateManager : function(){
37037 return this.el.getUpdateManager();
37040 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37041 * @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:
37044 url: "your-url.php",
37045 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37046 callback: yourFunction,
37047 scope: yourObject, //(optional scope)
37050 text: "Loading...",
37055 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37056 * 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.
37057 * @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}
37058 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37059 * @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.
37060 * @return {Roo.ContentPanel} this
37063 var um = this.el.getUpdateManager();
37064 um.update.apply(um, arguments);
37070 * 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.
37071 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37072 * @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)
37073 * @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)
37074 * @return {Roo.UpdateManager} The UpdateManager
37076 setUrl : function(url, params, loadOnce){
37077 if(this.refreshDelegate){
37078 this.removeListener("activate", this.refreshDelegate);
37080 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37081 this.on("activate", this.refreshDelegate);
37082 return this.el.getUpdateManager();
37085 _handleRefresh : function(url, params, loadOnce){
37086 if(!loadOnce || !this.loaded){
37087 var updater = this.el.getUpdateManager();
37088 updater.update(url, params, this._setLoaded.createDelegate(this));
37092 _setLoaded : function(){
37093 this.loaded = true;
37097 * Returns this panel's id
37100 getId : function(){
37105 * Returns this panel's element - used by regiosn to add.
37106 * @return {Roo.Element}
37108 getEl : function(){
37109 return this.wrapEl || this.el;
37114 adjustForComponents : function(width, height)
37116 //Roo.log('adjustForComponents ');
37117 if(this.resizeEl != this.el){
37118 width -= this.el.getFrameWidth('lr');
37119 height -= this.el.getFrameWidth('tb');
37122 var te = this.toolbar.getEl();
37123 te.setWidth(width);
37124 height -= te.getHeight();
37127 var te = this.footer.getEl();
37128 te.setWidth(width);
37129 height -= te.getHeight();
37133 if(this.adjustments){
37134 width += this.adjustments[0];
37135 height += this.adjustments[1];
37137 return {"width": width, "height": height};
37140 setSize : function(width, height){
37141 if(this.fitToFrame && !this.ignoreResize(width, height)){
37142 if(this.fitContainer && this.resizeEl != this.el){
37143 this.el.setSize(width, height);
37145 var size = this.adjustForComponents(width, height);
37146 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37147 this.fireEvent('resize', this, size.width, size.height);
37152 * Returns this panel's title
37155 getTitle : function(){
37157 if (typeof(this.title) != 'object') {
37162 for (var k in this.title) {
37163 if (!this.title.hasOwnProperty(k)) {
37167 if (k.indexOf('-') >= 0) {
37168 var s = k.split('-');
37169 for (var i = 0; i<s.length; i++) {
37170 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37173 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37180 * Set this panel's title
37181 * @param {String} title
37183 setTitle : function(title){
37184 this.title = title;
37186 this.region.updatePanelTitle(this, title);
37191 * Returns true is this panel was configured to be closable
37192 * @return {Boolean}
37194 isClosable : function(){
37195 return this.closable;
37198 beforeSlide : function(){
37200 this.resizeEl.clip();
37203 afterSlide : function(){
37205 this.resizeEl.unclip();
37209 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37210 * Will fail silently if the {@link #setUrl} method has not been called.
37211 * This does not activate the panel, just updates its content.
37213 refresh : function(){
37214 if(this.refreshDelegate){
37215 this.loaded = false;
37216 this.refreshDelegate();
37221 * Destroys this panel
37223 destroy : function(){
37224 this.el.removeAllListeners();
37225 var tempEl = document.createElement("span");
37226 tempEl.appendChild(this.el.dom);
37227 tempEl.innerHTML = "";
37233 * form - if the content panel contains a form - this is a reference to it.
37234 * @type {Roo.form.Form}
37238 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37239 * This contains a reference to it.
37245 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37255 * @param {Object} cfg Xtype definition of item to add.
37259 getChildContainer: function () {
37260 return this.getEl();
37265 var ret = new Roo.factory(cfg);
37270 if (cfg.xtype.match(/^Form$/)) {
37273 //if (this.footer) {
37274 // el = this.footer.container.insertSibling(false, 'before');
37276 el = this.el.createChild();
37279 this.form = new Roo.form.Form(cfg);
37282 if ( this.form.allItems.length) {
37283 this.form.render(el.dom);
37287 // should only have one of theses..
37288 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37289 // views.. should not be just added - used named prop 'view''
37291 cfg.el = this.el.appendChild(document.createElement("div"));
37294 var ret = new Roo.factory(cfg);
37296 ret.render && ret.render(false, ''); // render blank..
37306 * @class Roo.bootstrap.panel.Grid
37307 * @extends Roo.bootstrap.panel.Content
37309 * Create a new GridPanel.
37310 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37311 * @param {Object} config A the config object
37317 Roo.bootstrap.panel.Grid = function(config)
37321 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37322 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37324 config.el = this.wrapper;
37325 //this.el = this.wrapper;
37327 if (config.container) {
37328 // ctor'ed from a Border/panel.grid
37331 this.wrapper.setStyle("overflow", "hidden");
37332 this.wrapper.addClass('roo-grid-container');
37337 if(config.toolbar){
37338 var tool_el = this.wrapper.createChild();
37339 this.toolbar = Roo.factory(config.toolbar);
37341 if (config.toolbar.items) {
37342 ti = config.toolbar.items ;
37343 delete config.toolbar.items ;
37347 this.toolbar.render(tool_el);
37348 for(var i =0;i < ti.length;i++) {
37349 // Roo.log(['add child', items[i]]);
37350 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37352 this.toolbar.items = nitems;
37354 delete config.toolbar;
37357 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37358 config.grid.scrollBody = true;;
37359 config.grid.monitorWindowResize = false; // turn off autosizing
37360 config.grid.autoHeight = false;
37361 config.grid.autoWidth = false;
37363 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37365 if (config.background) {
37366 // render grid on panel activation (if panel background)
37367 this.on('activate', function(gp) {
37368 if (!gp.grid.rendered) {
37369 gp.grid.render(this.wrapper);
37370 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37375 this.grid.render(this.wrapper);
37376 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37379 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37380 // ??? needed ??? config.el = this.wrapper;
37385 // xtype created footer. - not sure if will work as we normally have to render first..
37386 if (this.footer && !this.footer.el && this.footer.xtype) {
37388 var ctr = this.grid.getView().getFooterPanel(true);
37389 this.footer.dataSource = this.grid.dataSource;
37390 this.footer = Roo.factory(this.footer, Roo);
37391 this.footer.render(ctr);
37401 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37402 getId : function(){
37403 return this.grid.id;
37407 * Returns the grid for this panel
37408 * @return {Roo.bootstrap.Table}
37410 getGrid : function(){
37414 setSize : function(width, height){
37415 if(!this.ignoreResize(width, height)){
37416 var grid = this.grid;
37417 var size = this.adjustForComponents(width, height);
37418 var gridel = grid.getGridEl();
37419 gridel.setSize(size.width, size.height);
37421 var thd = grid.getGridEl().select('thead',true).first();
37422 var tbd = grid.getGridEl().select('tbody', true).first();
37424 tbd.setSize(width, height - thd.getHeight());
37433 beforeSlide : function(){
37434 this.grid.getView().scroller.clip();
37437 afterSlide : function(){
37438 this.grid.getView().scroller.unclip();
37441 destroy : function(){
37442 this.grid.destroy();
37444 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37449 * @class Roo.bootstrap.panel.Nest
37450 * @extends Roo.bootstrap.panel.Content
37452 * Create a new Panel, that can contain a layout.Border.
37455 * @param {Roo.BorderLayout} layout The layout for this panel
37456 * @param {String/Object} config A string to set only the title or a config object
37458 Roo.bootstrap.panel.Nest = function(config)
37460 // construct with only one argument..
37461 /* FIXME - implement nicer consturctors
37462 if (layout.layout) {
37464 layout = config.layout;
37465 delete config.layout;
37467 if (layout.xtype && !layout.getEl) {
37468 // then layout needs constructing..
37469 layout = Roo.factory(layout, Roo);
37473 config.el = config.layout.getEl();
37475 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37477 config.layout.monitorWindowResize = false; // turn off autosizing
37478 this.layout = config.layout;
37479 this.layout.getEl().addClass("roo-layout-nested-layout");
37486 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37488 setSize : function(width, height){
37489 if(!this.ignoreResize(width, height)){
37490 var size = this.adjustForComponents(width, height);
37491 var el = this.layout.getEl();
37492 if (size.height < 1) {
37493 el.setWidth(size.width);
37495 el.setSize(size.width, size.height);
37497 var touch = el.dom.offsetWidth;
37498 this.layout.layout();
37499 // ie requires a double layout on the first pass
37500 if(Roo.isIE && !this.initialized){
37501 this.initialized = true;
37502 this.layout.layout();
37507 // activate all subpanels if not currently active..
37509 setActiveState : function(active){
37510 this.active = active;
37511 this.setActiveClass(active);
37514 this.fireEvent("deactivate", this);
37518 this.fireEvent("activate", this);
37519 // not sure if this should happen before or after..
37520 if (!this.layout) {
37521 return; // should not happen..
37524 for (var r in this.layout.regions) {
37525 reg = this.layout.getRegion(r);
37526 if (reg.getActivePanel()) {
37527 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37528 reg.setActivePanel(reg.getActivePanel());
37531 if (!reg.panels.length) {
37534 reg.showPanel(reg.getPanel(0));
37543 * Returns the nested BorderLayout for this panel
37544 * @return {Roo.BorderLayout}
37546 getLayout : function(){
37547 return this.layout;
37551 * Adds a xtype elements to the layout of the nested panel
37555 xtype : 'ContentPanel',
37562 xtype : 'NestedLayoutPanel',
37568 items : [ ... list of content panels or nested layout panels.. ]
37572 * @param {Object} cfg Xtype definition of item to add.
37574 addxtype : function(cfg) {
37575 return this.layout.addxtype(cfg);
37580 * Ext JS Library 1.1.1
37581 * Copyright(c) 2006-2007, Ext JS, LLC.
37583 * Originally Released Under LGPL - original licence link has changed is not relivant.
37586 * <script type="text/javascript">
37589 * @class Roo.TabPanel
37590 * @extends Roo.util.Observable
37591 * A lightweight tab container.
37595 // basic tabs 1, built from existing content
37596 var tabs = new Roo.TabPanel("tabs1");
37597 tabs.addTab("script", "View Script");
37598 tabs.addTab("markup", "View Markup");
37599 tabs.activate("script");
37601 // more advanced tabs, built from javascript
37602 var jtabs = new Roo.TabPanel("jtabs");
37603 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37605 // set up the UpdateManager
37606 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37607 var updater = tab2.getUpdateManager();
37608 updater.setDefaultUrl("ajax1.htm");
37609 tab2.on('activate', updater.refresh, updater, true);
37611 // Use setUrl for Ajax loading
37612 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37613 tab3.setUrl("ajax2.htm", null, true);
37616 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37619 jtabs.activate("jtabs-1");
37622 * Create a new TabPanel.
37623 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37624 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37626 Roo.bootstrap.panel.Tabs = function(config){
37628 * The container element for this TabPanel.
37629 * @type Roo.Element
37631 this.el = Roo.get(config.el);
37634 if(typeof config == "boolean"){
37635 this.tabPosition = config ? "bottom" : "top";
37637 Roo.apply(this, config);
37641 if(this.tabPosition == "bottom"){
37642 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37643 this.el.addClass("roo-tabs-bottom");
37645 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37646 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37647 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37649 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37651 if(this.tabPosition != "bottom"){
37652 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37653 * @type Roo.Element
37655 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37656 this.el.addClass("roo-tabs-top");
37660 this.bodyEl.setStyle("position", "relative");
37662 this.active = null;
37663 this.activateDelegate = this.activate.createDelegate(this);
37668 * Fires when the active tab changes
37669 * @param {Roo.TabPanel} this
37670 * @param {Roo.TabPanelItem} activePanel The new active tab
37674 * @event beforetabchange
37675 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37676 * @param {Roo.TabPanel} this
37677 * @param {Object} e Set cancel to true on this object to cancel the tab change
37678 * @param {Roo.TabPanelItem} tab The tab being changed to
37680 "beforetabchange" : true
37683 Roo.EventManager.onWindowResize(this.onResize, this);
37684 this.cpad = this.el.getPadding("lr");
37685 this.hiddenCount = 0;
37688 // toolbar on the tabbar support...
37689 if (this.toolbar) {
37690 alert("no toolbar support yet");
37691 this.toolbar = false;
37693 var tcfg = this.toolbar;
37694 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37695 this.toolbar = new Roo.Toolbar(tcfg);
37696 if (Roo.isSafari) {
37697 var tbl = tcfg.container.child('table', true);
37698 tbl.setAttribute('width', '100%');
37706 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37709 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37711 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37713 tabPosition : "top",
37715 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37717 currentTabWidth : 0,
37719 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37723 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37727 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37729 preferredTabWidth : 175,
37731 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37733 resizeTabs : false,
37735 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37737 monitorResize : true,
37739 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37744 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37745 * @param {String} id The id of the div to use <b>or create</b>
37746 * @param {String} text The text for the tab
37747 * @param {String} content (optional) Content to put in the TabPanelItem body
37748 * @param {Boolean} closable (optional) True to create a close icon on the tab
37749 * @return {Roo.TabPanelItem} The created TabPanelItem
37751 addTab : function(id, text, content, closable, tpl)
37753 var item = new Roo.bootstrap.panel.TabItem({
37757 closable : closable,
37760 this.addTabItem(item);
37762 item.setContent(content);
37768 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37769 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37770 * @return {Roo.TabPanelItem}
37772 getTab : function(id){
37773 return this.items[id];
37777 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37778 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37780 hideTab : function(id){
37781 var t = this.items[id];
37784 this.hiddenCount++;
37785 this.autoSizeTabs();
37790 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37791 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37793 unhideTab : function(id){
37794 var t = this.items[id];
37796 t.setHidden(false);
37797 this.hiddenCount--;
37798 this.autoSizeTabs();
37803 * Adds an existing {@link Roo.TabPanelItem}.
37804 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37806 addTabItem : function(item){
37807 this.items[item.id] = item;
37808 this.items.push(item);
37809 // if(this.resizeTabs){
37810 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37811 // this.autoSizeTabs();
37813 // item.autoSize();
37818 * Removes a {@link Roo.TabPanelItem}.
37819 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37821 removeTab : function(id){
37822 var items = this.items;
37823 var tab = items[id];
37824 if(!tab) { return; }
37825 var index = items.indexOf(tab);
37826 if(this.active == tab && items.length > 1){
37827 var newTab = this.getNextAvailable(index);
37832 this.stripEl.dom.removeChild(tab.pnode.dom);
37833 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37834 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37836 items.splice(index, 1);
37837 delete this.items[tab.id];
37838 tab.fireEvent("close", tab);
37839 tab.purgeListeners();
37840 this.autoSizeTabs();
37843 getNextAvailable : function(start){
37844 var items = this.items;
37846 // look for a next tab that will slide over to
37847 // replace the one being removed
37848 while(index < items.length){
37849 var item = items[++index];
37850 if(item && !item.isHidden()){
37854 // if one isn't found select the previous tab (on the left)
37857 var item = items[--index];
37858 if(item && !item.isHidden()){
37866 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37867 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37869 disableTab : function(id){
37870 var tab = this.items[id];
37871 if(tab && this.active != tab){
37877 * Enables a {@link Roo.TabPanelItem} that is disabled.
37878 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37880 enableTab : function(id){
37881 var tab = this.items[id];
37886 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37887 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37888 * @return {Roo.TabPanelItem} The TabPanelItem.
37890 activate : function(id){
37891 var tab = this.items[id];
37895 if(tab == this.active || tab.disabled){
37899 this.fireEvent("beforetabchange", this, e, tab);
37900 if(e.cancel !== true && !tab.disabled){
37902 this.active.hide();
37904 this.active = this.items[id];
37905 this.active.show();
37906 this.fireEvent("tabchange", this, this.active);
37912 * Gets the active {@link Roo.TabPanelItem}.
37913 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37915 getActiveTab : function(){
37916 return this.active;
37920 * Updates the tab body element to fit the height of the container element
37921 * for overflow scrolling
37922 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37924 syncHeight : function(targetHeight){
37925 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37926 var bm = this.bodyEl.getMargins();
37927 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37928 this.bodyEl.setHeight(newHeight);
37932 onResize : function(){
37933 if(this.monitorResize){
37934 this.autoSizeTabs();
37939 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37941 beginUpdate : function(){
37942 this.updating = true;
37946 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37948 endUpdate : function(){
37949 this.updating = false;
37950 this.autoSizeTabs();
37954 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37956 autoSizeTabs : function(){
37957 var count = this.items.length;
37958 var vcount = count - this.hiddenCount;
37959 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37962 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37963 var availWidth = Math.floor(w / vcount);
37964 var b = this.stripBody;
37965 if(b.getWidth() > w){
37966 var tabs = this.items;
37967 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37968 if(availWidth < this.minTabWidth){
37969 /*if(!this.sleft){ // incomplete scrolling code
37970 this.createScrollButtons();
37973 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37976 if(this.currentTabWidth < this.preferredTabWidth){
37977 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37983 * Returns the number of tabs in this TabPanel.
37986 getCount : function(){
37987 return this.items.length;
37991 * Resizes all the tabs to the passed width
37992 * @param {Number} The new width
37994 setTabWidth : function(width){
37995 this.currentTabWidth = width;
37996 for(var i = 0, len = this.items.length; i < len; i++) {
37997 if(!this.items[i].isHidden()) {
37998 this.items[i].setWidth(width);
38004 * Destroys this TabPanel
38005 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38007 destroy : function(removeEl){
38008 Roo.EventManager.removeResizeListener(this.onResize, this);
38009 for(var i = 0, len = this.items.length; i < len; i++){
38010 this.items[i].purgeListeners();
38012 if(removeEl === true){
38013 this.el.update("");
38018 createStrip : function(container)
38020 var strip = document.createElement("nav");
38021 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38022 container.appendChild(strip);
38026 createStripList : function(strip)
38028 // div wrapper for retard IE
38029 // returns the "tr" element.
38030 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38031 //'<div class="x-tabs-strip-wrap">'+
38032 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38033 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38034 return strip.firstChild; //.firstChild.firstChild.firstChild;
38036 createBody : function(container)
38038 var body = document.createElement("div");
38039 Roo.id(body, "tab-body");
38040 //Roo.fly(body).addClass("x-tabs-body");
38041 Roo.fly(body).addClass("tab-content");
38042 container.appendChild(body);
38045 createItemBody :function(bodyEl, id){
38046 var body = Roo.getDom(id);
38048 body = document.createElement("div");
38051 //Roo.fly(body).addClass("x-tabs-item-body");
38052 Roo.fly(body).addClass("tab-pane");
38053 bodyEl.insertBefore(body, bodyEl.firstChild);
38057 createStripElements : function(stripEl, text, closable, tpl)
38059 var td = document.createElement("li"); // was td..
38062 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38065 stripEl.appendChild(td);
38067 td.className = "x-tabs-closable";
38068 if(!this.closeTpl){
38069 this.closeTpl = new Roo.Template(
38070 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38071 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38072 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38075 var el = this.closeTpl.overwrite(td, {"text": text});
38076 var close = el.getElementsByTagName("div")[0];
38077 var inner = el.getElementsByTagName("em")[0];
38078 return {"el": el, "close": close, "inner": inner};
38081 // not sure what this is..
38082 // if(!this.tabTpl){
38083 //this.tabTpl = new Roo.Template(
38084 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38085 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38087 // this.tabTpl = new Roo.Template(
38088 // '<a href="#">' +
38089 // '<span unselectable="on"' +
38090 // (this.disableTooltips ? '' : ' title="{text}"') +
38091 // ' >{text}</span></a>'
38097 var template = tpl || this.tabTpl || false;
38101 template = new Roo.Template(
38103 '<span unselectable="on"' +
38104 (this.disableTooltips ? '' : ' title="{text}"') +
38105 ' >{text}</span></a>'
38109 switch (typeof(template)) {
38113 template = new Roo.Template(template);
38119 var el = template.overwrite(td, {"text": text});
38121 var inner = el.getElementsByTagName("span")[0];
38123 return {"el": el, "inner": inner};
38131 * @class Roo.TabPanelItem
38132 * @extends Roo.util.Observable
38133 * Represents an individual item (tab plus body) in a TabPanel.
38134 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38135 * @param {String} id The id of this TabPanelItem
38136 * @param {String} text The text for the tab of this TabPanelItem
38137 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38139 Roo.bootstrap.panel.TabItem = function(config){
38141 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38142 * @type Roo.TabPanel
38144 this.tabPanel = config.panel;
38146 * The id for this TabPanelItem
38149 this.id = config.id;
38151 this.disabled = false;
38153 this.text = config.text;
38155 this.loaded = false;
38156 this.closable = config.closable;
38159 * The body element for this TabPanelItem.
38160 * @type Roo.Element
38162 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38163 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38164 this.bodyEl.setStyle("display", "block");
38165 this.bodyEl.setStyle("zoom", "1");
38166 //this.hideAction();
38168 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38170 this.el = Roo.get(els.el);
38171 this.inner = Roo.get(els.inner, true);
38172 this.textEl = Roo.get(this.el.dom.firstChild, true);
38173 this.pnode = Roo.get(els.el.parentNode, true);
38174 // this.el.on("mousedown", this.onTabMouseDown, this);
38175 this.el.on("click", this.onTabClick, this);
38177 if(config.closable){
38178 var c = Roo.get(els.close, true);
38179 c.dom.title = this.closeText;
38180 c.addClassOnOver("close-over");
38181 c.on("click", this.closeClick, this);
38187 * Fires when this tab becomes the active tab.
38188 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38189 * @param {Roo.TabPanelItem} this
38193 * @event beforeclose
38194 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38195 * @param {Roo.TabPanelItem} this
38196 * @param {Object} e Set cancel to true on this object to cancel the close.
38198 "beforeclose": true,
38201 * Fires when this tab is closed.
38202 * @param {Roo.TabPanelItem} this
38206 * @event deactivate
38207 * Fires when this tab is no longer the active tab.
38208 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38209 * @param {Roo.TabPanelItem} this
38211 "deactivate" : true
38213 this.hidden = false;
38215 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38218 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38220 purgeListeners : function(){
38221 Roo.util.Observable.prototype.purgeListeners.call(this);
38222 this.el.removeAllListeners();
38225 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38228 this.pnode.addClass("active");
38231 this.tabPanel.stripWrap.repaint();
38233 this.fireEvent("activate", this.tabPanel, this);
38237 * Returns true if this tab is the active tab.
38238 * @return {Boolean}
38240 isActive : function(){
38241 return this.tabPanel.getActiveTab() == this;
38245 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38248 this.pnode.removeClass("active");
38250 this.fireEvent("deactivate", this.tabPanel, this);
38253 hideAction : function(){
38254 this.bodyEl.hide();
38255 this.bodyEl.setStyle("position", "absolute");
38256 this.bodyEl.setLeft("-20000px");
38257 this.bodyEl.setTop("-20000px");
38260 showAction : function(){
38261 this.bodyEl.setStyle("position", "relative");
38262 this.bodyEl.setTop("");
38263 this.bodyEl.setLeft("");
38264 this.bodyEl.show();
38268 * Set the tooltip for the tab.
38269 * @param {String} tooltip The tab's tooltip
38271 setTooltip : function(text){
38272 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38273 this.textEl.dom.qtip = text;
38274 this.textEl.dom.removeAttribute('title');
38276 this.textEl.dom.title = text;
38280 onTabClick : function(e){
38281 e.preventDefault();
38282 this.tabPanel.activate(this.id);
38285 onTabMouseDown : function(e){
38286 e.preventDefault();
38287 this.tabPanel.activate(this.id);
38290 getWidth : function(){
38291 return this.inner.getWidth();
38294 setWidth : function(width){
38295 var iwidth = width - this.pnode.getPadding("lr");
38296 this.inner.setWidth(iwidth);
38297 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38298 this.pnode.setWidth(width);
38302 * Show or hide the tab
38303 * @param {Boolean} hidden True to hide or false to show.
38305 setHidden : function(hidden){
38306 this.hidden = hidden;
38307 this.pnode.setStyle("display", hidden ? "none" : "");
38311 * Returns true if this tab is "hidden"
38312 * @return {Boolean}
38314 isHidden : function(){
38315 return this.hidden;
38319 * Returns the text for this tab
38322 getText : function(){
38326 autoSize : function(){
38327 //this.el.beginMeasure();
38328 this.textEl.setWidth(1);
38330 * #2804 [new] Tabs in Roojs
38331 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38333 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38334 //this.el.endMeasure();
38338 * Sets the text for the tab (Note: this also sets the tooltip text)
38339 * @param {String} text The tab's text and tooltip
38341 setText : function(text){
38343 this.textEl.update(text);
38344 this.setTooltip(text);
38345 //if(!this.tabPanel.resizeTabs){
38346 // this.autoSize();
38350 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38352 activate : function(){
38353 this.tabPanel.activate(this.id);
38357 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38359 disable : function(){
38360 if(this.tabPanel.active != this){
38361 this.disabled = true;
38362 this.pnode.addClass("disabled");
38367 * Enables this TabPanelItem if it was previously disabled.
38369 enable : function(){
38370 this.disabled = false;
38371 this.pnode.removeClass("disabled");
38375 * Sets the content for this TabPanelItem.
38376 * @param {String} content The content
38377 * @param {Boolean} loadScripts true to look for and load scripts
38379 setContent : function(content, loadScripts){
38380 this.bodyEl.update(content, loadScripts);
38384 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38385 * @return {Roo.UpdateManager} The UpdateManager
38387 getUpdateManager : function(){
38388 return this.bodyEl.getUpdateManager();
38392 * Set a URL to be used to load the content for this TabPanelItem.
38393 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38394 * @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)
38395 * @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)
38396 * @return {Roo.UpdateManager} The UpdateManager
38398 setUrl : function(url, params, loadOnce){
38399 if(this.refreshDelegate){
38400 this.un('activate', this.refreshDelegate);
38402 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38403 this.on("activate", this.refreshDelegate);
38404 return this.bodyEl.getUpdateManager();
38408 _handleRefresh : function(url, params, loadOnce){
38409 if(!loadOnce || !this.loaded){
38410 var updater = this.bodyEl.getUpdateManager();
38411 updater.update(url, params, this._setLoaded.createDelegate(this));
38416 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38417 * Will fail silently if the setUrl method has not been called.
38418 * This does not activate the panel, just updates its content.
38420 refresh : function(){
38421 if(this.refreshDelegate){
38422 this.loaded = false;
38423 this.refreshDelegate();
38428 _setLoaded : function(){
38429 this.loaded = true;
38433 closeClick : function(e){
38436 this.fireEvent("beforeclose", this, o);
38437 if(o.cancel !== true){
38438 this.tabPanel.removeTab(this.id);
38442 * The text displayed in the tooltip for the close icon.
38445 closeText : "Close this tab"
38448 * This script refer to:
38449 * Title: International Telephone Input
38450 * Author: Jack O'Connor
38451 * Code version: v12.1.12
38452 * Availability: https://github.com/jackocnr/intl-tel-input.git
38455 Roo.bootstrap.PhoneInputData = function() {
38458 "Afghanistan (افغانستان)",
38463 "Albania (Shqipëri)",
38468 "Algeria (الجزائر)",
38493 "Antigua and Barbuda",
38503 "Armenia (Հայաստան)",
38519 "Austria (Österreich)",
38524 "Azerbaijan (Azərbaycan)",
38534 "Bahrain (البحرين)",
38539 "Bangladesh (বাংলাদেশ)",
38549 "Belarus (Беларусь)",
38554 "Belgium (België)",
38584 "Bosnia and Herzegovina (Босна и Херцеговина)",
38599 "British Indian Ocean Territory",
38604 "British Virgin Islands",
38614 "Bulgaria (България)",
38624 "Burundi (Uburundi)",
38629 "Cambodia (កម្ពុជា)",
38634 "Cameroon (Cameroun)",
38643 ["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"]
38646 "Cape Verde (Kabu Verdi)",
38651 "Caribbean Netherlands",
38662 "Central African Republic (République centrafricaine)",
38682 "Christmas Island",
38688 "Cocos (Keeling) Islands",
38699 "Comoros (جزر القمر)",
38704 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38709 "Congo (Republic) (Congo-Brazzaville)",
38729 "Croatia (Hrvatska)",
38750 "Czech Republic (Česká republika)",
38755 "Denmark (Danmark)",
38770 "Dominican Republic (República Dominicana)",
38774 ["809", "829", "849"]
38792 "Equatorial Guinea (Guinea Ecuatorial)",
38812 "Falkland Islands (Islas Malvinas)",
38817 "Faroe Islands (Føroyar)",
38838 "French Guiana (Guyane française)",
38843 "French Polynesia (Polynésie française)",
38858 "Georgia (საქართველო)",
38863 "Germany (Deutschland)",
38883 "Greenland (Kalaallit Nunaat)",
38920 "Guinea-Bissau (Guiné Bissau)",
38945 "Hungary (Magyarország)",
38950 "Iceland (Ísland)",
38970 "Iraq (العراق)",
38986 "Israel (ישראל)",
39013 "Jordan (الأردن)",
39018 "Kazakhstan (Казахстан)",
39039 "Kuwait (الكويت)",
39044 "Kyrgyzstan (Кыргызстан)",
39054 "Latvia (Latvija)",
39059 "Lebanon (لبنان)",
39074 "Libya (ليبيا)",
39084 "Lithuania (Lietuva)",
39099 "Macedonia (FYROM) (Македонија)",
39104 "Madagascar (Madagasikara)",
39134 "Marshall Islands",
39144 "Mauritania (موريتانيا)",
39149 "Mauritius (Moris)",
39170 "Moldova (Republica Moldova)",
39180 "Mongolia (Монгол)",
39185 "Montenegro (Crna Gora)",
39195 "Morocco (المغرب)",
39201 "Mozambique (Moçambique)",
39206 "Myanmar (Burma) (မြန်မာ)",
39211 "Namibia (Namibië)",
39226 "Netherlands (Nederland)",
39231 "New Caledonia (Nouvelle-Calédonie)",
39266 "North Korea (조선 민주주의 인민 공화국)",
39271 "Northern Mariana Islands",
39287 "Pakistan (پاکستان)",
39297 "Palestine (فلسطين)",
39307 "Papua New Guinea",
39349 "Réunion (La Réunion)",
39355 "Romania (România)",
39371 "Saint Barthélemy",
39382 "Saint Kitts and Nevis",
39392 "Saint Martin (Saint-Martin (partie française))",
39398 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39403 "Saint Vincent and the Grenadines",
39418 "São Tomé and Príncipe (São Tomé e Príncipe)",
39423 "Saudi Arabia (المملكة العربية السعودية)",
39428 "Senegal (Sénégal)",
39458 "Slovakia (Slovensko)",
39463 "Slovenia (Slovenija)",
39473 "Somalia (Soomaaliya)",
39483 "South Korea (대한민국)",
39488 "South Sudan (جنوب السودان)",
39498 "Sri Lanka (ශ්රී ලංකාව)",
39503 "Sudan (السودان)",
39513 "Svalbard and Jan Mayen",
39524 "Sweden (Sverige)",
39529 "Switzerland (Schweiz)",
39534 "Syria (سوريا)",
39579 "Trinidad and Tobago",
39584 "Tunisia (تونس)",
39589 "Turkey (Türkiye)",
39599 "Turks and Caicos Islands",
39609 "U.S. Virgin Islands",
39619 "Ukraine (Україна)",
39624 "United Arab Emirates (الإمارات العربية المتحدة)",
39646 "Uzbekistan (Oʻzbekiston)",
39656 "Vatican City (Città del Vaticano)",
39667 "Vietnam (Việt Nam)",
39672 "Wallis and Futuna (Wallis-et-Futuna)",
39677 "Western Sahara (الصحراء الغربية)",
39683 "Yemen (اليمن)",
39707 * This script refer to:
39708 * Title: International Telephone Input
39709 * Author: Jack O'Connor
39710 * Code version: v12.1.12
39711 * Availability: https://github.com/jackocnr/intl-tel-input.git
39715 * @class Roo.bootstrap.PhoneInput
39716 * @extends Roo.bootstrap.TriggerField
39717 * An input with International dial-code selection
39719 * @cfg {String} defaultDialCode default '+852'
39720 * @cfg {Array} preferedCountries default []
39723 * Create a new PhoneInput.
39724 * @param {Object} config Configuration options
39727 Roo.bootstrap.PhoneInput = function(config) {
39728 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39731 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39733 listWidth: undefined,
39735 selectedClass: 'active',
39737 invalidClass : "has-warning",
39739 validClass: 'has-success',
39741 allowed: '0123456789',
39744 * @cfg {String} defaultDialCode The default dial code when initializing the input
39746 defaultDialCode: '+852',
39749 * @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
39751 preferedCountries: false,
39753 getAutoCreate : function()
39755 var data = Roo.bootstrap.PhoneInputData();
39756 var align = this.labelAlign || this.parentLabelAlign();
39759 this.allCountries = [];
39760 this.dialCodeMapping = [];
39762 for (var i = 0; i < data.length; i++) {
39764 this.allCountries[i] = {
39768 priority: c[3] || 0,
39769 areaCodes: c[4] || null
39771 this.dialCodeMapping[c[2]] = {
39774 priority: c[3] || 0,
39775 areaCodes: c[4] || null
39787 cls : 'form-control tel-input',
39788 autocomplete: 'new-password'
39791 var hiddenInput = {
39794 cls: 'hidden-tel-input'
39798 hiddenInput.name = this.name;
39801 if (this.disabled) {
39802 input.disabled = true;
39805 var flag_container = {
39822 cls: this.hasFeedback ? 'has-feedback' : '',
39828 cls: 'dial-code-holder',
39835 cls: 'roo-select2-container input-group',
39842 if (this.fieldLabel.length) {
39845 tooltip: 'This field is required'
39851 cls: 'control-label',
39857 html: this.fieldLabel
39860 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39866 if(this.indicatorpos == 'right') {
39867 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39874 if(align == 'left') {
39882 if(this.labelWidth > 12){
39883 label.style = "width: " + this.labelWidth + 'px';
39885 if(this.labelWidth < 13 && this.labelmd == 0){
39886 this.labelmd = this.labelWidth;
39888 if(this.labellg > 0){
39889 label.cls += ' col-lg-' + this.labellg;
39890 input.cls += ' col-lg-' + (12 - this.labellg);
39892 if(this.labelmd > 0){
39893 label.cls += ' col-md-' + this.labelmd;
39894 container.cls += ' col-md-' + (12 - this.labelmd);
39896 if(this.labelsm > 0){
39897 label.cls += ' col-sm-' + this.labelsm;
39898 container.cls += ' col-sm-' + (12 - this.labelsm);
39900 if(this.labelxs > 0){
39901 label.cls += ' col-xs-' + this.labelxs;
39902 container.cls += ' col-xs-' + (12 - this.labelxs);
39912 var settings = this;
39914 ['xs','sm','md','lg'].map(function(size){
39915 if (settings[size]) {
39916 cfg.cls += ' col-' + size + '-' + settings[size];
39920 this.store = new Roo.data.Store({
39921 proxy : new Roo.data.MemoryProxy({}),
39922 reader : new Roo.data.JsonReader({
39933 'name' : 'dialCode',
39937 'name' : 'priority',
39941 'name' : 'areaCodes',
39948 if(!this.preferedCountries) {
39949 this.preferedCountries = [
39956 var p = this.preferedCountries.reverse();
39959 for (var i = 0; i < p.length; i++) {
39960 for (var j = 0; j < this.allCountries.length; j++) {
39961 if(this.allCountries[j].iso2 == p[i]) {
39962 var t = this.allCountries[j];
39963 this.allCountries.splice(j,1);
39964 this.allCountries.unshift(t);
39970 this.store.proxy.data = {
39972 data: this.allCountries
39978 initEvents : function()
39981 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39983 this.indicator = this.indicatorEl();
39984 this.flag = this.flagEl();
39985 this.dialCodeHolder = this.dialCodeHolderEl();
39987 this.trigger = this.el.select('div.flag-box',true).first();
39988 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39993 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39994 _this.list.setWidth(lw);
39997 this.list.on('mouseover', this.onViewOver, this);
39998 this.list.on('mousemove', this.onViewMove, this);
39999 this.inputEl().on("keyup", this.onKeyUp, this);
40001 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40003 this.view = new Roo.View(this.list, this.tpl, {
40004 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40007 this.view.on('click', this.onViewClick, this);
40008 this.setValue(this.defaultDialCode);
40011 onTriggerClick : function(e)
40013 Roo.log('trigger click');
40018 if(this.isExpanded()){
40020 this.hasFocus = false;
40022 this.store.load({});
40023 this.hasFocus = true;
40028 isExpanded : function()
40030 return this.list.isVisible();
40033 collapse : function()
40035 if(!this.isExpanded()){
40039 Roo.get(document).un('mousedown', this.collapseIf, this);
40040 Roo.get(document).un('mousewheel', this.collapseIf, this);
40041 this.fireEvent('collapse', this);
40045 expand : function()
40049 if(this.isExpanded() || !this.hasFocus){
40053 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40054 this.list.setWidth(lw);
40057 this.restrictHeight();
40059 Roo.get(document).on('mousedown', this.collapseIf, this);
40060 Roo.get(document).on('mousewheel', this.collapseIf, this);
40062 this.fireEvent('expand', this);
40065 restrictHeight : function()
40067 this.list.alignTo(this.inputEl(), this.listAlign);
40068 this.list.alignTo(this.inputEl(), this.listAlign);
40071 onViewOver : function(e, t)
40073 if(this.inKeyMode){
40076 var item = this.view.findItemFromChild(t);
40079 var index = this.view.indexOf(item);
40080 this.select(index, false);
40085 onViewClick : function(view, doFocus, el, e)
40087 var index = this.view.getSelectedIndexes()[0];
40089 var r = this.store.getAt(index);
40092 this.onSelect(r, index);
40094 if(doFocus !== false && !this.blockFocus){
40095 this.inputEl().focus();
40099 onViewMove : function(e, t)
40101 this.inKeyMode = false;
40104 select : function(index, scrollIntoView)
40106 this.selectedIndex = index;
40107 this.view.select(index);
40108 if(scrollIntoView !== false){
40109 var el = this.view.getNode(index);
40111 this.list.scrollChildIntoView(el, false);
40116 createList : function()
40118 this.list = Roo.get(document.body).createChild({
40120 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40121 style: 'display:none'
40124 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40127 collapseIf : function(e)
40129 var in_combo = e.within(this.el);
40130 var in_list = e.within(this.list);
40131 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40133 if (in_combo || in_list || is_list) {
40139 onSelect : function(record, index)
40141 if(this.fireEvent('beforeselect', this, record, index) !== false){
40143 this.setFlagClass(record.data.iso2);
40144 this.setDialCode(record.data.dialCode);
40145 this.hasFocus = false;
40147 this.fireEvent('select', this, record, index);
40151 flagEl : function()
40153 var flag = this.el.select('div.flag',true).first();
40160 dialCodeHolderEl : function()
40162 var d = this.el.select('input.dial-code-holder',true).first();
40169 setDialCode : function(v)
40171 this.dialCodeHolder.dom.value = '+'+v;
40174 setFlagClass : function(n)
40176 this.flag.dom.className = 'flag '+n;
40179 getValue : function()
40181 var v = this.inputEl().getValue();
40182 if(this.dialCodeHolder) {
40183 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40188 setValue : function(v)
40190 var d = this.getDialCode(v);
40192 //invalid dial code
40193 if(v.length == 0 || !d || d.length == 0) {
40195 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40196 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40202 this.setFlagClass(this.dialCodeMapping[d].iso2);
40203 this.setDialCode(d);
40204 this.inputEl().dom.value = v.replace('+'+d,'');
40205 this.hiddenEl().dom.value = this.getValue();
40210 getDialCode : function(v)
40214 if (v.length == 0) {
40215 return this.dialCodeHolder.dom.value;
40219 if (v.charAt(0) != "+") {
40222 var numericChars = "";
40223 for (var i = 1; i < v.length; i++) {
40224 var c = v.charAt(i);
40227 if (this.dialCodeMapping[numericChars]) {
40228 dialCode = v.substr(1, i);
40230 if (numericChars.length == 4) {
40240 this.setValue(this.defaultDialCode);
40244 hiddenEl : function()
40246 return this.el.select('input.hidden-tel-input',true).first();
40249 onKeyUp : function(e){
40251 var k = e.getKey();
40252 var c = e.getCharCode();
40255 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40256 this.allowed.indexOf(String.fromCharCode(c)) === -1
40261 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40264 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40268 this.setValue(this.getValue());
40273 * @class Roo.bootstrap.MoneyField
40274 * @extends Roo.bootstrap.ComboBox
40275 * Bootstrap MoneyField class
40278 * Create a new MoneyField.
40279 * @param {Object} config Configuration options
40282 Roo.bootstrap.MoneyField = function(config) {
40284 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40288 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40291 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40293 allowDecimals : true,
40295 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40297 decimalSeparator : ".",
40299 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40301 decimalPrecision : 0,
40303 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40305 allowNegative : true,
40307 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40311 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40313 minValue : Number.NEGATIVE_INFINITY,
40315 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40317 maxValue : Number.MAX_VALUE,
40319 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40321 minText : "The minimum value for this field is {0}",
40323 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40325 maxText : "The maximum value for this field is {0}",
40327 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40328 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40330 nanText : "{0} is not a valid number",
40332 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40336 * @cfg {String} defaults currency of the MoneyField
40337 * value should be in lkey
40339 defaultCurrency : false,
40341 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40343 thousandsDelimiter : false,
40353 getAutoCreate : function()
40355 var align = this.labelAlign || this.parentLabelAlign();
40367 cls : 'form-control roo-money-amount-input',
40368 autocomplete: 'new-password'
40371 var hiddenInput = {
40375 cls: 'hidden-number-input'
40379 hiddenInput.name = this.name;
40382 if (this.disabled) {
40383 input.disabled = true;
40386 var clg = 12 - this.inputlg;
40387 var cmd = 12 - this.inputmd;
40388 var csm = 12 - this.inputsm;
40389 var cxs = 12 - this.inputxs;
40393 cls : 'row roo-money-field',
40397 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40401 cls: 'roo-select2-container input-group',
40405 cls : 'form-control roo-money-currency-input',
40406 autocomplete: 'new-password',
40408 name : this.currencyName
40412 cls : 'input-group-addon',
40426 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40430 cls: this.hasFeedback ? 'has-feedback' : '',
40441 if (this.fieldLabel.length) {
40444 tooltip: 'This field is required'
40450 cls: 'control-label',
40456 html: this.fieldLabel
40459 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40465 if(this.indicatorpos == 'right') {
40466 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40473 if(align == 'left') {
40481 if(this.labelWidth > 12){
40482 label.style = "width: " + this.labelWidth + 'px';
40484 if(this.labelWidth < 13 && this.labelmd == 0){
40485 this.labelmd = this.labelWidth;
40487 if(this.labellg > 0){
40488 label.cls += ' col-lg-' + this.labellg;
40489 input.cls += ' col-lg-' + (12 - this.labellg);
40491 if(this.labelmd > 0){
40492 label.cls += ' col-md-' + this.labelmd;
40493 container.cls += ' col-md-' + (12 - this.labelmd);
40495 if(this.labelsm > 0){
40496 label.cls += ' col-sm-' + this.labelsm;
40497 container.cls += ' col-sm-' + (12 - this.labelsm);
40499 if(this.labelxs > 0){
40500 label.cls += ' col-xs-' + this.labelxs;
40501 container.cls += ' col-xs-' + (12 - this.labelxs);
40512 var settings = this;
40514 ['xs','sm','md','lg'].map(function(size){
40515 if (settings[size]) {
40516 cfg.cls += ' col-' + size + '-' + settings[size];
40523 initEvents : function()
40525 this.indicator = this.indicatorEl();
40527 this.initCurrencyEvent();
40529 this.initNumberEvent();
40532 initCurrencyEvent : function()
40535 throw "can not find store for combo";
40538 this.store = Roo.factory(this.store, Roo.data);
40539 this.store.parent = this;
40543 this.triggerEl = this.el.select('.input-group-addon', true).first();
40545 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40550 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40551 _this.list.setWidth(lw);
40554 this.list.on('mouseover', this.onViewOver, this);
40555 this.list.on('mousemove', this.onViewMove, this);
40556 this.list.on('scroll', this.onViewScroll, this);
40559 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40562 this.view = new Roo.View(this.list, this.tpl, {
40563 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40566 this.view.on('click', this.onViewClick, this);
40568 this.store.on('beforeload', this.onBeforeLoad, this);
40569 this.store.on('load', this.onLoad, this);
40570 this.store.on('loadexception', this.onLoadException, this);
40572 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40573 "up" : function(e){
40574 this.inKeyMode = true;
40578 "down" : function(e){
40579 if(!this.isExpanded()){
40580 this.onTriggerClick();
40582 this.inKeyMode = true;
40587 "enter" : function(e){
40590 if(this.fireEvent("specialkey", this, e)){
40591 this.onViewClick(false);
40597 "esc" : function(e){
40601 "tab" : function(e){
40604 if(this.fireEvent("specialkey", this, e)){
40605 this.onViewClick(false);
40613 doRelay : function(foo, bar, hname){
40614 if(hname == 'down' || this.scope.isExpanded()){
40615 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40623 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40627 initNumberEvent : function(e)
40629 this.inputEl().on("keydown" , this.fireKey, this);
40630 this.inputEl().on("focus", this.onFocus, this);
40631 this.inputEl().on("blur", this.onBlur, this);
40633 this.inputEl().relayEvent('keyup', this);
40635 if(this.indicator){
40636 this.indicator.addClass('invisible');
40639 this.originalValue = this.getValue();
40641 if(this.validationEvent == 'keyup'){
40642 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40643 this.inputEl().on('keyup', this.filterValidation, this);
40645 else if(this.validationEvent !== false){
40646 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40649 if(this.selectOnFocus){
40650 this.on("focus", this.preFocus, this);
40653 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40654 this.inputEl().on("keypress", this.filterKeys, this);
40656 this.inputEl().relayEvent('keypress', this);
40659 var allowed = "0123456789";
40661 if(this.allowDecimals){
40662 allowed += this.decimalSeparator;
40665 if(this.allowNegative){
40669 if(this.thousandsDelimiter) {
40673 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40675 var keyPress = function(e){
40677 var k = e.getKey();
40679 var c = e.getCharCode();
40682 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40683 allowed.indexOf(String.fromCharCode(c)) === -1
40689 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40693 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40698 this.inputEl().on("keypress", keyPress, this);
40702 onTriggerClick : function(e)
40709 this.loadNext = false;
40711 if(this.isExpanded()){
40716 this.hasFocus = true;
40718 if(this.triggerAction == 'all') {
40719 this.doQuery(this.allQuery, true);
40723 this.doQuery(this.getRawValue());
40726 getCurrency : function()
40728 var v = this.currencyEl().getValue();
40733 restrictHeight : function()
40735 this.list.alignTo(this.currencyEl(), this.listAlign);
40736 this.list.alignTo(this.currencyEl(), this.listAlign);
40739 onViewClick : function(view, doFocus, el, e)
40741 var index = this.view.getSelectedIndexes()[0];
40743 var r = this.store.getAt(index);
40746 this.onSelect(r, index);
40750 onSelect : function(record, index){
40752 if(this.fireEvent('beforeselect', this, record, index) !== false){
40754 this.setFromCurrencyData(index > -1 ? record.data : false);
40758 this.fireEvent('select', this, record, index);
40762 setFromCurrencyData : function(o)
40766 this.lastCurrency = o;
40768 if (this.currencyField) {
40769 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40771 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40774 this.lastSelectionText = currency;
40776 //setting default currency
40777 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40778 this.setCurrency(this.defaultCurrency);
40782 this.setCurrency(currency);
40785 setFromData : function(o)
40789 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40791 this.setFromCurrencyData(c);
40796 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40798 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40801 this.setValue(value);
40805 setCurrency : function(v)
40807 this.currencyValue = v;
40810 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40815 setValue : function(v)
40817 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40823 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40825 this.inputEl().dom.value = (v == '') ? '' :
40826 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40828 if(!this.allowZero && v === '0') {
40829 this.hiddenEl().dom.value = '';
40830 this.inputEl().dom.value = '';
40837 getRawValue : function()
40839 var v = this.inputEl().getValue();
40844 getValue : function()
40846 return this.fixPrecision(this.parseValue(this.getRawValue()));
40849 parseValue : function(value)
40851 if(this.thousandsDelimiter) {
40853 r = new RegExp(",", "g");
40854 value = value.replace(r, "");
40857 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40858 return isNaN(value) ? '' : value;
40862 fixPrecision : function(value)
40864 if(this.thousandsDelimiter) {
40866 r = new RegExp(",", "g");
40867 value = value.replace(r, "");
40870 var nan = isNaN(value);
40872 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40873 return nan ? '' : value;
40875 return parseFloat(value).toFixed(this.decimalPrecision);
40878 decimalPrecisionFcn : function(v)
40880 return Math.floor(v);
40883 validateValue : function(value)
40885 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40889 var num = this.parseValue(value);
40892 this.markInvalid(String.format(this.nanText, value));
40896 if(num < this.minValue){
40897 this.markInvalid(String.format(this.minText, this.minValue));
40901 if(num > this.maxValue){
40902 this.markInvalid(String.format(this.maxText, this.maxValue));
40909 validate : function()
40911 if(this.disabled || this.allowBlank){
40916 var currency = this.getCurrency();
40918 if(this.validateValue(this.getRawValue()) && currency.length){
40923 this.markInvalid();
40927 getName: function()
40932 beforeBlur : function()
40938 var v = this.parseValue(this.getRawValue());
40945 onBlur : function()
40949 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40950 //this.el.removeClass(this.focusClass);
40953 this.hasFocus = false;
40955 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40959 var v = this.getValue();
40961 if(String(v) !== String(this.startValue)){
40962 this.fireEvent('change', this, v, this.startValue);
40965 this.fireEvent("blur", this);
40968 inputEl : function()
40970 return this.el.select('.roo-money-amount-input', true).first();
40973 currencyEl : function()
40975 return this.el.select('.roo-money-currency-input', true).first();
40978 hiddenEl : function()
40980 return this.el.select('input.hidden-number-input',true).first();