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',
2650 onRender : function(ct, position)
2652 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2655 var cfg = Roo.apply({}, this.getAutoCreate());
2658 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660 //if (!cfg.name.length) {
2664 cfg.cls += ' ' + this.cls;
2667 cfg.style = this.style;
2669 this.el = Roo.get(document.body).createChild(cfg, position);
2671 //var type = this.el.dom.type;
2674 if(this.tabIndex !== undefined){
2675 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2678 this.dialogEl = this.el.select('.modal-dialog',true).first();
2679 this.bodyEl = this.el.select('.modal-body',true).first();
2680 this.closeEl = this.el.select('.modal-header .close', true).first();
2681 this.headerEl = this.el.select('.modal-header',true).first();
2682 this.titleEl = this.el.select('.modal-title',true).first();
2683 this.footerEl = this.el.select('.modal-footer',true).first();
2685 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687 //this.el.addClass("x-dlg-modal");
2689 if (this.buttons.length) {
2690 Roo.each(this.buttons, function(bb) {
2691 var b = Roo.apply({}, bb);
2692 b.xns = b.xns || Roo.bootstrap;
2693 b.xtype = b.xtype || 'Button';
2694 if (typeof(b.listeners) == 'undefined') {
2695 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2698 var btn = Roo.factory(b);
2700 btn.render(this.el.select('.modal-footer div').first());
2704 // render the children.
2707 if(typeof(this.items) != 'undefined'){
2708 var items = this.items;
2711 for(var i =0;i < items.length;i++) {
2712 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2716 this.items = nitems;
2718 // where are these used - they used to be body/close/footer
2722 //this.el.addClass([this.fieldClass, this.cls]);
2726 getAutoCreate : function()
2730 html : this.html || ''
2735 cls : 'modal-title',
2739 if(this.specificTitle){
2745 if (this.allow_close) {
2757 if(this.size.length){
2758 size = 'modal-' + this.size;
2765 cls: "modal-dialog " + size,
2768 cls : "modal-content",
2771 cls : 'modal-header',
2776 cls : 'modal-footer',
2780 cls: 'btn-' + this.buttonPosition
2797 modal.cls += ' fade';
2803 getChildContainer : function() {
2808 getButtonContainer : function() {
2809 return this.el.select('.modal-footer div',true).first();
2812 initEvents : function()
2814 if (this.allow_close) {
2815 this.closeEl.on('click', this.hide, this);
2817 Roo.EventManager.onWindowResize(this.resize, this, true);
2824 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2826 if (this.fitwindow) {
2827 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2832 if(!this.fitwindow && this.max_width !== 0){
2834 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2841 setSize : function(w,h)
2851 if (!this.rendered) {
2855 //this.el.setStyle('display', 'block');
2856 this.el.removeClass('hideing');
2857 this.el.addClass('show');
2859 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2862 this.el.addClass('in');
2865 this.el.addClass('in');
2868 // not sure how we can show data in here..
2870 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2873 Roo.get(document.body).addClass("x-body-masked");
2875 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2876 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2877 this.maskEl.addClass('show');
2881 this.fireEvent('show', this);
2883 // set zindex here - otherwise it appears to be ignored...
2884 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2887 this.items.forEach( function(e) {
2888 e.layout ? e.layout() : false;
2896 if(this.fireEvent("beforehide", this) !== false){
2897 this.maskEl.removeClass('show');
2898 Roo.get(document.body).removeClass("x-body-masked");
2899 this.el.removeClass('in');
2900 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2902 if(this.animate){ // why
2903 this.el.addClass('hideing');
2905 if (!this.el.hasClass('hideing')) {
2906 return; // it's been shown again...
2908 this.el.removeClass('show');
2909 this.el.removeClass('hideing');
2913 this.el.removeClass('show');
2915 this.fireEvent('hide', this);
2918 isVisible : function()
2921 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2925 addButton : function(str, cb)
2929 var b = Roo.apply({}, { html : str } );
2930 b.xns = b.xns || Roo.bootstrap;
2931 b.xtype = b.xtype || 'Button';
2932 if (typeof(b.listeners) == 'undefined') {
2933 b.listeners = { click : cb.createDelegate(this) };
2936 var btn = Roo.factory(b);
2938 btn.render(this.el.select('.modal-footer div').first());
2944 setDefaultButton : function(btn)
2946 //this.el.select('.modal-footer').()
2950 resizeTo: function(w,h)
2954 this.dialogEl.setWidth(w);
2955 if (this.diff === false) {
2956 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2959 this.bodyEl.setHeight(h-this.diff);
2961 this.fireEvent('resize', this);
2964 setContentSize : function(w, h)
2968 onButtonClick: function(btn,e)
2971 this.fireEvent('btnclick', btn.name, e);
2974 * Set the title of the Dialog
2975 * @param {String} str new Title
2977 setTitle: function(str) {
2978 this.titleEl.dom.innerHTML = str;
2981 * Set the body of the Dialog
2982 * @param {String} str new Title
2984 setBody: function(str) {
2985 this.bodyEl.dom.innerHTML = str;
2988 * Set the body of the Dialog using the template
2989 * @param {Obj} data - apply this data to the template and replace the body contents.
2991 applyBody: function(obj)
2994 Roo.log("Error - using apply Body without a template");
2997 this.tmpl.overwrite(this.bodyEl, obj);
3003 Roo.apply(Roo.bootstrap.Modal, {
3005 * Button config that displays a single OK button
3014 * Button config that displays Yes and No buttons
3030 * Button config that displays OK and Cancel buttons
3045 * Button config that displays Yes, No and Cancel buttons
3069 * messagebox - can be used as a replace
3073 * @class Roo.MessageBox
3074 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3078 Roo.Msg.alert('Status', 'Changes saved successfully.');
3080 // Prompt for user data:
3081 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3083 // process text value...
3087 // Show a dialog using config options:
3089 title:'Save Changes?',
3090 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3091 buttons: Roo.Msg.YESNOCANCEL,
3098 Roo.bootstrap.MessageBox = function(){
3099 var dlg, opt, mask, waitTimer;
3100 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3101 var buttons, activeTextEl, bwidth;
3105 var handleButton = function(button){
3107 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3111 var handleHide = function(){
3113 dlg.el.removeClass(opt.cls);
3116 // Roo.TaskMgr.stop(waitTimer);
3117 // waitTimer = null;
3122 var updateButtons = function(b){
3125 buttons["ok"].hide();
3126 buttons["cancel"].hide();
3127 buttons["yes"].hide();
3128 buttons["no"].hide();
3129 //dlg.footer.dom.style.display = 'none';
3132 dlg.footerEl.dom.style.display = '';
3133 for(var k in buttons){
3134 if(typeof buttons[k] != "function"){
3137 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3138 width += buttons[k].el.getWidth()+15;
3148 var handleEsc = function(d, k, e){
3149 if(opt && opt.closable !== false){
3159 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3160 * @return {Roo.BasicDialog} The BasicDialog element
3162 getDialog : function(){
3164 dlg = new Roo.bootstrap.Modal( {
3167 //constraintoviewport:false,
3169 //collapsible : false,
3174 //buttonAlign:"center",
3175 closeClick : function(){
3176 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3179 handleButton("cancel");
3184 dlg.on("hide", handleHide);
3186 //dlg.addKeyListener(27, handleEsc);
3188 this.buttons = buttons;
3189 var bt = this.buttonText;
3190 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3191 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3192 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3193 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3195 bodyEl = dlg.bodyEl.createChild({
3197 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3198 '<textarea class="roo-mb-textarea"></textarea>' +
3199 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3201 msgEl = bodyEl.dom.firstChild;
3202 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3203 textboxEl.enableDisplayMode();
3204 textboxEl.addKeyListener([10,13], function(){
3205 if(dlg.isVisible() && opt && opt.buttons){
3208 }else if(opt.buttons.yes){
3209 handleButton("yes");
3213 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3214 textareaEl.enableDisplayMode();
3215 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3216 progressEl.enableDisplayMode();
3218 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3219 var pf = progressEl.dom.firstChild;
3221 pp = Roo.get(pf.firstChild);
3222 pp.setHeight(pf.offsetHeight);
3230 * Updates the message box body text
3231 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3232 * the XHTML-compliant non-breaking space character '&#160;')
3233 * @return {Roo.MessageBox} This message box
3235 updateText : function(text)
3237 if(!dlg.isVisible() && !opt.width){
3238 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3239 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3241 msgEl.innerHTML = text || ' ';
3243 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3244 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3246 Math.min(opt.width || cw , this.maxWidth),
3247 Math.max(opt.minWidth || this.minWidth, bwidth)
3250 activeTextEl.setWidth(w);
3252 if(dlg.isVisible()){
3253 dlg.fixedcenter = false;
3255 // to big, make it scroll. = But as usual stupid IE does not support
3258 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3259 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3260 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3262 bodyEl.dom.style.height = '';
3263 bodyEl.dom.style.overflowY = '';
3266 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3268 bodyEl.dom.style.overflowX = '';
3271 dlg.setContentSize(w, bodyEl.getHeight());
3272 if(dlg.isVisible()){
3273 dlg.fixedcenter = true;
3279 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3280 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3281 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3282 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3283 * @return {Roo.MessageBox} This message box
3285 updateProgress : function(value, text){
3287 this.updateText(text);
3290 if (pp) { // weird bug on my firefox - for some reason this is not defined
3291 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3292 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3298 * Returns true if the message box is currently displayed
3299 * @return {Boolean} True if the message box is visible, else false
3301 isVisible : function(){
3302 return dlg && dlg.isVisible();
3306 * Hides the message box if it is displayed
3309 if(this.isVisible()){
3315 * Displays a new message box, or reinitializes an existing message box, based on the config options
3316 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3317 * The following config object properties are supported:
3319 Property Type Description
3320 ---------- --------------- ------------------------------------------------------------------------------------
3321 animEl String/Element An id or Element from which the message box should animate as it opens and
3322 closes (defaults to undefined)
3323 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3324 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3325 closable Boolean False to hide the top-right close button (defaults to true). Note that
3326 progress and wait dialogs will ignore this property and always hide the
3327 close button as they can only be closed programmatically.
3328 cls String A custom CSS class to apply to the message box element
3329 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3330 displayed (defaults to 75)
3331 fn Function A callback function to execute after closing the dialog. The arguments to the
3332 function will be btn (the name of the button that was clicked, if applicable,
3333 e.g. "ok"), and text (the value of the active text field, if applicable).
3334 Progress and wait dialogs will ignore this option since they do not respond to
3335 user actions and can only be closed programmatically, so any required function
3336 should be called by the same code after it closes the dialog.
3337 icon String A CSS class that provides a background image to be used as an icon for
3338 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3339 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3340 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3341 modal Boolean False to allow user interaction with the page while the message box is
3342 displayed (defaults to true)
3343 msg String A string that will replace the existing message box body text (defaults
3344 to the XHTML-compliant non-breaking space character ' ')
3345 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3346 progress Boolean True to display a progress bar (defaults to false)
3347 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3348 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3349 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3350 title String The title text
3351 value String The string value to set into the active textbox element if displayed
3352 wait Boolean True to display a progress bar (defaults to false)
3353 width Number The width of the dialog in pixels
3360 msg: 'Please enter your address:',
3362 buttons: Roo.MessageBox.OKCANCEL,
3365 animEl: 'addAddressBtn'
3368 * @param {Object} config Configuration options
3369 * @return {Roo.MessageBox} This message box
3371 show : function(options)
3374 // this causes nightmares if you show one dialog after another
3375 // especially on callbacks..
3377 if(this.isVisible()){
3380 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3381 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3382 Roo.log("New Dialog Message:" + options.msg )
3383 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3384 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3387 var d = this.getDialog();
3389 d.setTitle(opt.title || " ");
3390 d.closeEl.setDisplayed(opt.closable !== false);
3391 activeTextEl = textboxEl;
3392 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3397 textareaEl.setHeight(typeof opt.multiline == "number" ?
3398 opt.multiline : this.defaultTextHeight);
3399 activeTextEl = textareaEl;
3408 progressEl.setDisplayed(opt.progress === true);
3409 this.updateProgress(0);
3410 activeTextEl.dom.value = opt.value || "";
3412 dlg.setDefaultButton(activeTextEl);
3414 var bs = opt.buttons;
3418 }else if(bs && bs.yes){
3419 db = buttons["yes"];
3421 dlg.setDefaultButton(db);
3423 bwidth = updateButtons(opt.buttons);
3424 this.updateText(opt.msg);
3426 d.el.addClass(opt.cls);
3428 d.proxyDrag = opt.proxyDrag === true;
3429 d.modal = opt.modal !== false;
3430 d.mask = opt.modal !== false ? mask : false;
3432 // force it to the end of the z-index stack so it gets a cursor in FF
3433 document.body.appendChild(dlg.el.dom);
3434 d.animateTarget = null;
3435 d.show(options.animEl);
3441 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3442 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3443 * and closing the message box when the process is complete.
3444 * @param {String} title The title bar text
3445 * @param {String} msg The message box body text
3446 * @return {Roo.MessageBox} This message box
3448 progress : function(title, msg){
3455 minWidth: this.minProgressWidth,
3462 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3463 * If a callback function is passed it will be called after the user clicks the button, and the
3464 * id of the button that was clicked will be passed as the only parameter to the callback
3465 * (could also be the top-right close button).
3466 * @param {String} title The title bar text
3467 * @param {String} msg The message box body text
3468 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3469 * @param {Object} scope (optional) The scope of the callback function
3470 * @return {Roo.MessageBox} This message box
3472 alert : function(title, msg, fn, scope)
3487 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3488 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3489 * You are responsible for closing the message box when the process is complete.
3490 * @param {String} msg The message box body text
3491 * @param {String} title (optional) The title bar text
3492 * @return {Roo.MessageBox} This message box
3494 wait : function(msg, title){
3505 waitTimer = Roo.TaskMgr.start({
3507 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3515 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3516 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3517 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3518 * @param {String} title The title bar text
3519 * @param {String} msg The message box body text
3520 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3521 * @param {Object} scope (optional) The scope of the callback function
3522 * @return {Roo.MessageBox} This message box
3524 confirm : function(title, msg, fn, scope){
3528 buttons: this.YESNO,
3537 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3538 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3539 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3540 * (could also be the top-right close button) and the text that was entered will be passed as the two
3541 * parameters to the callback.
3542 * @param {String} title The title bar text
3543 * @param {String} msg The message box body text
3544 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3545 * @param {Object} scope (optional) The scope of the callback function
3546 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3547 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3548 * @return {Roo.MessageBox} This message box
3550 prompt : function(title, msg, fn, scope, multiline){
3554 buttons: this.OKCANCEL,
3559 multiline: multiline,
3566 * Button config that displays a single OK button
3571 * Button config that displays Yes and No buttons
3574 YESNO : {yes:true, no:true},
3576 * Button config that displays OK and Cancel buttons
3579 OKCANCEL : {ok:true, cancel:true},
3581 * Button config that displays Yes, No and Cancel buttons
3584 YESNOCANCEL : {yes:true, no:true, cancel:true},
3587 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3590 defaultTextHeight : 75,
3592 * The maximum width in pixels of the message box (defaults to 600)
3597 * The minimum width in pixels of the message box (defaults to 100)
3602 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3603 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3606 minProgressWidth : 250,
3608 * An object containing the default button text strings that can be overriden for localized language support.
3609 * Supported properties are: ok, cancel, yes and no.
3610 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3623 * Shorthand for {@link Roo.MessageBox}
3625 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3626 Roo.Msg = Roo.Msg || Roo.MessageBox;
3635 * @class Roo.bootstrap.Navbar
3636 * @extends Roo.bootstrap.Component
3637 * Bootstrap Navbar class
3640 * Create a new Navbar
3641 * @param {Object} config The config object
3645 Roo.bootstrap.Navbar = function(config){
3646 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3650 * @event beforetoggle
3651 * Fire before toggle the menu
3652 * @param {Roo.EventObject} e
3654 "beforetoggle" : true
3658 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3667 getAutoCreate : function(){
3670 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3674 initEvents :function ()
3676 //Roo.log(this.el.select('.navbar-toggle',true));
3677 this.el.select('.navbar-toggle',true).on('click', function() {
3678 if(this.fireEvent('beforetoggle', this) !== false){
3679 this.el.select('.navbar-collapse',true).toggleClass('in');
3689 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3691 var size = this.el.getSize();
3692 this.maskEl.setSize(size.width, size.height);
3693 this.maskEl.enableDisplayMode("block");
3702 getChildContainer : function()
3704 if (this.el.select('.collapse').getCount()) {
3705 return this.el.select('.collapse',true).first();
3738 * @class Roo.bootstrap.NavSimplebar
3739 * @extends Roo.bootstrap.Navbar
3740 * Bootstrap Sidebar class
3742 * @cfg {Boolean} inverse is inverted color
3744 * @cfg {String} type (nav | pills | tabs)
3745 * @cfg {Boolean} arrangement stacked | justified
3746 * @cfg {String} align (left | right) alignment
3748 * @cfg {Boolean} main (true|false) main nav bar? default false
3749 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3751 * @cfg {String} tag (header|footer|nav|div) default is nav
3757 * Create a new Sidebar
3758 * @param {Object} config The config object
3762 Roo.bootstrap.NavSimplebar = function(config){
3763 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3766 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3782 getAutoCreate : function(){
3786 tag : this.tag || 'div',
3799 this.type = this.type || 'nav';
3800 if (['tabs','pills'].indexOf(this.type)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.type
3805 if (this.type!=='nav') {
3806 Roo.log('nav type must be nav/tabs/pills')
3808 cfg.cn[0].cls += ' navbar-nav'
3814 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3815 cfg.cn[0].cls += ' nav-' + this.arrangement;
3819 if (this.align === 'right') {
3820 cfg.cn[0].cls += ' navbar-right';
3824 cfg.cls += ' navbar-inverse';
3851 * @class Roo.bootstrap.NavHeaderbar
3852 * @extends Roo.bootstrap.NavSimplebar
3853 * Bootstrap Sidebar class
3855 * @cfg {String} brand what is brand
3856 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3857 * @cfg {String} brand_href href of the brand
3858 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3859 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3860 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3861 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3864 * Create a new Sidebar
3865 * @param {Object} config The config object
3869 Roo.bootstrap.NavHeaderbar = function(config){
3870 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3874 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3881 desktopCenter : false,
3884 getAutoCreate : function(){
3887 tag: this.nav || 'nav',
3894 if (this.desktopCenter) {
3895 cn.push({cls : 'container', cn : []});
3902 cls: 'navbar-header',
3907 cls: 'navbar-toggle',
3908 'data-toggle': 'collapse',
3913 html: 'Toggle navigation'
3935 cls: 'collapse navbar-collapse',
3939 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3941 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3942 cfg.cls += ' navbar-' + this.position;
3944 // tag can override this..
3946 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3949 if (this.brand !== '') {
3952 href: this.brand_href ? this.brand_href : '#',
3953 cls: 'navbar-brand',
3961 cfg.cls += ' main-nav';
3969 getHeaderChildContainer : function()
3971 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3972 return this.el.select('.navbar-header',true).first();
3975 return this.getChildContainer();
3979 initEvents : function()
3981 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3983 if (this.autohide) {
3988 Roo.get(document).on('scroll',function(e) {
3989 var ns = Roo.get(document).getScroll().top;
3990 var os = prevScroll;
3994 ft.removeClass('slideDown');
3995 ft.addClass('slideUp');
3998 ft.removeClass('slideUp');
3999 ft.addClass('slideDown');
4020 * @class Roo.bootstrap.NavSidebar
4021 * @extends Roo.bootstrap.Navbar
4022 * Bootstrap Sidebar class
4025 * Create a new Sidebar
4026 * @param {Object} config The config object
4030 Roo.bootstrap.NavSidebar = function(config){
4031 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4034 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4036 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4038 getAutoCreate : function(){
4043 cls: 'sidebar sidebar-nav'
4065 * @class Roo.bootstrap.NavGroup
4066 * @extends Roo.bootstrap.Component
4067 * Bootstrap NavGroup class
4068 * @cfg {String} align (left|right)
4069 * @cfg {Boolean} inverse
4070 * @cfg {String} type (nav|pills|tab) default nav
4071 * @cfg {String} navId - reference Id for navbar.
4075 * Create a new nav group
4076 * @param {Object} config The config object
4079 Roo.bootstrap.NavGroup = function(config){
4080 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4083 Roo.bootstrap.NavGroup.register(this);
4087 * Fires when the active item changes
4088 * @param {Roo.bootstrap.NavGroup} this
4089 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4090 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4097 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4108 getAutoCreate : function()
4110 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4117 if (['tabs','pills'].indexOf(this.type)!==-1) {
4118 cfg.cls += ' nav-' + this.type
4120 if (this.type!=='nav') {
4121 Roo.log('nav type must be nav/tabs/pills')
4123 cfg.cls += ' navbar-nav'
4126 if (this.parent() && this.parent().sidebar) {
4129 cls: 'dashboard-menu sidebar-menu'
4135 if (this.form === true) {
4141 if (this.align === 'right') {
4142 cfg.cls += ' navbar-right';
4144 cfg.cls += ' navbar-left';
4148 if (this.align === 'right') {
4149 cfg.cls += ' navbar-right';
4153 cfg.cls += ' navbar-inverse';
4161 * sets the active Navigation item
4162 * @param {Roo.bootstrap.NavItem} the new current navitem
4164 setActiveItem : function(item)
4167 Roo.each(this.navItems, function(v){
4172 v.setActive(false, true);
4179 item.setActive(true, true);
4180 this.fireEvent('changed', this, item, prev);
4185 * gets the active Navigation item
4186 * @return {Roo.bootstrap.NavItem} the current navitem
4188 getActive : function()
4192 Roo.each(this.navItems, function(v){
4203 indexOfNav : function()
4207 Roo.each(this.navItems, function(v,i){
4218 * adds a Navigation item
4219 * @param {Roo.bootstrap.NavItem} the navitem to add
4221 addItem : function(cfg)
4223 var cn = new Roo.bootstrap.NavItem(cfg);
4225 cn.parentId = this.id;
4226 cn.onRender(this.el, null);
4230 * register a Navigation item
4231 * @param {Roo.bootstrap.NavItem} the navitem to add
4233 register : function(item)
4235 this.navItems.push( item);
4236 item.navId = this.navId;
4241 * clear all the Navigation item
4244 clearAll : function()
4247 this.el.dom.innerHTML = '';
4250 getNavItem: function(tabId)
4253 Roo.each(this.navItems, function(e) {
4254 if (e.tabId == tabId) {
4264 setActiveNext : function()
4266 var i = this.indexOfNav(this.getActive());
4267 if (i > this.navItems.length) {
4270 this.setActiveItem(this.navItems[i+1]);
4272 setActivePrev : function()
4274 var i = this.indexOfNav(this.getActive());
4278 this.setActiveItem(this.navItems[i-1]);
4280 clearWasActive : function(except) {
4281 Roo.each(this.navItems, function(e) {
4282 if (e.tabId != except.tabId && e.was_active) {
4283 e.was_active = false;
4290 getWasActive : function ()
4293 Roo.each(this.navItems, function(e) {
4308 Roo.apply(Roo.bootstrap.NavGroup, {
4312 * register a Navigation Group
4313 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4315 register : function(navgrp)
4317 this.groups[navgrp.navId] = navgrp;
4321 * fetch a Navigation Group based on the navigation ID
4322 * @param {string} the navgroup to add
4323 * @returns {Roo.bootstrap.NavGroup} the navgroup
4325 get: function(navId) {
4326 if (typeof(this.groups[navId]) == 'undefined') {
4328 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4330 return this.groups[navId] ;
4345 * @class Roo.bootstrap.NavItem
4346 * @extends Roo.bootstrap.Component
4347 * Bootstrap Navbar.NavItem class
4348 * @cfg {String} href link to
4349 * @cfg {String} html content of button
4350 * @cfg {String} badge text inside badge
4351 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4352 * @cfg {String} glyphicon name of glyphicon
4353 * @cfg {String} icon name of font awesome icon
4354 * @cfg {Boolean} active Is item active
4355 * @cfg {Boolean} disabled Is item disabled
4357 * @cfg {Boolean} preventDefault (true | false) default false
4358 * @cfg {String} tabId the tab that this item activates.
4359 * @cfg {String} tagtype (a|span) render as a href or span?
4360 * @cfg {Boolean} animateRef (true|false) link to element default false
4363 * Create a new Navbar Item
4364 * @param {Object} config The config object
4366 Roo.bootstrap.NavItem = function(config){
4367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4372 * The raw click event for the entire grid.
4373 * @param {Roo.EventObject} e
4378 * Fires when the active item active state changes
4379 * @param {Roo.bootstrap.NavItem} this
4380 * @param {boolean} state the new state
4386 * Fires when scroll to element
4387 * @param {Roo.bootstrap.NavItem} this
4388 * @param {Object} options
4389 * @param {Roo.EventObject} e
4397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4405 preventDefault : false,
4412 getAutoCreate : function(){
4421 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4423 if (this.disabled) {
4424 cfg.cls += ' disabled';
4427 if (this.href || this.html || this.glyphicon || this.icon) {
4431 href : this.href || "#",
4432 html: this.html || ''
4437 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4440 if(this.glyphicon) {
4441 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4446 cfg.cn[0].html += " <span class='caret'></span>";
4450 if (this.badge !== '') {
4452 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4460 initEvents: function()
4462 if (typeof (this.menu) != 'undefined') {
4463 this.menu.parentType = this.xtype;
4464 this.menu.triggerEl = this.el;
4465 this.menu = this.addxtype(Roo.apply({}, this.menu));
4468 this.el.select('a',true).on('click', this.onClick, this);
4470 if(this.tagtype == 'span'){
4471 this.el.select('span',true).on('click', this.onClick, this);
4474 // at this point parent should be available..
4475 this.parent().register(this);
4478 onClick : function(e)
4480 if (e.getTarget('.dropdown-menu-item')) {
4481 // did you click on a menu itemm.... - then don't trigger onclick..
4486 this.preventDefault ||
4489 Roo.log("NavItem - prevent Default?");
4493 if (this.disabled) {
4497 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4498 if (tg && tg.transition) {
4499 Roo.log("waiting for the transitionend");
4505 //Roo.log("fire event clicked");
4506 if(this.fireEvent('click', this, e) === false){
4510 if(this.tagtype == 'span'){
4514 //Roo.log(this.href);
4515 var ael = this.el.select('a',true).first();
4518 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4519 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4520 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4521 return; // ignore... - it's a 'hash' to another page.
4523 Roo.log("NavItem - prevent Default?");
4525 this.scrollToElement(e);
4529 var p = this.parent();
4531 if (['tabs','pills'].indexOf(p.type)!==-1) {
4532 if (typeof(p.setActiveItem) !== 'undefined') {
4533 p.setActiveItem(this);
4537 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4538 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4539 // remove the collapsed menu expand...
4540 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4544 isActive: function () {
4547 setActive : function(state, fire, is_was_active)
4549 if (this.active && !state && this.navId) {
4550 this.was_active = true;
4551 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4553 nv.clearWasActive(this);
4557 this.active = state;
4560 this.el.removeClass('active');
4561 } else if (!this.el.hasClass('active')) {
4562 this.el.addClass('active');
4565 this.fireEvent('changed', this, state);
4568 // show a panel if it's registered and related..
4570 if (!this.navId || !this.tabId || !state || is_was_active) {
4574 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4578 var pan = tg.getPanelByName(this.tabId);
4582 // if we can not flip to new panel - go back to old nav highlight..
4583 if (false == tg.showPanel(pan)) {
4584 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4586 var onav = nv.getWasActive();
4588 onav.setActive(true, false, true);
4597 // this should not be here...
4598 setDisabled : function(state)
4600 this.disabled = state;
4602 this.el.removeClass('disabled');
4603 } else if (!this.el.hasClass('disabled')) {
4604 this.el.addClass('disabled');
4610 * Fetch the element to display the tooltip on.
4611 * @return {Roo.Element} defaults to this.el
4613 tooltipEl : function()
4615 return this.el.select('' + this.tagtype + '', true).first();
4618 scrollToElement : function(e)
4620 var c = document.body;
4623 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4625 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4626 c = document.documentElement;
4629 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4635 var o = target.calcOffsetsTo(c);
4642 this.fireEvent('scrollto', this, options, e);
4644 Roo.get(c).scrollTo('top', options.value, true);
4657 * <span> icon </span>
4658 * <span> text </span>
4659 * <span>badge </span>
4663 * @class Roo.bootstrap.NavSidebarItem
4664 * @extends Roo.bootstrap.NavItem
4665 * Bootstrap Navbar.NavSidebarItem class
4666 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4667 * {Boolean} open is the menu open
4668 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4669 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4670 * {String} buttonSize (sm|md|lg)the extra classes for the button
4671 * {Boolean} showArrow show arrow next to the text (default true)
4673 * Create a new Navbar Button
4674 * @param {Object} config The config object
4676 Roo.bootstrap.NavSidebarItem = function(config){
4677 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4682 * The raw click event for the entire grid.
4683 * @param {Roo.EventObject} e
4688 * Fires when the active item active state changes
4689 * @param {Roo.bootstrap.NavSidebarItem} this
4690 * @param {boolean} state the new state
4698 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4700 badgeWeight : 'default',
4706 buttonWeight : 'default',
4712 getAutoCreate : function(){
4717 href : this.href || '#',
4723 if(this.buttonView){
4726 href : this.href || '#',
4727 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4740 cfg.cls += ' active';
4743 if (this.disabled) {
4744 cfg.cls += ' disabled';
4747 cfg.cls += ' open x-open';
4750 if (this.glyphicon || this.icon) {
4751 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4752 a.cn.push({ tag : 'i', cls : c }) ;
4755 if(!this.buttonView){
4758 html : this.html || ''
4765 if (this.badge !== '') {
4766 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4772 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4775 a.cls += ' dropdown-toggle treeview' ;
4781 initEvents : function()
4783 if (typeof (this.menu) != 'undefined') {
4784 this.menu.parentType = this.xtype;
4785 this.menu.triggerEl = this.el;
4786 this.menu = this.addxtype(Roo.apply({}, this.menu));
4789 this.el.on('click', this.onClick, this);
4791 if(this.badge !== ''){
4792 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4797 onClick : function(e)
4804 if(this.preventDefault){
4808 this.fireEvent('click', this);
4811 disable : function()
4813 this.setDisabled(true);
4818 this.setDisabled(false);
4821 setDisabled : function(state)
4823 if(this.disabled == state){
4827 this.disabled = state;
4830 this.el.addClass('disabled');
4834 this.el.removeClass('disabled');
4839 setActive : function(state)
4841 if(this.active == state){
4845 this.active = state;
4848 this.el.addClass('active');
4852 this.el.removeClass('active');
4857 isActive: function ()
4862 setBadge : function(str)
4868 this.badgeEl.dom.innerHTML = str;
4885 * @class Roo.bootstrap.Row
4886 * @extends Roo.bootstrap.Component
4887 * Bootstrap Row class (contains columns...)
4891 * @param {Object} config The config object
4894 Roo.bootstrap.Row = function(config){
4895 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4898 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4900 getAutoCreate : function(){
4919 * @class Roo.bootstrap.Element
4920 * @extends Roo.bootstrap.Component
4921 * Bootstrap Element class
4922 * @cfg {String} html contents of the element
4923 * @cfg {String} tag tag of the element
4924 * @cfg {String} cls class of the element
4925 * @cfg {Boolean} preventDefault (true|false) default false
4926 * @cfg {Boolean} clickable (true|false) default false
4929 * Create a new Element
4930 * @param {Object} config The config object
4933 Roo.bootstrap.Element = function(config){
4934 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4940 * When a element is chick
4941 * @param {Roo.bootstrap.Element} this
4942 * @param {Roo.EventObject} e
4948 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4953 preventDefault: false,
4956 getAutoCreate : function(){
4960 // cls: this.cls, double assign in parent class Component.js :: onRender
4967 initEvents: function()
4969 Roo.bootstrap.Element.superclass.initEvents.call(this);
4972 this.el.on('click', this.onClick, this);
4977 onClick : function(e)
4979 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4986 getValue : function()
4988 return this.el.dom.innerHTML;
4991 setValue : function(value)
4993 this.el.dom.innerHTML = value;
5008 * @class Roo.bootstrap.Pagination
5009 * @extends Roo.bootstrap.Component
5010 * Bootstrap Pagination class
5011 * @cfg {String} size xs | sm | md | lg
5012 * @cfg {Boolean} inverse false | true
5015 * Create a new Pagination
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Pagination = function(config){
5020 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5023 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5029 getAutoCreate : function(){
5035 cfg.cls += ' inverse';
5041 cfg.cls += " " + this.cls;
5059 * @class Roo.bootstrap.PaginationItem
5060 * @extends Roo.bootstrap.Component
5061 * Bootstrap PaginationItem class
5062 * @cfg {String} html text
5063 * @cfg {String} href the link
5064 * @cfg {Boolean} preventDefault (true | false) default true
5065 * @cfg {Boolean} active (true | false) default false
5066 * @cfg {Boolean} disabled default false
5070 * Create a new PaginationItem
5071 * @param {Object} config The config object
5075 Roo.bootstrap.PaginationItem = function(config){
5076 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5081 * The raw click event for the entire grid.
5082 * @param {Roo.EventObject} e
5088 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5092 preventDefault: true,
5097 getAutoCreate : function(){
5103 href : this.href ? this.href : '#',
5104 html : this.html ? this.html : ''
5114 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5118 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5124 initEvents: function() {
5126 this.el.on('click', this.onClick, this);
5129 onClick : function(e)
5131 Roo.log('PaginationItem on click ');
5132 if(this.preventDefault){
5140 this.fireEvent('click', this, e);
5156 * @class Roo.bootstrap.Slider
5157 * @extends Roo.bootstrap.Component
5158 * Bootstrap Slider class
5161 * Create a new Slider
5162 * @param {Object} config The config object
5165 Roo.bootstrap.Slider = function(config){
5166 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5169 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5171 getAutoCreate : function(){
5175 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5179 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5191 * Ext JS Library 1.1.1
5192 * Copyright(c) 2006-2007, Ext JS, LLC.
5194 * Originally Released Under LGPL - original licence link has changed is not relivant.
5197 * <script type="text/javascript">
5202 * @class Roo.grid.ColumnModel
5203 * @extends Roo.util.Observable
5204 * This is the default implementation of a ColumnModel used by the Grid. It defines
5205 * the columns in the grid.
5208 var colModel = new Roo.grid.ColumnModel([
5209 {header: "Ticker", width: 60, sortable: true, locked: true},
5210 {header: "Company Name", width: 150, sortable: true},
5211 {header: "Market Cap.", width: 100, sortable: true},
5212 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5213 {header: "Employees", width: 100, sortable: true, resizable: false}
5218 * The config options listed for this class are options which may appear in each
5219 * individual column definition.
5220 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5222 * @param {Object} config An Array of column config objects. See this class's
5223 * config objects for details.
5225 Roo.grid.ColumnModel = function(config){
5227 * The config passed into the constructor
5229 this.config = config;
5232 // if no id, create one
5233 // if the column does not have a dataIndex mapping,
5234 // map it to the order it is in the config
5235 for(var i = 0, len = config.length; i < len; i++){
5237 if(typeof c.dataIndex == "undefined"){
5240 if(typeof c.renderer == "string"){
5241 c.renderer = Roo.util.Format[c.renderer];
5243 if(typeof c.id == "undefined"){
5246 if(c.editor && c.editor.xtype){
5247 c.editor = Roo.factory(c.editor, Roo.grid);
5249 if(c.editor && c.editor.isFormField){
5250 c.editor = new Roo.grid.GridEditor(c.editor);
5252 this.lookup[c.id] = c;
5256 * The width of columns which have no width specified (defaults to 100)
5259 this.defaultWidth = 100;
5262 * Default sortable of columns which have no sortable specified (defaults to false)
5265 this.defaultSortable = false;
5269 * @event widthchange
5270 * Fires when the width of a column changes.
5271 * @param {ColumnModel} this
5272 * @param {Number} columnIndex The column index
5273 * @param {Number} newWidth The new width
5275 "widthchange": true,
5277 * @event headerchange
5278 * Fires when the text of a header changes.
5279 * @param {ColumnModel} this
5280 * @param {Number} columnIndex The column index
5281 * @param {Number} newText The new header text
5283 "headerchange": true,
5285 * @event hiddenchange
5286 * Fires when a column is hidden or "unhidden".
5287 * @param {ColumnModel} this
5288 * @param {Number} columnIndex The column index
5289 * @param {Boolean} hidden true if hidden, false otherwise
5291 "hiddenchange": true,
5293 * @event columnmoved
5294 * Fires when a column is moved.
5295 * @param {ColumnModel} this
5296 * @param {Number} oldIndex
5297 * @param {Number} newIndex
5299 "columnmoved" : true,
5301 * @event columlockchange
5302 * Fires when a column's locked state is changed
5303 * @param {ColumnModel} this
5304 * @param {Number} colIndex
5305 * @param {Boolean} locked true if locked
5307 "columnlockchange" : true
5309 Roo.grid.ColumnModel.superclass.constructor.call(this);
5311 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5313 * @cfg {String} header The header text to display in the Grid view.
5316 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5317 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5318 * specified, the column's index is used as an index into the Record's data Array.
5321 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5322 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5325 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5326 * Defaults to the value of the {@link #defaultSortable} property.
5327 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5330 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5333 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5336 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5339 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5342 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5343 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5344 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5345 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5348 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5351 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5354 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5357 * @cfg {String} cursor (Optional)
5360 * @cfg {String} tooltip (Optional)
5363 * @cfg {Number} xs (Optional)
5366 * @cfg {Number} sm (Optional)
5369 * @cfg {Number} md (Optional)
5372 * @cfg {Number} lg (Optional)
5375 * Returns the id of the column at the specified index.
5376 * @param {Number} index The column index
5377 * @return {String} the id
5379 getColumnId : function(index){
5380 return this.config[index].id;
5384 * Returns the column for a specified id.
5385 * @param {String} id The column id
5386 * @return {Object} the column
5388 getColumnById : function(id){
5389 return this.lookup[id];
5394 * Returns the column for a specified dataIndex.
5395 * @param {String} dataIndex The column dataIndex
5396 * @return {Object|Boolean} the column or false if not found
5398 getColumnByDataIndex: function(dataIndex){
5399 var index = this.findColumnIndex(dataIndex);
5400 return index > -1 ? this.config[index] : false;
5404 * Returns the index for a specified column id.
5405 * @param {String} id The column id
5406 * @return {Number} the index, or -1 if not found
5408 getIndexById : function(id){
5409 for(var i = 0, len = this.config.length; i < len; i++){
5410 if(this.config[i].id == id){
5418 * Returns the index for a specified column dataIndex.
5419 * @param {String} dataIndex The column dataIndex
5420 * @return {Number} the index, or -1 if not found
5423 findColumnIndex : function(dataIndex){
5424 for(var i = 0, len = this.config.length; i < len; i++){
5425 if(this.config[i].dataIndex == dataIndex){
5433 moveColumn : function(oldIndex, newIndex){
5434 var c = this.config[oldIndex];
5435 this.config.splice(oldIndex, 1);
5436 this.config.splice(newIndex, 0, c);
5437 this.dataMap = null;
5438 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5441 isLocked : function(colIndex){
5442 return this.config[colIndex].locked === true;
5445 setLocked : function(colIndex, value, suppressEvent){
5446 if(this.isLocked(colIndex) == value){
5449 this.config[colIndex].locked = value;
5451 this.fireEvent("columnlockchange", this, colIndex, value);
5455 getTotalLockedWidth : function(){
5457 for(var i = 0; i < this.config.length; i++){
5458 if(this.isLocked(i) && !this.isHidden(i)){
5459 this.totalWidth += this.getColumnWidth(i);
5465 getLockedCount : function(){
5466 for(var i = 0, len = this.config.length; i < len; i++){
5467 if(!this.isLocked(i)){
5472 return this.config.length;
5476 * Returns the number of columns.
5479 getColumnCount : function(visibleOnly){
5480 if(visibleOnly === true){
5482 for(var i = 0, len = this.config.length; i < len; i++){
5483 if(!this.isHidden(i)){
5489 return this.config.length;
5493 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5494 * @param {Function} fn
5495 * @param {Object} scope (optional)
5496 * @return {Array} result
5498 getColumnsBy : function(fn, scope){
5500 for(var i = 0, len = this.config.length; i < len; i++){
5501 var c = this.config[i];
5502 if(fn.call(scope||this, c, i) === true){
5510 * Returns true if the specified column is sortable.
5511 * @param {Number} col The column index
5514 isSortable : function(col){
5515 if(typeof this.config[col].sortable == "undefined"){
5516 return this.defaultSortable;
5518 return this.config[col].sortable;
5522 * Returns the rendering (formatting) function defined for the column.
5523 * @param {Number} col The column index.
5524 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5526 getRenderer : function(col){
5527 if(!this.config[col].renderer){
5528 return Roo.grid.ColumnModel.defaultRenderer;
5530 return this.config[col].renderer;
5534 * Sets the rendering (formatting) function for a column.
5535 * @param {Number} col The column index
5536 * @param {Function} fn The function to use to process the cell's raw data
5537 * to return HTML markup for the grid view. The render function is called with
5538 * the following parameters:<ul>
5539 * <li>Data value.</li>
5540 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5541 * <li>css A CSS style string to apply to the table cell.</li>
5542 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5543 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5544 * <li>Row index</li>
5545 * <li>Column index</li>
5546 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5548 setRenderer : function(col, fn){
5549 this.config[col].renderer = fn;
5553 * Returns the width for the specified column.
5554 * @param {Number} col The column index
5557 getColumnWidth : function(col){
5558 return this.config[col].width * 1 || this.defaultWidth;
5562 * Sets the width for a column.
5563 * @param {Number} col The column index
5564 * @param {Number} width The new width
5566 setColumnWidth : function(col, width, suppressEvent){
5567 this.config[col].width = width;
5568 this.totalWidth = null;
5570 this.fireEvent("widthchange", this, col, width);
5575 * Returns the total width of all columns.
5576 * @param {Boolean} includeHidden True to include hidden column widths
5579 getTotalWidth : function(includeHidden){
5580 if(!this.totalWidth){
5581 this.totalWidth = 0;
5582 for(var i = 0, len = this.config.length; i < len; i++){
5583 if(includeHidden || !this.isHidden(i)){
5584 this.totalWidth += this.getColumnWidth(i);
5588 return this.totalWidth;
5592 * Returns the header for the specified column.
5593 * @param {Number} col The column index
5596 getColumnHeader : function(col){
5597 return this.config[col].header;
5601 * Sets the header for a column.
5602 * @param {Number} col The column index
5603 * @param {String} header The new header
5605 setColumnHeader : function(col, header){
5606 this.config[col].header = header;
5607 this.fireEvent("headerchange", this, col, header);
5611 * Returns the tooltip for the specified column.
5612 * @param {Number} col The column index
5615 getColumnTooltip : function(col){
5616 return this.config[col].tooltip;
5619 * Sets the tooltip for a column.
5620 * @param {Number} col The column index
5621 * @param {String} tooltip The new tooltip
5623 setColumnTooltip : function(col, tooltip){
5624 this.config[col].tooltip = tooltip;
5628 * Returns the dataIndex for the specified column.
5629 * @param {Number} col The column index
5632 getDataIndex : function(col){
5633 return this.config[col].dataIndex;
5637 * Sets the dataIndex for a column.
5638 * @param {Number} col The column index
5639 * @param {Number} dataIndex The new dataIndex
5641 setDataIndex : function(col, dataIndex){
5642 this.config[col].dataIndex = dataIndex;
5648 * Returns true if the cell is editable.
5649 * @param {Number} colIndex The column index
5650 * @param {Number} rowIndex The row index - this is nto actually used..?
5653 isCellEditable : function(colIndex, rowIndex){
5654 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5658 * Returns the editor defined for the cell/column.
5659 * return false or null to disable editing.
5660 * @param {Number} colIndex The column index
5661 * @param {Number} rowIndex The row index
5664 getCellEditor : function(colIndex, rowIndex){
5665 return this.config[colIndex].editor;
5669 * Sets if a column is editable.
5670 * @param {Number} col The column index
5671 * @param {Boolean} editable True if the column is editable
5673 setEditable : function(col, editable){
5674 this.config[col].editable = editable;
5679 * Returns true if the column is hidden.
5680 * @param {Number} colIndex The column index
5683 isHidden : function(colIndex){
5684 return this.config[colIndex].hidden;
5689 * Returns true if the column width cannot be changed
5691 isFixed : function(colIndex){
5692 return this.config[colIndex].fixed;
5696 * Returns true if the column can be resized
5699 isResizable : function(colIndex){
5700 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5703 * Sets if a column is hidden.
5704 * @param {Number} colIndex The column index
5705 * @param {Boolean} hidden True if the column is hidden
5707 setHidden : function(colIndex, hidden){
5708 this.config[colIndex].hidden = hidden;
5709 this.totalWidth = null;
5710 this.fireEvent("hiddenchange", this, colIndex, hidden);
5714 * Sets the editor for a column.
5715 * @param {Number} col The column index
5716 * @param {Object} editor The editor object
5718 setEditor : function(col, editor){
5719 this.config[col].editor = editor;
5723 Roo.grid.ColumnModel.defaultRenderer = function(value)
5725 if(typeof value == "object") {
5728 if(typeof value == "string" && value.length < 1){
5732 return String.format("{0}", value);
5735 // Alias for backwards compatibility
5736 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5739 * Ext JS Library 1.1.1
5740 * Copyright(c) 2006-2007, Ext JS, LLC.
5742 * Originally Released Under LGPL - original licence link has changed is not relivant.
5745 * <script type="text/javascript">
5749 * @class Roo.LoadMask
5750 * A simple utility class for generically masking elements while loading data. If the element being masked has
5751 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5752 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5753 * element's UpdateManager load indicator and will be destroyed after the initial load.
5755 * Create a new LoadMask
5756 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5757 * @param {Object} config The config object
5759 Roo.LoadMask = function(el, config){
5760 this.el = Roo.get(el);
5761 Roo.apply(this, config);
5763 this.store.on('beforeload', this.onBeforeLoad, this);
5764 this.store.on('load', this.onLoad, this);
5765 this.store.on('loadexception', this.onLoadException, this);
5766 this.removeMask = false;
5768 var um = this.el.getUpdateManager();
5769 um.showLoadIndicator = false; // disable the default indicator
5770 um.on('beforeupdate', this.onBeforeLoad, this);
5771 um.on('update', this.onLoad, this);
5772 um.on('failure', this.onLoad, this);
5773 this.removeMask = true;
5777 Roo.LoadMask.prototype = {
5779 * @cfg {Boolean} removeMask
5780 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5781 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5785 * The text to display in a centered loading message box (defaults to 'Loading...')
5789 * @cfg {String} msgCls
5790 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5792 msgCls : 'x-mask-loading',
5795 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5801 * Disables the mask to prevent it from being displayed
5803 disable : function(){
5804 this.disabled = true;
5808 * Enables the mask so that it can be displayed
5810 enable : function(){
5811 this.disabled = false;
5814 onLoadException : function()
5818 if (typeof(arguments[3]) != 'undefined') {
5819 Roo.MessageBox.alert("Error loading",arguments[3]);
5823 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5824 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5831 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5836 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5840 onBeforeLoad : function(){
5842 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5847 destroy : function(){
5849 this.store.un('beforeload', this.onBeforeLoad, this);
5850 this.store.un('load', this.onLoad, this);
5851 this.store.un('loadexception', this.onLoadException, this);
5853 var um = this.el.getUpdateManager();
5854 um.un('beforeupdate', this.onBeforeLoad, this);
5855 um.un('update', this.onLoad, this);
5856 um.un('failure', this.onLoad, this);
5867 * @class Roo.bootstrap.Table
5868 * @extends Roo.bootstrap.Component
5869 * Bootstrap Table class
5870 * @cfg {String} cls table class
5871 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5872 * @cfg {String} bgcolor Specifies the background color for a table
5873 * @cfg {Number} border Specifies whether the table cells should have borders or not
5874 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5875 * @cfg {Number} cellspacing Specifies the space between cells
5876 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5877 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5878 * @cfg {String} sortable Specifies that the table should be sortable
5879 * @cfg {String} summary Specifies a summary of the content of a table
5880 * @cfg {Number} width Specifies the width of a table
5881 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5883 * @cfg {boolean} striped Should the rows be alternative striped
5884 * @cfg {boolean} bordered Add borders to the table
5885 * @cfg {boolean} hover Add hover highlighting
5886 * @cfg {boolean} condensed Format condensed
5887 * @cfg {boolean} responsive Format condensed
5888 * @cfg {Boolean} loadMask (true|false) default false
5889 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5890 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5891 * @cfg {Boolean} rowSelection (true|false) default false
5892 * @cfg {Boolean} cellSelection (true|false) default false
5893 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5894 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5895 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5896 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5900 * Create a new Table
5901 * @param {Object} config The config object
5904 Roo.bootstrap.Table = function(config){
5905 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5910 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5911 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5912 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5913 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5915 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5917 this.sm.grid = this;
5918 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5919 this.sm = this.selModel;
5920 this.sm.xmodule = this.xmodule || false;
5923 if (this.cm && typeof(this.cm.config) == 'undefined') {
5924 this.colModel = new Roo.grid.ColumnModel(this.cm);
5925 this.cm = this.colModel;
5926 this.cm.xmodule = this.xmodule || false;
5929 this.store= Roo.factory(this.store, Roo.data);
5930 this.ds = this.store;
5931 this.ds.xmodule = this.xmodule || false;
5934 if (this.footer && this.store) {
5935 this.footer.dataSource = this.ds;
5936 this.footer = Roo.factory(this.footer);
5943 * Fires when a cell is clicked
5944 * @param {Roo.bootstrap.Table} this
5945 * @param {Roo.Element} el
5946 * @param {Number} rowIndex
5947 * @param {Number} columnIndex
5948 * @param {Roo.EventObject} e
5952 * @event celldblclick
5953 * Fires when a cell is double clicked
5954 * @param {Roo.bootstrap.Table} this
5955 * @param {Roo.Element} el
5956 * @param {Number} rowIndex
5957 * @param {Number} columnIndex
5958 * @param {Roo.EventObject} e
5960 "celldblclick" : true,
5963 * Fires when a row is clicked
5964 * @param {Roo.bootstrap.Table} this
5965 * @param {Roo.Element} el
5966 * @param {Number} rowIndex
5967 * @param {Roo.EventObject} e
5971 * @event rowdblclick
5972 * Fires when a row is double clicked
5973 * @param {Roo.bootstrap.Table} this
5974 * @param {Roo.Element} el
5975 * @param {Number} rowIndex
5976 * @param {Roo.EventObject} e
5978 "rowdblclick" : true,
5981 * Fires when a mouseover occur
5982 * @param {Roo.bootstrap.Table} this
5983 * @param {Roo.Element} el
5984 * @param {Number} rowIndex
5985 * @param {Number} columnIndex
5986 * @param {Roo.EventObject} e
5991 * Fires when a mouseout occur
5992 * @param {Roo.bootstrap.Table} this
5993 * @param {Roo.Element} el
5994 * @param {Number} rowIndex
5995 * @param {Number} columnIndex
5996 * @param {Roo.EventObject} e
6001 * Fires when a row is rendered, so you can change add a style to it.
6002 * @param {Roo.bootstrap.Table} this
6003 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6007 * @event rowsrendered
6008 * Fires when all the rows have been rendered
6009 * @param {Roo.bootstrap.Table} this
6011 'rowsrendered' : true,
6013 * @event contextmenu
6014 * The raw contextmenu event for the entire grid.
6015 * @param {Roo.EventObject} e
6017 "contextmenu" : true,
6019 * @event rowcontextmenu
6020 * Fires when a row is right clicked
6021 * @param {Roo.bootstrap.Table} this
6022 * @param {Number} rowIndex
6023 * @param {Roo.EventObject} e
6025 "rowcontextmenu" : true,
6027 * @event cellcontextmenu
6028 * Fires when a cell is right clicked
6029 * @param {Roo.bootstrap.Table} this
6030 * @param {Number} rowIndex
6031 * @param {Number} cellIndex
6032 * @param {Roo.EventObject} e
6034 "cellcontextmenu" : true,
6036 * @event headercontextmenu
6037 * Fires when a header is right clicked
6038 * @param {Roo.bootstrap.Table} this
6039 * @param {Number} columnIndex
6040 * @param {Roo.EventObject} e
6042 "headercontextmenu" : true
6046 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6072 rowSelection : false,
6073 cellSelection : false,
6076 // Roo.Element - the tbody
6078 // Roo.Element - thead element
6081 container: false, // used by gridpanel...
6087 auto_hide_footer : false,
6089 getAutoCreate : function()
6091 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6098 if (this.scrollBody) {
6099 cfg.cls += ' table-body-fixed';
6102 cfg.cls += ' table-striped';
6106 cfg.cls += ' table-hover';
6108 if (this.bordered) {
6109 cfg.cls += ' table-bordered';
6111 if (this.condensed) {
6112 cfg.cls += ' table-condensed';
6114 if (this.responsive) {
6115 cfg.cls += ' table-responsive';
6119 cfg.cls+= ' ' +this.cls;
6122 // this lot should be simplifed...
6135 ].forEach(function(k) {
6143 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6146 if(this.store || this.cm){
6147 if(this.headerShow){
6148 cfg.cn.push(this.renderHeader());
6151 cfg.cn.push(this.renderBody());
6153 if(this.footerShow){
6154 cfg.cn.push(this.renderFooter());
6156 // where does this come from?
6157 //cfg.cls+= ' TableGrid';
6160 return { cn : [ cfg ] };
6163 initEvents : function()
6165 if(!this.store || !this.cm){
6168 if (this.selModel) {
6169 this.selModel.initEvents();
6173 //Roo.log('initEvents with ds!!!!');
6175 this.mainBody = this.el.select('tbody', true).first();
6176 this.mainHead = this.el.select('thead', true).first();
6177 this.mainFoot = this.el.select('tfoot', true).first();
6183 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6184 e.on('click', _this.sort, _this);
6187 this.mainBody.on("click", this.onClick, this);
6188 this.mainBody.on("dblclick", this.onDblClick, this);
6190 // why is this done????? = it breaks dialogs??
6191 //this.parent().el.setStyle('position', 'relative');
6195 this.footer.parentId = this.id;
6196 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6199 this.el.select('tfoot tr td').first().addClass('hide');
6204 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6207 this.store.on('load', this.onLoad, this);
6208 this.store.on('beforeload', this.onBeforeLoad, this);
6209 this.store.on('update', this.onUpdate, this);
6210 this.store.on('add', this.onAdd, this);
6211 this.store.on("clear", this.clear, this);
6213 this.el.on("contextmenu", this.onContextMenu, this);
6215 this.mainBody.on('scroll', this.onBodyScroll, this);
6217 this.cm.on("headerchange", this.onHeaderChange, this);
6219 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6223 onContextMenu : function(e, t)
6225 this.processEvent("contextmenu", e);
6228 processEvent : function(name, e)
6230 if (name != 'touchstart' ) {
6231 this.fireEvent(name, e);
6234 var t = e.getTarget();
6236 var cell = Roo.get(t);
6242 if(cell.findParent('tfoot', false, true)){
6246 if(cell.findParent('thead', false, true)){
6248 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6249 cell = Roo.get(t).findParent('th', false, true);
6251 Roo.log("failed to find th in thead?");
6252 Roo.log(e.getTarget());
6257 var cellIndex = cell.dom.cellIndex;
6259 var ename = name == 'touchstart' ? 'click' : name;
6260 this.fireEvent("header" + ename, this, cellIndex, e);
6265 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6266 cell = Roo.get(t).findParent('td', false, true);
6268 Roo.log("failed to find th in tbody?");
6269 Roo.log(e.getTarget());
6274 var row = cell.findParent('tr', false, true);
6275 var cellIndex = cell.dom.cellIndex;
6276 var rowIndex = row.dom.rowIndex - 1;
6280 this.fireEvent("row" + name, this, rowIndex, e);
6284 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6290 onMouseover : function(e, el)
6292 var cell = Roo.get(el);
6298 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6299 cell = cell.findParent('td', false, true);
6302 var row = cell.findParent('tr', false, true);
6303 var cellIndex = cell.dom.cellIndex;
6304 var rowIndex = row.dom.rowIndex - 1; // start from 0
6306 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6310 onMouseout : function(e, el)
6312 var cell = Roo.get(el);
6318 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6319 cell = cell.findParent('td', false, true);
6322 var row = cell.findParent('tr', false, true);
6323 var cellIndex = cell.dom.cellIndex;
6324 var rowIndex = row.dom.rowIndex - 1; // start from 0
6326 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6330 onClick : function(e, el)
6332 var cell = Roo.get(el);
6334 if(!cell || (!this.cellSelection && !this.rowSelection)){
6338 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6339 cell = cell.findParent('td', false, true);
6342 if(!cell || typeof(cell) == 'undefined'){
6346 var row = cell.findParent('tr', false, true);
6348 if(!row || typeof(row) == 'undefined'){
6352 var cellIndex = cell.dom.cellIndex;
6353 var rowIndex = this.getRowIndex(row);
6355 // why??? - should these not be based on SelectionModel?
6356 if(this.cellSelection){
6357 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6360 if(this.rowSelection){
6361 this.fireEvent('rowclick', this, row, rowIndex, e);
6367 onDblClick : function(e,el)
6369 var cell = Roo.get(el);
6371 if(!cell || (!this.cellSelection && !this.rowSelection)){
6375 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6376 cell = cell.findParent('td', false, true);
6379 if(!cell || typeof(cell) == 'undefined'){
6383 var row = cell.findParent('tr', false, true);
6385 if(!row || typeof(row) == 'undefined'){
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = this.getRowIndex(row);
6392 if(this.cellSelection){
6393 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6396 if(this.rowSelection){
6397 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6401 sort : function(e,el)
6403 var col = Roo.get(el);
6405 if(!col.hasClass('sortable')){
6409 var sort = col.attr('sort');
6412 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6416 this.store.sortInfo = {field : sort, direction : dir};
6419 Roo.log("calling footer first");
6420 this.footer.onClick('first');
6423 this.store.load({ params : { start : 0 } });
6427 renderHeader : function()
6435 this.totalWidth = 0;
6437 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6439 var config = cm.config[i];
6443 cls : 'x-hcol-' + i,
6445 html: cm.getColumnHeader(i)
6450 if(typeof(config.sortable) != 'undefined' && config.sortable){
6452 c.html = '<i class="glyphicon"></i>' + c.html;
6455 if(typeof(config.lgHeader) != 'undefined'){
6456 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6459 if(typeof(config.mdHeader) != 'undefined'){
6460 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6463 if(typeof(config.smHeader) != 'undefined'){
6464 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6467 if(typeof(config.xsHeader) != 'undefined'){
6468 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6475 if(typeof(config.tooltip) != 'undefined'){
6476 c.tooltip = config.tooltip;
6479 if(typeof(config.colspan) != 'undefined'){
6480 c.colspan = config.colspan;
6483 if(typeof(config.hidden) != 'undefined' && config.hidden){
6484 c.style += ' display:none;';
6487 if(typeof(config.dataIndex) != 'undefined'){
6488 c.sort = config.dataIndex;
6493 if(typeof(config.align) != 'undefined' && config.align.length){
6494 c.style += ' text-align:' + config.align + ';';
6497 if(typeof(config.width) != 'undefined'){
6498 c.style += ' width:' + config.width + 'px;';
6499 this.totalWidth += config.width;
6501 this.totalWidth += 100; // assume minimum of 100 per column?
6504 if(typeof(config.cls) != 'undefined'){
6505 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6508 ['xs','sm','md','lg'].map(function(size){
6510 if(typeof(config[size]) == 'undefined'){
6514 if (!config[size]) { // 0 = hidden
6515 c.cls += ' hidden-' + size;
6519 c.cls += ' col-' + size + '-' + config[size];
6529 renderBody : function()
6539 colspan : this.cm.getColumnCount()
6549 renderFooter : function()
6559 colspan : this.cm.getColumnCount()
6573 // Roo.log('ds onload');
6578 var ds = this.store;
6580 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6581 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6582 if (_this.store.sortInfo) {
6584 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6585 e.select('i', true).addClass(['glyphicon-arrow-up']);
6588 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6589 e.select('i', true).addClass(['glyphicon-arrow-down']);
6594 var tbody = this.mainBody;
6596 if(ds.getCount() > 0){
6597 ds.data.each(function(d,rowIndex){
6598 var row = this.renderRow(cm, ds, rowIndex);
6600 tbody.createChild(row);
6604 if(row.cellObjects.length){
6605 Roo.each(row.cellObjects, function(r){
6606 _this.renderCellObject(r);
6613 var tfoot = this.el.select('tfoot', true).first();
6615 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6617 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6619 var total = this.ds.getTotalCount();
6621 if(this.footer.pageSize < total){
6622 this.mainFoot.show();
6626 Roo.each(this.el.select('tbody td', true).elements, function(e){
6627 e.on('mouseover', _this.onMouseover, _this);
6630 Roo.each(this.el.select('tbody td', true).elements, function(e){
6631 e.on('mouseout', _this.onMouseout, _this);
6633 this.fireEvent('rowsrendered', this);
6639 onUpdate : function(ds,record)
6641 this.refreshRow(record);
6645 onRemove : function(ds, record, index, isUpdate){
6646 if(isUpdate !== true){
6647 this.fireEvent("beforerowremoved", this, index, record);
6649 var bt = this.mainBody.dom;
6651 var rows = this.el.select('tbody > tr', true).elements;
6653 if(typeof(rows[index]) != 'undefined'){
6654 bt.removeChild(rows[index].dom);
6657 // if(bt.rows[index]){
6658 // bt.removeChild(bt.rows[index]);
6661 if(isUpdate !== true){
6662 //this.stripeRows(index);
6663 //this.syncRowHeights(index, index);
6665 this.fireEvent("rowremoved", this, index, record);
6669 onAdd : function(ds, records, rowIndex)
6671 //Roo.log('on Add called');
6672 // - note this does not handle multiple adding very well..
6673 var bt = this.mainBody.dom;
6674 for (var i =0 ; i < records.length;i++) {
6675 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6676 //Roo.log(records[i]);
6677 //Roo.log(this.store.getAt(rowIndex+i));
6678 this.insertRow(this.store, rowIndex + i, false);
6685 refreshRow : function(record){
6686 var ds = this.store, index;
6687 if(typeof record == 'number'){
6689 record = ds.getAt(index);
6691 index = ds.indexOf(record);
6693 this.insertRow(ds, index, true);
6695 this.onRemove(ds, record, index+1, true);
6697 //this.syncRowHeights(index, index);
6699 this.fireEvent("rowupdated", this, index, record);
6702 insertRow : function(dm, rowIndex, isUpdate){
6705 this.fireEvent("beforerowsinserted", this, rowIndex);
6707 //var s = this.getScrollState();
6708 var row = this.renderRow(this.cm, this.store, rowIndex);
6709 // insert before rowIndex..
6710 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6714 if(row.cellObjects.length){
6715 Roo.each(row.cellObjects, function(r){
6716 _this.renderCellObject(r);
6721 this.fireEvent("rowsinserted", this, rowIndex);
6722 //this.syncRowHeights(firstRow, lastRow);
6723 //this.stripeRows(firstRow);
6730 getRowDom : function(rowIndex)
6732 var rows = this.el.select('tbody > tr', true).elements;
6734 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6737 // returns the object tree for a tr..
6740 renderRow : function(cm, ds, rowIndex)
6742 var d = ds.getAt(rowIndex);
6746 cls : 'x-row-' + rowIndex,
6750 var cellObjects = [];
6752 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6753 var config = cm.config[i];
6755 var renderer = cm.getRenderer(i);
6759 if(typeof(renderer) !== 'undefined'){
6760 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6762 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6763 // and are rendered into the cells after the row is rendered - using the id for the element.
6765 if(typeof(value) === 'object'){
6775 rowIndex : rowIndex,
6780 this.fireEvent('rowclass', this, rowcfg);
6784 cls : rowcfg.rowClass + ' x-col-' + i,
6786 html: (typeof(value) === 'object') ? '' : value
6793 if(typeof(config.colspan) != 'undefined'){
6794 td.colspan = config.colspan;
6797 if(typeof(config.hidden) != 'undefined' && config.hidden){
6798 td.style += ' display:none;';
6801 if(typeof(config.align) != 'undefined' && config.align.length){
6802 td.style += ' text-align:' + config.align + ';';
6804 if(typeof(config.valign) != 'undefined' && config.valign.length){
6805 td.style += ' vertical-align:' + config.valign + ';';
6808 if(typeof(config.width) != 'undefined'){
6809 td.style += ' width:' + config.width + 'px;';
6812 if(typeof(config.cursor) != 'undefined'){
6813 td.style += ' cursor:' + config.cursor + ';';
6816 if(typeof(config.cls) != 'undefined'){
6817 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6820 ['xs','sm','md','lg'].map(function(size){
6822 if(typeof(config[size]) == 'undefined'){
6826 if (!config[size]) { // 0 = hidden
6827 td.cls += ' hidden-' + size;
6831 td.cls += ' col-' + size + '-' + config[size];
6839 row.cellObjects = cellObjects;
6847 onBeforeLoad : function()
6856 this.el.select('tbody', true).first().dom.innerHTML = '';
6859 * Show or hide a row.
6860 * @param {Number} rowIndex to show or hide
6861 * @param {Boolean} state hide
6863 setRowVisibility : function(rowIndex, state)
6865 var bt = this.mainBody.dom;
6867 var rows = this.el.select('tbody > tr', true).elements;
6869 if(typeof(rows[rowIndex]) == 'undefined'){
6872 rows[rowIndex].dom.style.display = state ? '' : 'none';
6876 getSelectionModel : function(){
6878 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6880 return this.selModel;
6883 * Render the Roo.bootstrap object from renderder
6885 renderCellObject : function(r)
6889 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6891 var t = r.cfg.render(r.container);
6894 Roo.each(r.cfg.cn, function(c){
6896 container: t.getChildContainer(),
6899 _this.renderCellObject(child);
6904 getRowIndex : function(row)
6908 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6919 * Returns the grid's underlying element = used by panel.Grid
6920 * @return {Element} The element
6922 getGridEl : function(){
6926 * Forces a resize - used by panel.Grid
6927 * @return {Element} The element
6929 autoSize : function()
6931 //var ctr = Roo.get(this.container.dom.parentElement);
6932 var ctr = Roo.get(this.el.dom);
6934 var thd = this.getGridEl().select('thead',true).first();
6935 var tbd = this.getGridEl().select('tbody', true).first();
6936 var tfd = this.getGridEl().select('tfoot', true).first();
6938 var cw = ctr.getWidth();
6942 tbd.setSize(ctr.getWidth(),
6943 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6945 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6948 cw = Math.max(cw, this.totalWidth);
6949 this.getGridEl().select('tr',true).setWidth(cw);
6950 // resize 'expandable coloumn?
6952 return; // we doe not have a view in this design..
6955 onBodyScroll: function()
6957 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6959 this.mainHead.setStyle({
6960 'position' : 'relative',
6961 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6967 var scrollHeight = this.mainBody.dom.scrollHeight;
6969 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6971 var height = this.mainBody.getHeight();
6973 if(scrollHeight - height == scrollTop) {
6975 var total = this.ds.getTotalCount();
6977 if(this.footer.cursor + this.footer.pageSize < total){
6979 this.footer.ds.load({
6981 start : this.footer.cursor + this.footer.pageSize,
6982 limit : this.footer.pageSize
6992 onHeaderChange : function()
6994 var header = this.renderHeader();
6995 var table = this.el.select('table', true).first();
6997 this.mainHead.remove();
6998 this.mainHead = table.createChild(header, this.mainBody, false);
7001 onHiddenChange : function(colModel, colIndex, hidden)
7003 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7004 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7006 this.CSS.updateRule(thSelector, "display", "");
7007 this.CSS.updateRule(tdSelector, "display", "");
7010 this.CSS.updateRule(thSelector, "display", "none");
7011 this.CSS.updateRule(tdSelector, "display", "none");
7014 this.onHeaderChange();
7031 * @class Roo.bootstrap.TableCell
7032 * @extends Roo.bootstrap.Component
7033 * Bootstrap TableCell class
7034 * @cfg {String} html cell contain text
7035 * @cfg {String} cls cell class
7036 * @cfg {String} tag cell tag (td|th) default td
7037 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7038 * @cfg {String} align Aligns the content in a cell
7039 * @cfg {String} axis Categorizes cells
7040 * @cfg {String} bgcolor Specifies the background color of a cell
7041 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7042 * @cfg {Number} colspan Specifies the number of columns a cell should span
7043 * @cfg {String} headers Specifies one or more header cells a cell is related to
7044 * @cfg {Number} height Sets the height of a cell
7045 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7046 * @cfg {Number} rowspan Sets the number of rows a cell should span
7047 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7048 * @cfg {String} valign Vertical aligns the content in a cell
7049 * @cfg {Number} width Specifies the width of a cell
7052 * Create a new TableCell
7053 * @param {Object} config The config object
7056 Roo.bootstrap.TableCell = function(config){
7057 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7060 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7080 getAutoCreate : function(){
7081 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7101 cfg.align=this.align
7107 cfg.bgcolor=this.bgcolor
7110 cfg.charoff=this.charoff
7113 cfg.colspan=this.colspan
7116 cfg.headers=this.headers
7119 cfg.height=this.height
7122 cfg.nowrap=this.nowrap
7125 cfg.rowspan=this.rowspan
7128 cfg.scope=this.scope
7131 cfg.valign=this.valign
7134 cfg.width=this.width
7153 * @class Roo.bootstrap.TableRow
7154 * @extends Roo.bootstrap.Component
7155 * Bootstrap TableRow class
7156 * @cfg {String} cls row class
7157 * @cfg {String} align Aligns the content in a table row
7158 * @cfg {String} bgcolor Specifies a background color for a table row
7159 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7160 * @cfg {String} valign Vertical aligns the content in a table row
7163 * Create a new TableRow
7164 * @param {Object} config The config object
7167 Roo.bootstrap.TableRow = function(config){
7168 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7171 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7179 getAutoCreate : function(){
7180 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7190 cfg.align = this.align;
7193 cfg.bgcolor = this.bgcolor;
7196 cfg.charoff = this.charoff;
7199 cfg.valign = this.valign;
7217 * @class Roo.bootstrap.TableBody
7218 * @extends Roo.bootstrap.Component
7219 * Bootstrap TableBody class
7220 * @cfg {String} cls element class
7221 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7222 * @cfg {String} align Aligns the content inside the element
7223 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7224 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7227 * Create a new TableBody
7228 * @param {Object} config The config object
7231 Roo.bootstrap.TableBody = function(config){
7232 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7235 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7243 getAutoCreate : function(){
7244 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7258 cfg.align = this.align;
7261 cfg.charoff = this.charoff;
7264 cfg.valign = this.valign;
7271 // initEvents : function()
7278 // this.store = Roo.factory(this.store, Roo.data);
7279 // this.store.on('load', this.onLoad, this);
7281 // this.store.load();
7285 // onLoad: function ()
7287 // this.fireEvent('load', this);
7297 * Ext JS Library 1.1.1
7298 * Copyright(c) 2006-2007, Ext JS, LLC.
7300 * Originally Released Under LGPL - original licence link has changed is not relivant.
7303 * <script type="text/javascript">
7306 // as we use this in bootstrap.
7307 Roo.namespace('Roo.form');
7309 * @class Roo.form.Action
7310 * Internal Class used to handle form actions
7312 * @param {Roo.form.BasicForm} el The form element or its id
7313 * @param {Object} config Configuration options
7318 // define the action interface
7319 Roo.form.Action = function(form, options){
7321 this.options = options || {};
7324 * Client Validation Failed
7327 Roo.form.Action.CLIENT_INVALID = 'client';
7329 * Server Validation Failed
7332 Roo.form.Action.SERVER_INVALID = 'server';
7334 * Connect to Server Failed
7337 Roo.form.Action.CONNECT_FAILURE = 'connect';
7339 * Reading Data from Server Failed
7342 Roo.form.Action.LOAD_FAILURE = 'load';
7344 Roo.form.Action.prototype = {
7346 failureType : undefined,
7347 response : undefined,
7351 run : function(options){
7356 success : function(response){
7361 handleResponse : function(response){
7365 // default connection failure
7366 failure : function(response){
7368 this.response = response;
7369 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7370 this.form.afterAction(this, false);
7373 processResponse : function(response){
7374 this.response = response;
7375 if(!response.responseText){
7378 this.result = this.handleResponse(response);
7382 // utility functions used internally
7383 getUrl : function(appendParams){
7384 var url = this.options.url || this.form.url || this.form.el.dom.action;
7386 var p = this.getParams();
7388 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7394 getMethod : function(){
7395 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7398 getParams : function(){
7399 var bp = this.form.baseParams;
7400 var p = this.options.params;
7402 if(typeof p == "object"){
7403 p = Roo.urlEncode(Roo.applyIf(p, bp));
7404 }else if(typeof p == 'string' && bp){
7405 p += '&' + Roo.urlEncode(bp);
7408 p = Roo.urlEncode(bp);
7413 createCallback : function(){
7415 success: this.success,
7416 failure: this.failure,
7418 timeout: (this.form.timeout*1000),
7419 upload: this.form.fileUpload ? this.success : undefined
7424 Roo.form.Action.Submit = function(form, options){
7425 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7428 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7431 haveProgress : false,
7432 uploadComplete : false,
7434 // uploadProgress indicator.
7435 uploadProgress : function()
7437 if (!this.form.progressUrl) {
7441 if (!this.haveProgress) {
7442 Roo.MessageBox.progress("Uploading", "Uploading");
7444 if (this.uploadComplete) {
7445 Roo.MessageBox.hide();
7449 this.haveProgress = true;
7451 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7453 var c = new Roo.data.Connection();
7455 url : this.form.progressUrl,
7460 success : function(req){
7461 //console.log(data);
7465 rdata = Roo.decode(req.responseText)
7467 Roo.log("Invalid data from server..");
7471 if (!rdata || !rdata.success) {
7473 Roo.MessageBox.alert(Roo.encode(rdata));
7476 var data = rdata.data;
7478 if (this.uploadComplete) {
7479 Roo.MessageBox.hide();
7484 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7485 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7488 this.uploadProgress.defer(2000,this);
7491 failure: function(data) {
7492 Roo.log('progress url failed ');
7503 // run get Values on the form, so it syncs any secondary forms.
7504 this.form.getValues();
7506 var o = this.options;
7507 var method = this.getMethod();
7508 var isPost = method == 'POST';
7509 if(o.clientValidation === false || this.form.isValid()){
7511 if (this.form.progressUrl) {
7512 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7513 (new Date() * 1) + '' + Math.random());
7518 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7519 form:this.form.el.dom,
7520 url:this.getUrl(!isPost),
7522 params:isPost ? this.getParams() : null,
7523 isUpload: this.form.fileUpload
7526 this.uploadProgress();
7528 }else if (o.clientValidation !== false){ // client validation failed
7529 this.failureType = Roo.form.Action.CLIENT_INVALID;
7530 this.form.afterAction(this, false);
7534 success : function(response)
7536 this.uploadComplete= true;
7537 if (this.haveProgress) {
7538 Roo.MessageBox.hide();
7542 var result = this.processResponse(response);
7543 if(result === true || result.success){
7544 this.form.afterAction(this, true);
7548 this.form.markInvalid(result.errors);
7549 this.failureType = Roo.form.Action.SERVER_INVALID;
7551 this.form.afterAction(this, false);
7553 failure : function(response)
7555 this.uploadComplete= true;
7556 if (this.haveProgress) {
7557 Roo.MessageBox.hide();
7560 this.response = response;
7561 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7562 this.form.afterAction(this, false);
7565 handleResponse : function(response){
7566 if(this.form.errorReader){
7567 var rs = this.form.errorReader.read(response);
7570 for(var i = 0, len = rs.records.length; i < len; i++) {
7571 var r = rs.records[i];
7575 if(errors.length < 1){
7579 success : rs.success,
7585 ret = Roo.decode(response.responseText);
7589 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7599 Roo.form.Action.Load = function(form, options){
7600 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7601 this.reader = this.form.reader;
7604 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7609 Roo.Ajax.request(Roo.apply(
7610 this.createCallback(), {
7611 method:this.getMethod(),
7612 url:this.getUrl(false),
7613 params:this.getParams()
7617 success : function(response){
7619 var result = this.processResponse(response);
7620 if(result === true || !result.success || !result.data){
7621 this.failureType = Roo.form.Action.LOAD_FAILURE;
7622 this.form.afterAction(this, false);
7625 this.form.clearInvalid();
7626 this.form.setValues(result.data);
7627 this.form.afterAction(this, true);
7630 handleResponse : function(response){
7631 if(this.form.reader){
7632 var rs = this.form.reader.read(response);
7633 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7635 success : rs.success,
7639 return Roo.decode(response.responseText);
7643 Roo.form.Action.ACTION_TYPES = {
7644 'load' : Roo.form.Action.Load,
7645 'submit' : Roo.form.Action.Submit
7654 * @class Roo.bootstrap.Form
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Form class
7657 * @cfg {String} method GET | POST (default POST)
7658 * @cfg {String} labelAlign top | left (default top)
7659 * @cfg {String} align left | right - for navbars
7660 * @cfg {Boolean} loadMask load mask when submit (default true)
7665 * @param {Object} config The config object
7669 Roo.bootstrap.Form = function(config){
7671 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7673 Roo.bootstrap.Form.popover.apply();
7677 * @event clientvalidation
7678 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7679 * @param {Form} this
7680 * @param {Boolean} valid true if the form has passed client-side validation
7682 clientvalidation: true,
7684 * @event beforeaction
7685 * Fires before any action is performed. Return false to cancel the action.
7686 * @param {Form} this
7687 * @param {Action} action The action to be performed
7691 * @event actionfailed
7692 * Fires when an action fails.
7693 * @param {Form} this
7694 * @param {Action} action The action that failed
7696 actionfailed : true,
7698 * @event actioncomplete
7699 * Fires when an action is completed.
7700 * @param {Form} this
7701 * @param {Action} action The action that completed
7703 actioncomplete : true
7707 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7710 * @cfg {String} method
7711 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7716 * The URL to use for form actions if one isn't supplied in the action options.
7719 * @cfg {Boolean} fileUpload
7720 * Set to true if this form is a file upload.
7724 * @cfg {Object} baseParams
7725 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7729 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7733 * @cfg {Sting} align (left|right) for navbar forms
7738 activeAction : null,
7741 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7742 * element by passing it or its id or mask the form itself by passing in true.
7745 waitMsgTarget : false,
7750 * @cfg {Boolean} errorMask (true|false) default false
7755 * @cfg {Number} maskOffset Default 100
7760 * @cfg {Boolean} maskBody
7764 getAutoCreate : function(){
7768 method : this.method || 'POST',
7769 id : this.id || Roo.id(),
7772 if (this.parent().xtype.match(/^Nav/)) {
7773 cfg.cls = 'navbar-form navbar-' + this.align;
7777 if (this.labelAlign == 'left' ) {
7778 cfg.cls += ' form-horizontal';
7784 initEvents : function()
7786 this.el.on('submit', this.onSubmit, this);
7787 // this was added as random key presses on the form where triggering form submit.
7788 this.el.on('keypress', function(e) {
7789 if (e.getCharCode() != 13) {
7792 // we might need to allow it for textareas.. and some other items.
7793 // check e.getTarget().
7795 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7799 Roo.log("keypress blocked");
7807 onSubmit : function(e){
7812 * Returns true if client-side validation on the form is successful.
7815 isValid : function(){
7816 var items = this.getItems();
7820 items.each(function(f){
7828 if(!target && f.el.isVisible(true)){
7834 if(this.errorMask && !valid){
7835 Roo.bootstrap.Form.popover.mask(this, target);
7842 * Returns true if any fields in this form have changed since their original load.
7845 isDirty : function(){
7847 var items = this.getItems();
7848 items.each(function(f){
7858 * Performs a predefined action (submit or load) or custom actions you define on this form.
7859 * @param {String} actionName The name of the action type
7860 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7861 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7862 * accept other config options):
7864 Property Type Description
7865 ---------------- --------------- ----------------------------------------------------------------------------------
7866 url String The url for the action (defaults to the form's url)
7867 method String The form method to use (defaults to the form's method, or POST if not defined)
7868 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7869 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7870 validate the form on the client (defaults to false)
7872 * @return {BasicForm} this
7874 doAction : function(action, options){
7875 if(typeof action == 'string'){
7876 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7878 if(this.fireEvent('beforeaction', this, action) !== false){
7879 this.beforeAction(action);
7880 action.run.defer(100, action);
7886 beforeAction : function(action){
7887 var o = action.options;
7892 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7894 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7897 // not really supported yet.. ??
7899 //if(this.waitMsgTarget === true){
7900 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7901 //}else if(this.waitMsgTarget){
7902 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7903 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7905 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7911 afterAction : function(action, success){
7912 this.activeAction = null;
7913 var o = action.options;
7918 Roo.get(document.body).unmask();
7924 //if(this.waitMsgTarget === true){
7925 // this.el.unmask();
7926 //}else if(this.waitMsgTarget){
7927 // this.waitMsgTarget.unmask();
7929 // Roo.MessageBox.updateProgress(1);
7930 // Roo.MessageBox.hide();
7937 Roo.callback(o.success, o.scope, [this, action]);
7938 this.fireEvent('actioncomplete', this, action);
7942 // failure condition..
7943 // we have a scenario where updates need confirming.
7944 // eg. if a locking scenario exists..
7945 // we look for { errors : { needs_confirm : true }} in the response.
7947 (typeof(action.result) != 'undefined') &&
7948 (typeof(action.result.errors) != 'undefined') &&
7949 (typeof(action.result.errors.needs_confirm) != 'undefined')
7952 Roo.log("not supported yet");
7955 Roo.MessageBox.confirm(
7956 "Change requires confirmation",
7957 action.result.errorMsg,
7962 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7972 Roo.callback(o.failure, o.scope, [this, action]);
7973 // show an error message if no failed handler is set..
7974 if (!this.hasListener('actionfailed')) {
7975 Roo.log("need to add dialog support");
7977 Roo.MessageBox.alert("Error",
7978 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7979 action.result.errorMsg :
7980 "Saving Failed, please check your entries or try again"
7985 this.fireEvent('actionfailed', this, action);
7990 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7991 * @param {String} id The value to search for
7994 findField : function(id){
7995 var items = this.getItems();
7996 var field = items.get(id);
7998 items.each(function(f){
7999 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8006 return field || null;
8009 * Mark fields in this form invalid in bulk.
8010 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8011 * @return {BasicForm} this
8013 markInvalid : function(errors){
8014 if(errors instanceof Array){
8015 for(var i = 0, len = errors.length; i < len; i++){
8016 var fieldError = errors[i];
8017 var f = this.findField(fieldError.id);
8019 f.markInvalid(fieldError.msg);
8025 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8026 field.markInvalid(errors[id]);
8030 //Roo.each(this.childForms || [], function (f) {
8031 // f.markInvalid(errors);
8038 * Set values for fields in this form in bulk.
8039 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8040 * @return {BasicForm} this
8042 setValues : function(values){
8043 if(values instanceof Array){ // array of objects
8044 for(var i = 0, len = values.length; i < len; i++){
8046 var f = this.findField(v.id);
8048 f.setValue(v.value);
8049 if(this.trackResetOnLoad){
8050 f.originalValue = f.getValue();
8054 }else{ // object hash
8057 if(typeof values[id] != 'function' && (field = this.findField(id))){
8059 if (field.setFromData &&
8061 field.displayField &&
8062 // combos' with local stores can
8063 // be queried via setValue()
8064 // to set their value..
8065 (field.store && !field.store.isLocal)
8069 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8070 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8071 field.setFromData(sd);
8073 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8075 field.setFromData(values);
8078 field.setValue(values[id]);
8082 if(this.trackResetOnLoad){
8083 field.originalValue = field.getValue();
8089 //Roo.each(this.childForms || [], function (f) {
8090 // f.setValues(values);
8097 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8098 * they are returned as an array.
8099 * @param {Boolean} asString
8102 getValues : function(asString){
8103 //if (this.childForms) {
8104 // copy values from the child forms
8105 // Roo.each(this.childForms, function (f) {
8106 // this.setValues(f.getValues());
8112 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8113 if(asString === true){
8116 return Roo.urlDecode(fs);
8120 * Returns the fields in this form as an object with key/value pairs.
8121 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8124 getFieldValues : function(with_hidden)
8126 var items = this.getItems();
8128 items.each(function(f){
8134 var v = f.getValue();
8136 if (f.inputType =='radio') {
8137 if (typeof(ret[f.getName()]) == 'undefined') {
8138 ret[f.getName()] = ''; // empty..
8141 if (!f.el.dom.checked) {
8149 if(f.xtype == 'MoneyField'){
8150 ret[f.currencyName] = f.getCurrency();
8153 // not sure if this supported any more..
8154 if ((typeof(v) == 'object') && f.getRawValue) {
8155 v = f.getRawValue() ; // dates..
8157 // combo boxes where name != hiddenName...
8158 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8159 ret[f.name] = f.getRawValue();
8161 ret[f.getName()] = v;
8168 * Clears all invalid messages in this form.
8169 * @return {BasicForm} this
8171 clearInvalid : function(){
8172 var items = this.getItems();
8174 items.each(function(f){
8183 * @return {BasicForm} this
8186 var items = this.getItems();
8187 items.each(function(f){
8191 Roo.each(this.childForms || [], function (f) {
8199 getItems : function()
8201 var r=new Roo.util.MixedCollection(false, function(o){
8202 return o.id || (o.id = Roo.id());
8204 var iter = function(el) {
8211 Roo.each(el.items,function(e) {
8220 hideFields : function(items)
8222 Roo.each(items, function(i){
8224 var f = this.findField(i);
8230 if(f.xtype == 'DateField'){
8231 f.setVisible(false);
8240 showFields : function(items)
8242 Roo.each(items, function(i){
8244 var f = this.findField(i);
8250 if(f.xtype == 'DateField'){
8262 Roo.apply(Roo.bootstrap.Form, {
8289 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8290 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8291 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8292 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8295 this.maskEl.top.enableDisplayMode("block");
8296 this.maskEl.left.enableDisplayMode("block");
8297 this.maskEl.bottom.enableDisplayMode("block");
8298 this.maskEl.right.enableDisplayMode("block");
8300 this.toolTip = new Roo.bootstrap.Tooltip({
8301 cls : 'roo-form-error-popover',
8303 'left' : ['r-l', [-2,0], 'right'],
8304 'right' : ['l-r', [2,0], 'left'],
8305 'bottom' : ['tl-bl', [0,2], 'top'],
8306 'top' : [ 'bl-tl', [0,-2], 'bottom']
8310 this.toolTip.render(Roo.get(document.body));
8312 this.toolTip.el.enableDisplayMode("block");
8314 Roo.get(document.body).on('click', function(){
8318 Roo.get(document.body).on('touchstart', function(){
8322 this.isApplied = true
8325 mask : function(form, target)
8329 this.target = target;
8331 if(!this.form.errorMask || !target.el){
8335 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8337 Roo.log(scrollable);
8339 var ot = this.target.el.calcOffsetsTo(scrollable);
8341 var scrollTo = ot[1] - this.form.maskOffset;
8343 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8345 scrollable.scrollTo('top', scrollTo);
8347 var box = this.target.el.getBox();
8349 var zIndex = Roo.bootstrap.Modal.zIndex++;
8352 this.maskEl.top.setStyle('position', 'absolute');
8353 this.maskEl.top.setStyle('z-index', zIndex);
8354 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8355 this.maskEl.top.setLeft(0);
8356 this.maskEl.top.setTop(0);
8357 this.maskEl.top.show();
8359 this.maskEl.left.setStyle('position', 'absolute');
8360 this.maskEl.left.setStyle('z-index', zIndex);
8361 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8362 this.maskEl.left.setLeft(0);
8363 this.maskEl.left.setTop(box.y - this.padding);
8364 this.maskEl.left.show();
8366 this.maskEl.bottom.setStyle('position', 'absolute');
8367 this.maskEl.bottom.setStyle('z-index', zIndex);
8368 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8369 this.maskEl.bottom.setLeft(0);
8370 this.maskEl.bottom.setTop(box.bottom + this.padding);
8371 this.maskEl.bottom.show();
8373 this.maskEl.right.setStyle('position', 'absolute');
8374 this.maskEl.right.setStyle('z-index', zIndex);
8375 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8376 this.maskEl.right.setLeft(box.right + this.padding);
8377 this.maskEl.right.setTop(box.y - this.padding);
8378 this.maskEl.right.show();
8380 this.toolTip.bindEl = this.target.el;
8382 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8384 var tip = this.target.blankText;
8386 if(this.target.getValue() !== '' ) {
8388 if (this.target.invalidText.length) {
8389 tip = this.target.invalidText;
8390 } else if (this.target.regexText.length){
8391 tip = this.target.regexText;
8395 this.toolTip.show(tip);
8397 this.intervalID = window.setInterval(function() {
8398 Roo.bootstrap.Form.popover.unmask();
8401 window.onwheel = function(){ return false;};
8403 (function(){ this.isMasked = true; }).defer(500, this);
8409 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8413 this.maskEl.top.setStyle('position', 'absolute');
8414 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8415 this.maskEl.top.hide();
8417 this.maskEl.left.setStyle('position', 'absolute');
8418 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8419 this.maskEl.left.hide();
8421 this.maskEl.bottom.setStyle('position', 'absolute');
8422 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8423 this.maskEl.bottom.hide();
8425 this.maskEl.right.setStyle('position', 'absolute');
8426 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8427 this.maskEl.right.hide();
8429 this.toolTip.hide();
8431 this.toolTip.el.hide();
8433 window.onwheel = function(){ return true;};
8435 if(this.intervalID){
8436 window.clearInterval(this.intervalID);
8437 this.intervalID = false;
8440 this.isMasked = false;
8450 * Ext JS Library 1.1.1
8451 * Copyright(c) 2006-2007, Ext JS, LLC.
8453 * Originally Released Under LGPL - original licence link has changed is not relivant.
8456 * <script type="text/javascript">
8459 * @class Roo.form.VTypes
8460 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8463 Roo.form.VTypes = function(){
8464 // closure these in so they are only created once.
8465 var alpha = /^[a-zA-Z_]+$/;
8466 var alphanum = /^[a-zA-Z0-9_]+$/;
8467 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8468 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8470 // All these messages and functions are configurable
8473 * The function used to validate email addresses
8474 * @param {String} value The email address
8476 'email' : function(v){
8477 return email.test(v);
8480 * The error text to display when the email validation function returns false
8483 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8485 * The keystroke filter mask to be applied on email input
8488 'emailMask' : /[a-z0-9_\.\-@]/i,
8491 * The function used to validate URLs
8492 * @param {String} value The URL
8494 'url' : function(v){
8498 * The error text to display when the url validation function returns false
8501 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8504 * The function used to validate alpha values
8505 * @param {String} value The value
8507 'alpha' : function(v){
8508 return alpha.test(v);
8511 * The error text to display when the alpha validation function returns false
8514 'alphaText' : 'This field should only contain letters and _',
8516 * The keystroke filter mask to be applied on alpha input
8519 'alphaMask' : /[a-z_]/i,
8522 * The function used to validate alphanumeric values
8523 * @param {String} value The value
8525 'alphanum' : function(v){
8526 return alphanum.test(v);
8529 * The error text to display when the alphanumeric validation function returns false
8532 'alphanumText' : 'This field should only contain letters, numbers and _',
8534 * The keystroke filter mask to be applied on alphanumeric input
8537 'alphanumMask' : /[a-z0-9_]/i
8547 * @class Roo.bootstrap.Input
8548 * @extends Roo.bootstrap.Component
8549 * Bootstrap Input class
8550 * @cfg {Boolean} disabled is it disabled
8551 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8552 * @cfg {String} name name of the input
8553 * @cfg {string} fieldLabel - the label associated
8554 * @cfg {string} placeholder - placeholder to put in text.
8555 * @cfg {string} before - input group add on before
8556 * @cfg {string} after - input group add on after
8557 * @cfg {string} size - (lg|sm) or leave empty..
8558 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8559 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8560 * @cfg {Number} md colspan out of 12 for computer-sized screens
8561 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8562 * @cfg {string} value default value of the input
8563 * @cfg {Number} labelWidth set the width of label
8564 * @cfg {Number} labellg set the width of label (1-12)
8565 * @cfg {Number} labelmd set the width of label (1-12)
8566 * @cfg {Number} labelsm set the width of label (1-12)
8567 * @cfg {Number} labelxs set the width of label (1-12)
8568 * @cfg {String} labelAlign (top|left)
8569 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8570 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8571 * @cfg {String} indicatorpos (left|right) default left
8572 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8573 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8575 * @cfg {String} align (left|center|right) Default left
8576 * @cfg {Boolean} forceFeedback (true|false) Default false
8579 * Create a new Input
8580 * @param {Object} config The config object
8583 Roo.bootstrap.Input = function(config){
8585 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8590 * Fires when this field receives input focus.
8591 * @param {Roo.form.Field} this
8596 * Fires when this field loses input focus.
8597 * @param {Roo.form.Field} this
8602 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8603 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8604 * @param {Roo.form.Field} this
8605 * @param {Roo.EventObject} e The event object
8610 * Fires just before the field blurs if the field value has changed.
8611 * @param {Roo.form.Field} this
8612 * @param {Mixed} newValue The new value
8613 * @param {Mixed} oldValue The original value
8618 * Fires after the field has been marked as invalid.
8619 * @param {Roo.form.Field} this
8620 * @param {String} msg The validation message
8625 * Fires after the field has been validated with no errors.
8626 * @param {Roo.form.Field} this
8631 * Fires after the key up
8632 * @param {Roo.form.Field} this
8633 * @param {Roo.EventObject} e The event Object
8639 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8641 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8642 automatic validation (defaults to "keyup").
8644 validationEvent : "keyup",
8646 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8648 validateOnBlur : true,
8650 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8652 validationDelay : 250,
8654 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8656 focusClass : "x-form-focus", // not needed???
8660 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8662 invalidClass : "has-warning",
8665 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8667 validClass : "has-success",
8670 * @cfg {Boolean} hasFeedback (true|false) default true
8675 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8677 invalidFeedbackClass : "glyphicon-warning-sign",
8680 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8682 validFeedbackClass : "glyphicon-ok",
8685 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8687 selectOnFocus : false,
8690 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8694 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8699 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8701 disableKeyFilter : false,
8704 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8708 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8712 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8714 blankText : "Please complete this mandatory field",
8717 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8721 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8723 maxLength : Number.MAX_VALUE,
8725 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8727 minLengthText : "The minimum length for this field is {0}",
8729 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8731 maxLengthText : "The maximum length for this field is {0}",
8735 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8736 * If available, this function will be called only after the basic validators all return true, and will be passed the
8737 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8741 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8742 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8743 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8747 * @cfg {String} regexText -- Depricated - use Invalid Text
8752 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8758 autocomplete: false,
8777 formatedValue : false,
8778 forceFeedback : false,
8780 indicatorpos : 'left',
8790 parentLabelAlign : function()
8793 while (parent.parent()) {
8794 parent = parent.parent();
8795 if (typeof(parent.labelAlign) !='undefined') {
8796 return parent.labelAlign;
8803 getAutoCreate : function()
8805 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8811 if(this.inputType != 'hidden'){
8812 cfg.cls = 'form-group' //input-group
8818 type : this.inputType,
8820 cls : 'form-control',
8821 placeholder : this.placeholder || '',
8822 autocomplete : this.autocomplete || 'new-password'
8825 if(this.capture.length){
8826 input.capture = this.capture;
8829 if(this.accept.length){
8830 input.accept = this.accept + "/*";
8834 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8837 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8838 input.maxLength = this.maxLength;
8841 if (this.disabled) {
8842 input.disabled=true;
8845 if (this.readOnly) {
8846 input.readonly=true;
8850 input.name = this.name;
8854 input.cls += ' input-' + this.size;
8858 ['xs','sm','md','lg'].map(function(size){
8859 if (settings[size]) {
8860 cfg.cls += ' col-' + size + '-' + settings[size];
8864 var inputblock = input;
8868 cls: 'glyphicon form-control-feedback'
8871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8874 cls : 'has-feedback',
8882 if (this.before || this.after) {
8885 cls : 'input-group',
8889 if (this.before && typeof(this.before) == 'string') {
8891 inputblock.cn.push({
8893 cls : 'roo-input-before input-group-addon',
8897 if (this.before && typeof(this.before) == 'object') {
8898 this.before = Roo.factory(this.before);
8900 inputblock.cn.push({
8902 cls : 'roo-input-before input-group-' +
8903 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8907 inputblock.cn.push(input);
8909 if (this.after && typeof(this.after) == 'string') {
8910 inputblock.cn.push({
8912 cls : 'roo-input-after input-group-addon',
8916 if (this.after && typeof(this.after) == 'object') {
8917 this.after = Roo.factory(this.after);
8919 inputblock.cn.push({
8921 cls : 'roo-input-after input-group-' +
8922 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8926 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8927 inputblock.cls += ' has-feedback';
8928 inputblock.cn.push(feedback);
8932 if (align ==='left' && this.fieldLabel.length) {
8934 cfg.cls += ' roo-form-group-label-left';
8939 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8940 tooltip : 'This field is required'
8945 cls : 'control-label',
8946 html : this.fieldLabel
8957 var labelCfg = cfg.cn[1];
8958 var contentCfg = cfg.cn[2];
8960 if(this.indicatorpos == 'right'){
8965 cls : 'control-label',
8969 html : this.fieldLabel
8973 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8974 tooltip : 'This field is required'
8987 labelCfg = cfg.cn[0];
8988 contentCfg = cfg.cn[1];
8992 if(this.labelWidth > 12){
8993 labelCfg.style = "width: " + this.labelWidth + 'px';
8996 if(this.labelWidth < 13 && this.labelmd == 0){
8997 this.labelmd = this.labelWidth;
9000 if(this.labellg > 0){
9001 labelCfg.cls += ' col-lg-' + this.labellg;
9002 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9005 if(this.labelmd > 0){
9006 labelCfg.cls += ' col-md-' + this.labelmd;
9007 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9010 if(this.labelsm > 0){
9011 labelCfg.cls += ' col-sm-' + this.labelsm;
9012 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9015 if(this.labelxs > 0){
9016 labelCfg.cls += ' col-xs-' + this.labelxs;
9017 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9021 } else if ( this.fieldLabel.length) {
9026 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9027 tooltip : 'This field is required'
9031 //cls : 'input-group-addon',
9032 html : this.fieldLabel
9040 if(this.indicatorpos == 'right'){
9045 //cls : 'input-group-addon',
9046 html : this.fieldLabel
9051 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9052 tooltip : 'This field is required'
9072 if (this.parentType === 'Navbar' && this.parent().bar) {
9073 cfg.cls += ' navbar-form';
9076 if (this.parentType === 'NavGroup') {
9077 cfg.cls += ' navbar-form';
9085 * return the real input element.
9087 inputEl: function ()
9089 return this.el.select('input.form-control',true).first();
9092 tooltipEl : function()
9094 return this.inputEl();
9097 indicatorEl : function()
9099 var indicator = this.el.select('i.roo-required-indicator',true).first();
9109 setDisabled : function(v)
9111 var i = this.inputEl().dom;
9113 i.removeAttribute('disabled');
9117 i.setAttribute('disabled','true');
9119 initEvents : function()
9122 this.inputEl().on("keydown" , this.fireKey, this);
9123 this.inputEl().on("focus", this.onFocus, this);
9124 this.inputEl().on("blur", this.onBlur, this);
9126 this.inputEl().relayEvent('keyup', this);
9128 this.indicator = this.indicatorEl();
9131 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9134 // reference to original value for reset
9135 this.originalValue = this.getValue();
9136 //Roo.form.TextField.superclass.initEvents.call(this);
9137 if(this.validationEvent == 'keyup'){
9138 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9139 this.inputEl().on('keyup', this.filterValidation, this);
9141 else if(this.validationEvent !== false){
9142 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9145 if(this.selectOnFocus){
9146 this.on("focus", this.preFocus, this);
9149 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9150 this.inputEl().on("keypress", this.filterKeys, this);
9152 this.inputEl().relayEvent('keypress', this);
9155 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9156 this.el.on("click", this.autoSize, this);
9159 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9160 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9163 if (typeof(this.before) == 'object') {
9164 this.before.render(this.el.select('.roo-input-before',true).first());
9166 if (typeof(this.after) == 'object') {
9167 this.after.render(this.el.select('.roo-input-after',true).first());
9170 this.inputEl().on('change', this.onChange, this);
9173 filterValidation : function(e){
9174 if(!e.isNavKeyPress()){
9175 this.validationTask.delay(this.validationDelay);
9179 * Validates the field value
9180 * @return {Boolean} True if the value is valid, else false
9182 validate : function(){
9183 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9184 if(this.disabled || this.validateValue(this.getRawValue())){
9195 * Validates a value according to the field's validation rules and marks the field as invalid
9196 * if the validation fails
9197 * @param {Mixed} value The value to validate
9198 * @return {Boolean} True if the value is valid, else false
9200 validateValue : function(value)
9202 if(this.getVisibilityEl().hasClass('hidden')){
9206 if(value.length < 1) { // if it's blank
9207 if(this.allowBlank){
9213 if(value.length < this.minLength){
9216 if(value.length > this.maxLength){
9220 var vt = Roo.form.VTypes;
9221 if(!vt[this.vtype](value, this)){
9225 if(typeof this.validator == "function"){
9226 var msg = this.validator(value);
9230 if (typeof(msg) == 'string') {
9231 this.invalidText = msg;
9235 if(this.regex && !this.regex.test(value)){
9243 fireKey : function(e){
9244 //Roo.log('field ' + e.getKey());
9245 if(e.isNavKeyPress()){
9246 this.fireEvent("specialkey", this, e);
9249 focus : function (selectText){
9251 this.inputEl().focus();
9252 if(selectText === true){
9253 this.inputEl().dom.select();
9259 onFocus : function(){
9260 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9261 // this.el.addClass(this.focusClass);
9264 this.hasFocus = true;
9265 this.startValue = this.getValue();
9266 this.fireEvent("focus", this);
9270 beforeBlur : Roo.emptyFn,
9274 onBlur : function(){
9276 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9277 //this.el.removeClass(this.focusClass);
9279 this.hasFocus = false;
9280 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9283 var v = this.getValue();
9284 if(String(v) !== String(this.startValue)){
9285 this.fireEvent('change', this, v, this.startValue);
9287 this.fireEvent("blur", this);
9290 onChange : function(e)
9292 var v = this.getValue();
9293 if(String(v) !== String(this.startValue)){
9294 this.fireEvent('change', this, v, this.startValue);
9300 * Resets the current field value to the originally loaded value and clears any validation messages
9303 this.setValue(this.originalValue);
9307 * Returns the name of the field
9308 * @return {Mixed} name The name field
9310 getName: function(){
9314 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9315 * @return {Mixed} value The field value
9317 getValue : function(){
9319 var v = this.inputEl().getValue();
9324 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9325 * @return {Mixed} value The field value
9327 getRawValue : function(){
9328 var v = this.inputEl().getValue();
9334 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9335 * @param {Mixed} value The value to set
9337 setRawValue : function(v){
9338 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9341 selectText : function(start, end){
9342 var v = this.getRawValue();
9344 start = start === undefined ? 0 : start;
9345 end = end === undefined ? v.length : end;
9346 var d = this.inputEl().dom;
9347 if(d.setSelectionRange){
9348 d.setSelectionRange(start, end);
9349 }else if(d.createTextRange){
9350 var range = d.createTextRange();
9351 range.moveStart("character", start);
9352 range.moveEnd("character", v.length-end);
9359 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9360 * @param {Mixed} value The value to set
9362 setValue : function(v){
9365 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9371 processValue : function(value){
9372 if(this.stripCharsRe){
9373 var newValue = value.replace(this.stripCharsRe, '');
9374 if(newValue !== value){
9375 this.setRawValue(newValue);
9382 preFocus : function(){
9384 if(this.selectOnFocus){
9385 this.inputEl().dom.select();
9388 filterKeys : function(e){
9390 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9393 var c = e.getCharCode(), cc = String.fromCharCode(c);
9394 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9397 if(!this.maskRe.test(cc)){
9402 * Clear any invalid styles/messages for this field
9404 clearInvalid : function(){
9406 if(!this.el || this.preventMark){ // not rendered
9411 this.el.removeClass(this.invalidClass);
9413 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9415 var feedback = this.el.select('.form-control-feedback', true).first();
9418 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9424 this.indicator.removeClass('visible');
9425 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9428 this.fireEvent('valid', this);
9432 * Mark this field as valid
9434 markValid : function()
9436 if(!this.el || this.preventMark){ // not rendered...
9440 this.el.removeClass([this.invalidClass, this.validClass]);
9442 var feedback = this.el.select('.form-control-feedback', true).first();
9445 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9449 this.indicator.removeClass('visible');
9450 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9457 if(this.allowBlank && !this.getRawValue().length){
9461 this.el.addClass(this.validClass);
9463 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9465 var feedback = this.el.select('.form-control-feedback', true).first();
9468 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9469 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9474 this.fireEvent('valid', this);
9478 * Mark this field as invalid
9479 * @param {String} msg The validation message
9481 markInvalid : function(msg)
9483 if(!this.el || this.preventMark){ // not rendered
9487 this.el.removeClass([this.invalidClass, this.validClass]);
9489 var feedback = this.el.select('.form-control-feedback', true).first();
9492 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9499 if(this.allowBlank && !this.getRawValue().length){
9504 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9505 this.indicator.addClass('visible');
9508 this.el.addClass(this.invalidClass);
9510 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9512 var feedback = this.el.select('.form-control-feedback', true).first();
9515 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9517 if(this.getValue().length || this.forceFeedback){
9518 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9525 this.fireEvent('invalid', this, msg);
9528 SafariOnKeyDown : function(event)
9530 // this is a workaround for a password hang bug on chrome/ webkit.
9531 if (this.inputEl().dom.type != 'password') {
9535 var isSelectAll = false;
9537 if(this.inputEl().dom.selectionEnd > 0){
9538 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9540 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9541 event.preventDefault();
9546 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9548 event.preventDefault();
9549 // this is very hacky as keydown always get's upper case.
9551 var cc = String.fromCharCode(event.getCharCode());
9552 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9556 adjustWidth : function(tag, w){
9557 tag = tag.toLowerCase();
9558 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9559 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9563 if(tag == 'textarea'){
9566 }else if(Roo.isOpera){
9570 if(tag == 'textarea'){
9578 setFieldLabel : function(v)
9585 var ar = this.el.select('label > span',true);
9587 if (ar.elements.length) {
9588 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9589 this.fieldLabel = v;
9593 var br = this.el.select('label',true);
9595 if(br.elements.length) {
9596 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9597 this.fieldLabel = v;
9601 Roo.log('Cannot Found any of label > span || label in input');
9605 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9606 this.fieldLabel = v;
9621 * @class Roo.bootstrap.TextArea
9622 * @extends Roo.bootstrap.Input
9623 * Bootstrap TextArea class
9624 * @cfg {Number} cols Specifies the visible width of a text area
9625 * @cfg {Number} rows Specifies the visible number of lines in a text area
9626 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9627 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9628 * @cfg {string} html text
9631 * Create a new TextArea
9632 * @param {Object} config The config object
9635 Roo.bootstrap.TextArea = function(config){
9636 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9640 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9650 getAutoCreate : function(){
9652 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9658 if(this.inputType != 'hidden'){
9659 cfg.cls = 'form-group' //input-group
9667 value : this.value || '',
9668 html: this.html || '',
9669 cls : 'form-control',
9670 placeholder : this.placeholder || ''
9674 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9675 input.maxLength = this.maxLength;
9679 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9683 input.cols = this.cols;
9686 if (this.readOnly) {
9687 input.readonly = true;
9691 input.name = this.name;
9695 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9699 ['xs','sm','md','lg'].map(function(size){
9700 if (settings[size]) {
9701 cfg.cls += ' col-' + size + '-' + settings[size];
9705 var inputblock = input;
9707 if(this.hasFeedback && !this.allowBlank){
9711 cls: 'glyphicon form-control-feedback'
9715 cls : 'has-feedback',
9724 if (this.before || this.after) {
9727 cls : 'input-group',
9731 inputblock.cn.push({
9733 cls : 'input-group-addon',
9738 inputblock.cn.push(input);
9740 if(this.hasFeedback && !this.allowBlank){
9741 inputblock.cls += ' has-feedback';
9742 inputblock.cn.push(feedback);
9746 inputblock.cn.push({
9748 cls : 'input-group-addon',
9755 if (align ==='left' && this.fieldLabel.length) {
9760 cls : 'control-label',
9761 html : this.fieldLabel
9772 if(this.labelWidth > 12){
9773 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9776 if(this.labelWidth < 13 && this.labelmd == 0){
9777 this.labelmd = this.labelWidth;
9780 if(this.labellg > 0){
9781 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9782 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9785 if(this.labelmd > 0){
9786 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9787 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9790 if(this.labelsm > 0){
9791 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9792 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9795 if(this.labelxs > 0){
9796 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9797 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9800 } else if ( this.fieldLabel.length) {
9805 //cls : 'input-group-addon',
9806 html : this.fieldLabel
9824 if (this.disabled) {
9825 input.disabled=true;
9832 * return the real textarea element.
9834 inputEl: function ()
9836 return this.el.select('textarea.form-control',true).first();
9840 * Clear any invalid styles/messages for this field
9842 clearInvalid : function()
9845 if(!this.el || this.preventMark){ // not rendered
9849 var label = this.el.select('label', true).first();
9850 var icon = this.el.select('i.fa-star', true).first();
9856 this.el.removeClass(this.invalidClass);
9858 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9860 var feedback = this.el.select('.form-control-feedback', true).first();
9863 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9868 this.fireEvent('valid', this);
9872 * Mark this field as valid
9874 markValid : function()
9876 if(!this.el || this.preventMark){ // not rendered
9880 this.el.removeClass([this.invalidClass, this.validClass]);
9882 var feedback = this.el.select('.form-control-feedback', true).first();
9885 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9888 if(this.disabled || this.allowBlank){
9892 var label = this.el.select('label', true).first();
9893 var icon = this.el.select('i.fa-star', true).first();
9899 this.el.addClass(this.validClass);
9901 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9903 var feedback = this.el.select('.form-control-feedback', true).first();
9906 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9912 this.fireEvent('valid', this);
9916 * Mark this field as invalid
9917 * @param {String} msg The validation message
9919 markInvalid : function(msg)
9921 if(!this.el || this.preventMark){ // not rendered
9925 this.el.removeClass([this.invalidClass, this.validClass]);
9927 var feedback = this.el.select('.form-control-feedback', true).first();
9930 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9933 if(this.disabled || this.allowBlank){
9937 var label = this.el.select('label', true).first();
9938 var icon = this.el.select('i.fa-star', true).first();
9940 if(!this.getValue().length && label && !icon){
9941 this.el.createChild({
9943 cls : 'text-danger fa fa-lg fa-star',
9944 tooltip : 'This field is required',
9945 style : 'margin-right:5px;'
9949 this.el.addClass(this.invalidClass);
9951 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9953 var feedback = this.el.select('.form-control-feedback', true).first();
9956 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9958 if(this.getValue().length || this.forceFeedback){
9959 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9966 this.fireEvent('invalid', this, msg);
9974 * trigger field - base class for combo..
9979 * @class Roo.bootstrap.TriggerField
9980 * @extends Roo.bootstrap.Input
9981 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9982 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9983 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9984 * for which you can provide a custom implementation. For example:
9986 var trigger = new Roo.bootstrap.TriggerField();
9987 trigger.onTriggerClick = myTriggerFn;
9988 trigger.applyTo('my-field');
9991 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9992 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9993 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9994 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9995 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9998 * Create a new TriggerField.
9999 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10000 * to the base TextField)
10002 Roo.bootstrap.TriggerField = function(config){
10003 this.mimicing = false;
10004 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10007 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10009 * @cfg {String} triggerClass A CSS class to apply to the trigger
10012 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10017 * @cfg {Boolean} removable (true|false) special filter default false
10021 /** @cfg {Boolean} grow @hide */
10022 /** @cfg {Number} growMin @hide */
10023 /** @cfg {Number} growMax @hide */
10029 autoSize: Roo.emptyFn,
10033 deferHeight : true,
10036 actionMode : 'wrap',
10041 getAutoCreate : function(){
10043 var align = this.labelAlign || this.parentLabelAlign();
10048 cls: 'form-group' //input-group
10055 type : this.inputType,
10056 cls : 'form-control',
10057 autocomplete: 'new-password',
10058 placeholder : this.placeholder || ''
10062 input.name = this.name;
10065 input.cls += ' input-' + this.size;
10068 if (this.disabled) {
10069 input.disabled=true;
10072 var inputblock = input;
10074 if(this.hasFeedback && !this.allowBlank){
10078 cls: 'glyphicon form-control-feedback'
10081 if(this.removable && !this.editable && !this.tickable){
10083 cls : 'has-feedback',
10089 cls : 'roo-combo-removable-btn close'
10096 cls : 'has-feedback',
10105 if(this.removable && !this.editable && !this.tickable){
10107 cls : 'roo-removable',
10113 cls : 'roo-combo-removable-btn close'
10120 if (this.before || this.after) {
10123 cls : 'input-group',
10127 inputblock.cn.push({
10129 cls : 'input-group-addon',
10134 inputblock.cn.push(input);
10136 if(this.hasFeedback && !this.allowBlank){
10137 inputblock.cls += ' has-feedback';
10138 inputblock.cn.push(feedback);
10142 inputblock.cn.push({
10144 cls : 'input-group-addon',
10157 cls: 'form-hidden-field'
10171 cls: 'form-hidden-field'
10175 cls: 'roo-select2-choices',
10179 cls: 'roo-select2-search-field',
10192 cls: 'roo-select2-container input-group',
10197 // cls: 'typeahead typeahead-long dropdown-menu',
10198 // style: 'display:none'
10203 if(!this.multiple && this.showToggleBtn){
10209 if (this.caret != false) {
10212 cls: 'fa fa-' + this.caret
10219 cls : 'input-group-addon btn dropdown-toggle',
10224 cls: 'combobox-clear',
10238 combobox.cls += ' roo-select2-container-multi';
10241 if (align ==='left' && this.fieldLabel.length) {
10243 cfg.cls += ' roo-form-group-label-left';
10248 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10249 tooltip : 'This field is required'
10254 cls : 'control-label',
10255 html : this.fieldLabel
10267 var labelCfg = cfg.cn[1];
10268 var contentCfg = cfg.cn[2];
10270 if(this.indicatorpos == 'right'){
10275 cls : 'control-label',
10279 html : this.fieldLabel
10283 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10284 tooltip : 'This field is required'
10297 labelCfg = cfg.cn[0];
10298 contentCfg = cfg.cn[1];
10301 if(this.labelWidth > 12){
10302 labelCfg.style = "width: " + this.labelWidth + 'px';
10305 if(this.labelWidth < 13 && this.labelmd == 0){
10306 this.labelmd = this.labelWidth;
10309 if(this.labellg > 0){
10310 labelCfg.cls += ' col-lg-' + this.labellg;
10311 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10314 if(this.labelmd > 0){
10315 labelCfg.cls += ' col-md-' + this.labelmd;
10316 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10319 if(this.labelsm > 0){
10320 labelCfg.cls += ' col-sm-' + this.labelsm;
10321 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10324 if(this.labelxs > 0){
10325 labelCfg.cls += ' col-xs-' + this.labelxs;
10326 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10329 } else if ( this.fieldLabel.length) {
10330 // Roo.log(" label");
10334 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10335 tooltip : 'This field is required'
10339 //cls : 'input-group-addon',
10340 html : this.fieldLabel
10348 if(this.indicatorpos == 'right'){
10356 html : this.fieldLabel
10360 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10361 tooltip : 'This field is required'
10374 // Roo.log(" no label && no align");
10381 ['xs','sm','md','lg'].map(function(size){
10382 if (settings[size]) {
10383 cfg.cls += ' col-' + size + '-' + settings[size];
10394 onResize : function(w, h){
10395 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10396 // if(typeof w == 'number'){
10397 // var x = w - this.trigger.getWidth();
10398 // this.inputEl().setWidth(this.adjustWidth('input', x));
10399 // this.trigger.setStyle('left', x+'px');
10404 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10407 getResizeEl : function(){
10408 return this.inputEl();
10412 getPositionEl : function(){
10413 return this.inputEl();
10417 alignErrorIcon : function(){
10418 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10422 initEvents : function(){
10426 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10427 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10428 if(!this.multiple && this.showToggleBtn){
10429 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10430 if(this.hideTrigger){
10431 this.trigger.setDisplayed(false);
10433 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10437 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10440 if(this.removable && !this.editable && !this.tickable){
10441 var close = this.closeTriggerEl();
10444 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10445 close.on('click', this.removeBtnClick, this, close);
10449 //this.trigger.addClassOnOver('x-form-trigger-over');
10450 //this.trigger.addClassOnClick('x-form-trigger-click');
10453 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10457 closeTriggerEl : function()
10459 var close = this.el.select('.roo-combo-removable-btn', true).first();
10460 return close ? close : false;
10463 removeBtnClick : function(e, h, el)
10465 e.preventDefault();
10467 if(this.fireEvent("remove", this) !== false){
10469 this.fireEvent("afterremove", this)
10473 createList : function()
10475 this.list = Roo.get(document.body).createChild({
10477 cls: 'typeahead typeahead-long dropdown-menu',
10478 style: 'display:none'
10481 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10486 initTrigger : function(){
10491 onDestroy : function(){
10493 this.trigger.removeAllListeners();
10494 // this.trigger.remove();
10497 // this.wrap.remove();
10499 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10503 onFocus : function(){
10504 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10506 if(!this.mimicing){
10507 this.wrap.addClass('x-trigger-wrap-focus');
10508 this.mimicing = true;
10509 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10510 if(this.monitorTab){
10511 this.el.on("keydown", this.checkTab, this);
10518 checkTab : function(e){
10519 if(e.getKey() == e.TAB){
10520 this.triggerBlur();
10525 onBlur : function(){
10530 mimicBlur : function(e, t){
10532 if(!this.wrap.contains(t) && this.validateBlur()){
10533 this.triggerBlur();
10539 triggerBlur : function(){
10540 this.mimicing = false;
10541 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10542 if(this.monitorTab){
10543 this.el.un("keydown", this.checkTab, this);
10545 //this.wrap.removeClass('x-trigger-wrap-focus');
10546 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10550 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10551 validateBlur : function(e, t){
10556 onDisable : function(){
10557 this.inputEl().dom.disabled = true;
10558 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10560 // this.wrap.addClass('x-item-disabled');
10565 onEnable : function(){
10566 this.inputEl().dom.disabled = false;
10567 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10569 // this.el.removeClass('x-item-disabled');
10574 onShow : function(){
10575 var ae = this.getActionEl();
10578 ae.dom.style.display = '';
10579 ae.dom.style.visibility = 'visible';
10585 onHide : function(){
10586 var ae = this.getActionEl();
10587 ae.dom.style.display = 'none';
10591 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10592 * by an implementing function.
10594 * @param {EventObject} e
10596 onTriggerClick : Roo.emptyFn
10600 * Ext JS Library 1.1.1
10601 * Copyright(c) 2006-2007, Ext JS, LLC.
10603 * Originally Released Under LGPL - original licence link has changed is not relivant.
10606 * <script type="text/javascript">
10611 * @class Roo.data.SortTypes
10613 * Defines the default sorting (casting?) comparison functions used when sorting data.
10615 Roo.data.SortTypes = {
10617 * Default sort that does nothing
10618 * @param {Mixed} s The value being converted
10619 * @return {Mixed} The comparison value
10621 none : function(s){
10626 * The regular expression used to strip tags
10630 stripTagsRE : /<\/?[^>]+>/gi,
10633 * Strips all HTML tags to sort on text only
10634 * @param {Mixed} s The value being converted
10635 * @return {String} The comparison value
10637 asText : function(s){
10638 return String(s).replace(this.stripTagsRE, "");
10642 * Strips all HTML tags to sort on text only - Case insensitive
10643 * @param {Mixed} s The value being converted
10644 * @return {String} The comparison value
10646 asUCText : function(s){
10647 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10651 * Case insensitive string
10652 * @param {Mixed} s The value being converted
10653 * @return {String} The comparison value
10655 asUCString : function(s) {
10656 return String(s).toUpperCase();
10661 * @param {Mixed} s The value being converted
10662 * @return {Number} The comparison value
10664 asDate : function(s) {
10668 if(s instanceof Date){
10669 return s.getTime();
10671 return Date.parse(String(s));
10676 * @param {Mixed} s The value being converted
10677 * @return {Float} The comparison value
10679 asFloat : function(s) {
10680 var val = parseFloat(String(s).replace(/,/g, ""));
10689 * @param {Mixed} s The value being converted
10690 * @return {Number} The comparison value
10692 asInt : function(s) {
10693 var val = parseInt(String(s).replace(/,/g, ""));
10701 * Ext JS Library 1.1.1
10702 * Copyright(c) 2006-2007, Ext JS, LLC.
10704 * Originally Released Under LGPL - original licence link has changed is not relivant.
10707 * <script type="text/javascript">
10711 * @class Roo.data.Record
10712 * Instances of this class encapsulate both record <em>definition</em> information, and record
10713 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10714 * to access Records cached in an {@link Roo.data.Store} object.<br>
10716 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10717 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10720 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10722 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10723 * {@link #create}. The parameters are the same.
10724 * @param {Array} data An associative Array of data values keyed by the field name.
10725 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10726 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10727 * not specified an integer id is generated.
10729 Roo.data.Record = function(data, id){
10730 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10735 * Generate a constructor for a specific record layout.
10736 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10737 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10738 * Each field definition object may contain the following properties: <ul>
10739 * <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,
10740 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10741 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10742 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10743 * is being used, then this is a string containing the javascript expression to reference the data relative to
10744 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10745 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10746 * this may be omitted.</p></li>
10747 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10748 * <ul><li>auto (Default, implies no conversion)</li>
10753 * <li>date</li></ul></p></li>
10754 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10755 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10756 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10757 * by the Reader into an object that will be stored in the Record. It is passed the
10758 * following parameters:<ul>
10759 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10761 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10763 * <br>usage:<br><pre><code>
10764 var TopicRecord = Roo.data.Record.create(
10765 {name: 'title', mapping: 'topic_title'},
10766 {name: 'author', mapping: 'username'},
10767 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10768 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10769 {name: 'lastPoster', mapping: 'user2'},
10770 {name: 'excerpt', mapping: 'post_text'}
10773 var myNewRecord = new TopicRecord({
10774 title: 'Do my job please',
10777 lastPost: new Date(),
10778 lastPoster: 'Animal',
10779 excerpt: 'No way dude!'
10781 myStore.add(myNewRecord);
10786 Roo.data.Record.create = function(o){
10787 var f = function(){
10788 f.superclass.constructor.apply(this, arguments);
10790 Roo.extend(f, Roo.data.Record);
10791 var p = f.prototype;
10792 p.fields = new Roo.util.MixedCollection(false, function(field){
10795 for(var i = 0, len = o.length; i < len; i++){
10796 p.fields.add(new Roo.data.Field(o[i]));
10798 f.getField = function(name){
10799 return p.fields.get(name);
10804 Roo.data.Record.AUTO_ID = 1000;
10805 Roo.data.Record.EDIT = 'edit';
10806 Roo.data.Record.REJECT = 'reject';
10807 Roo.data.Record.COMMIT = 'commit';
10809 Roo.data.Record.prototype = {
10811 * Readonly flag - true if this record has been modified.
10820 join : function(store){
10821 this.store = store;
10825 * Set the named field to the specified value.
10826 * @param {String} name The name of the field to set.
10827 * @param {Object} value The value to set the field to.
10829 set : function(name, value){
10830 if(this.data[name] == value){
10834 if(!this.modified){
10835 this.modified = {};
10837 if(typeof this.modified[name] == 'undefined'){
10838 this.modified[name] = this.data[name];
10840 this.data[name] = value;
10841 if(!this.editing && this.store){
10842 this.store.afterEdit(this);
10847 * Get the value of the named field.
10848 * @param {String} name The name of the field to get the value of.
10849 * @return {Object} The value of the field.
10851 get : function(name){
10852 return this.data[name];
10856 beginEdit : function(){
10857 this.editing = true;
10858 this.modified = {};
10862 cancelEdit : function(){
10863 this.editing = false;
10864 delete this.modified;
10868 endEdit : function(){
10869 this.editing = false;
10870 if(this.dirty && this.store){
10871 this.store.afterEdit(this);
10876 * Usually called by the {@link Roo.data.Store} which owns the Record.
10877 * Rejects all changes made to the Record since either creation, or the last commit operation.
10878 * Modified fields are reverted to their original values.
10880 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10881 * of reject operations.
10883 reject : function(){
10884 var m = this.modified;
10886 if(typeof m[n] != "function"){
10887 this.data[n] = m[n];
10890 this.dirty = false;
10891 delete this.modified;
10892 this.editing = false;
10894 this.store.afterReject(this);
10899 * Usually called by the {@link Roo.data.Store} which owns the Record.
10900 * Commits all changes made to the Record since either creation, or the last commit operation.
10902 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10903 * of commit operations.
10905 commit : function(){
10906 this.dirty = false;
10907 delete this.modified;
10908 this.editing = false;
10910 this.store.afterCommit(this);
10915 hasError : function(){
10916 return this.error != null;
10920 clearError : function(){
10925 * Creates a copy of this record.
10926 * @param {String} id (optional) A new record id if you don't want to use this record's id
10929 copy : function(newId) {
10930 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10934 * Ext JS Library 1.1.1
10935 * Copyright(c) 2006-2007, Ext JS, LLC.
10937 * Originally Released Under LGPL - original licence link has changed is not relivant.
10940 * <script type="text/javascript">
10946 * @class Roo.data.Store
10947 * @extends Roo.util.Observable
10948 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10949 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10951 * 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
10952 * has no knowledge of the format of the data returned by the Proxy.<br>
10954 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10955 * instances from the data object. These records are cached and made available through accessor functions.
10957 * Creates a new Store.
10958 * @param {Object} config A config object containing the objects needed for the Store to access data,
10959 * and read the data into Records.
10961 Roo.data.Store = function(config){
10962 this.data = new Roo.util.MixedCollection(false);
10963 this.data.getKey = function(o){
10966 this.baseParams = {};
10968 this.paramNames = {
10973 "multisort" : "_multisort"
10976 if(config && config.data){
10977 this.inlineData = config.data;
10978 delete config.data;
10981 Roo.apply(this, config);
10983 if(this.reader){ // reader passed
10984 this.reader = Roo.factory(this.reader, Roo.data);
10985 this.reader.xmodule = this.xmodule || false;
10986 if(!this.recordType){
10987 this.recordType = this.reader.recordType;
10989 if(this.reader.onMetaChange){
10990 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10994 if(this.recordType){
10995 this.fields = this.recordType.prototype.fields;
10997 this.modified = [];
11001 * @event datachanged
11002 * Fires when the data cache has changed, and a widget which is using this Store
11003 * as a Record cache should refresh its view.
11004 * @param {Store} this
11006 datachanged : true,
11008 * @event metachange
11009 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11010 * @param {Store} this
11011 * @param {Object} meta The JSON metadata
11016 * Fires when Records have been added to the Store
11017 * @param {Store} this
11018 * @param {Roo.data.Record[]} records The array of Records added
11019 * @param {Number} index The index at which the record(s) were added
11024 * Fires when a Record has been removed from the Store
11025 * @param {Store} this
11026 * @param {Roo.data.Record} record The Record that was removed
11027 * @param {Number} index The index at which the record was removed
11032 * Fires when a Record has been updated
11033 * @param {Store} this
11034 * @param {Roo.data.Record} record The Record that was updated
11035 * @param {String} operation The update operation being performed. Value may be one of:
11037 Roo.data.Record.EDIT
11038 Roo.data.Record.REJECT
11039 Roo.data.Record.COMMIT
11045 * Fires when the data cache has been cleared.
11046 * @param {Store} this
11050 * @event beforeload
11051 * Fires before a request is made for a new data object. If the beforeload handler returns false
11052 * the load action will be canceled.
11053 * @param {Store} this
11054 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11058 * @event beforeloadadd
11059 * Fires after a new set of Records has been loaded.
11060 * @param {Store} this
11061 * @param {Roo.data.Record[]} records The Records that were loaded
11062 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11064 beforeloadadd : true,
11067 * Fires after a new set of Records has been loaded, before they are added to the store.
11068 * @param {Store} this
11069 * @param {Roo.data.Record[]} records The Records that were loaded
11070 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11071 * @params {Object} return from reader
11075 * @event loadexception
11076 * Fires if an exception occurs in the Proxy during loading.
11077 * Called with the signature of the Proxy's "loadexception" event.
11078 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11081 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11082 * @param {Object} load options
11083 * @param {Object} jsonData from your request (normally this contains the Exception)
11085 loadexception : true
11089 this.proxy = Roo.factory(this.proxy, Roo.data);
11090 this.proxy.xmodule = this.xmodule || false;
11091 this.relayEvents(this.proxy, ["loadexception"]);
11093 this.sortToggle = {};
11094 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11096 Roo.data.Store.superclass.constructor.call(this);
11098 if(this.inlineData){
11099 this.loadData(this.inlineData);
11100 delete this.inlineData;
11104 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11106 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11107 * without a remote query - used by combo/forms at present.
11111 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11114 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11117 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11118 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11121 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11122 * on any HTTP request
11125 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11128 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11132 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11133 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11135 remoteSort : false,
11138 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11139 * loaded or when a record is removed. (defaults to false).
11141 pruneModifiedRecords : false,
11144 lastOptions : null,
11147 * Add Records to the Store and fires the add event.
11148 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11150 add : function(records){
11151 records = [].concat(records);
11152 for(var i = 0, len = records.length; i < len; i++){
11153 records[i].join(this);
11155 var index = this.data.length;
11156 this.data.addAll(records);
11157 this.fireEvent("add", this, records, index);
11161 * Remove a Record from the Store and fires the remove event.
11162 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11164 remove : function(record){
11165 var index = this.data.indexOf(record);
11166 this.data.removeAt(index);
11168 if(this.pruneModifiedRecords){
11169 this.modified.remove(record);
11171 this.fireEvent("remove", this, record, index);
11175 * Remove all Records from the Store and fires the clear event.
11177 removeAll : function(){
11179 if(this.pruneModifiedRecords){
11180 this.modified = [];
11182 this.fireEvent("clear", this);
11186 * Inserts Records to the Store at the given index and fires the add event.
11187 * @param {Number} index The start index at which to insert the passed Records.
11188 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11190 insert : function(index, records){
11191 records = [].concat(records);
11192 for(var i = 0, len = records.length; i < len; i++){
11193 this.data.insert(index, records[i]);
11194 records[i].join(this);
11196 this.fireEvent("add", this, records, index);
11200 * Get the index within the cache of the passed Record.
11201 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11202 * @return {Number} The index of the passed Record. Returns -1 if not found.
11204 indexOf : function(record){
11205 return this.data.indexOf(record);
11209 * Get the index within the cache of the Record with the passed id.
11210 * @param {String} id The id of the Record to find.
11211 * @return {Number} The index of the Record. Returns -1 if not found.
11213 indexOfId : function(id){
11214 return this.data.indexOfKey(id);
11218 * Get the Record with the specified id.
11219 * @param {String} id The id of the Record to find.
11220 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11222 getById : function(id){
11223 return this.data.key(id);
11227 * Get the Record at the specified index.
11228 * @param {Number} index The index of the Record to find.
11229 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11231 getAt : function(index){
11232 return this.data.itemAt(index);
11236 * Returns a range of Records between specified indices.
11237 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11238 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11239 * @return {Roo.data.Record[]} An array of Records
11241 getRange : function(start, end){
11242 return this.data.getRange(start, end);
11246 storeOptions : function(o){
11247 o = Roo.apply({}, o);
11250 this.lastOptions = o;
11254 * Loads the Record cache from the configured Proxy using the configured Reader.
11256 * If using remote paging, then the first load call must specify the <em>start</em>
11257 * and <em>limit</em> properties in the options.params property to establish the initial
11258 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11260 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11261 * and this call will return before the new data has been loaded. Perform any post-processing
11262 * in a callback function, or in a "load" event handler.</strong>
11264 * @param {Object} options An object containing properties which control loading options:<ul>
11265 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11266 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11267 * passed the following arguments:<ul>
11268 * <li>r : Roo.data.Record[]</li>
11269 * <li>options: Options object from the load call</li>
11270 * <li>success: Boolean success indicator</li></ul></li>
11271 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11272 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11275 load : function(options){
11276 options = options || {};
11277 if(this.fireEvent("beforeload", this, options) !== false){
11278 this.storeOptions(options);
11279 var p = Roo.apply(options.params || {}, this.baseParams);
11280 // if meta was not loaded from remote source.. try requesting it.
11281 if (!this.reader.metaFromRemote) {
11282 p._requestMeta = 1;
11284 if(this.sortInfo && this.remoteSort){
11285 var pn = this.paramNames;
11286 p[pn["sort"]] = this.sortInfo.field;
11287 p[pn["dir"]] = this.sortInfo.direction;
11289 if (this.multiSort) {
11290 var pn = this.paramNames;
11291 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11294 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11299 * Reloads the Record cache from the configured Proxy using the configured Reader and
11300 * the options from the last load operation performed.
11301 * @param {Object} options (optional) An object containing properties which may override the options
11302 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11303 * the most recently used options are reused).
11305 reload : function(options){
11306 this.load(Roo.applyIf(options||{}, this.lastOptions));
11310 // Called as a callback by the Reader during a load operation.
11311 loadRecords : function(o, options, success){
11312 if(!o || success === false){
11313 if(success !== false){
11314 this.fireEvent("load", this, [], options, o);
11316 if(options.callback){
11317 options.callback.call(options.scope || this, [], options, false);
11321 // if data returned failure - throw an exception.
11322 if (o.success === false) {
11323 // show a message if no listener is registered.
11324 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11325 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11327 // loadmask wil be hooked into this..
11328 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11331 var r = o.records, t = o.totalRecords || r.length;
11333 this.fireEvent("beforeloadadd", this, r, options, o);
11335 if(!options || options.add !== true){
11336 if(this.pruneModifiedRecords){
11337 this.modified = [];
11339 for(var i = 0, len = r.length; i < len; i++){
11343 this.data = this.snapshot;
11344 delete this.snapshot;
11347 this.data.addAll(r);
11348 this.totalLength = t;
11350 this.fireEvent("datachanged", this);
11352 this.totalLength = Math.max(t, this.data.length+r.length);
11356 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11358 var e = new Roo.data.Record({});
11360 e.set(this.parent.displayField, this.parent.emptyTitle);
11361 e.set(this.parent.valueField, '');
11366 this.fireEvent("load", this, r, options, o);
11367 if(options.callback){
11368 options.callback.call(options.scope || this, r, options, true);
11374 * Loads data from a passed data block. A Reader which understands the format of the data
11375 * must have been configured in the constructor.
11376 * @param {Object} data The data block from which to read the Records. The format of the data expected
11377 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11378 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11380 loadData : function(o, append){
11381 var r = this.reader.readRecords(o);
11382 this.loadRecords(r, {add: append}, true);
11386 * Gets the number of cached records.
11388 * <em>If using paging, this may not be the total size of the dataset. If the data object
11389 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11390 * the data set size</em>
11392 getCount : function(){
11393 return this.data.length || 0;
11397 * Gets the total number of records in the dataset as returned by the server.
11399 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11400 * the dataset size</em>
11402 getTotalCount : function(){
11403 return this.totalLength || 0;
11407 * Returns the sort state of the Store as an object with two properties:
11409 field {String} The name of the field by which the Records are sorted
11410 direction {String} The sort order, "ASC" or "DESC"
11413 getSortState : function(){
11414 return this.sortInfo;
11418 applySort : function(){
11419 if(this.sortInfo && !this.remoteSort){
11420 var s = this.sortInfo, f = s.field;
11421 var st = this.fields.get(f).sortType;
11422 var fn = function(r1, r2){
11423 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11424 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11426 this.data.sort(s.direction, fn);
11427 if(this.snapshot && this.snapshot != this.data){
11428 this.snapshot.sort(s.direction, fn);
11434 * Sets the default sort column and order to be used by the next load operation.
11435 * @param {String} fieldName The name of the field to sort by.
11436 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11438 setDefaultSort : function(field, dir){
11439 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11443 * Sort the Records.
11444 * If remote sorting is used, the sort is performed on the server, and the cache is
11445 * reloaded. If local sorting is used, the cache is sorted internally.
11446 * @param {String} fieldName The name of the field to sort by.
11447 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11449 sort : function(fieldName, dir){
11450 var f = this.fields.get(fieldName);
11452 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11454 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11455 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11460 this.sortToggle[f.name] = dir;
11461 this.sortInfo = {field: f.name, direction: dir};
11462 if(!this.remoteSort){
11464 this.fireEvent("datachanged", this);
11466 this.load(this.lastOptions);
11471 * Calls the specified function for each of the Records in the cache.
11472 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11473 * Returning <em>false</em> aborts and exits the iteration.
11474 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11476 each : function(fn, scope){
11477 this.data.each(fn, scope);
11481 * Gets all records modified since the last commit. Modified records are persisted across load operations
11482 * (e.g., during paging).
11483 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11485 getModifiedRecords : function(){
11486 return this.modified;
11490 createFilterFn : function(property, value, anyMatch){
11491 if(!value.exec){ // not a regex
11492 value = String(value);
11493 if(value.length == 0){
11496 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11498 return function(r){
11499 return value.test(r.data[property]);
11504 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11505 * @param {String} property A field on your records
11506 * @param {Number} start The record index to start at (defaults to 0)
11507 * @param {Number} end The last record index to include (defaults to length - 1)
11508 * @return {Number} The sum
11510 sum : function(property, start, end){
11511 var rs = this.data.items, v = 0;
11512 start = start || 0;
11513 end = (end || end === 0) ? end : rs.length-1;
11515 for(var i = start; i <= end; i++){
11516 v += (rs[i].data[property] || 0);
11522 * Filter the records by a specified property.
11523 * @param {String} field A field on your records
11524 * @param {String/RegExp} value Either a string that the field
11525 * should start with or a RegExp to test against the field
11526 * @param {Boolean} anyMatch True to match any part not just the beginning
11528 filter : function(property, value, anyMatch){
11529 var fn = this.createFilterFn(property, value, anyMatch);
11530 return fn ? this.filterBy(fn) : this.clearFilter();
11534 * Filter by a function. The specified function will be called with each
11535 * record in this data source. If the function returns true the record is included,
11536 * otherwise it is filtered.
11537 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11538 * @param {Object} scope (optional) The scope of the function (defaults to this)
11540 filterBy : function(fn, scope){
11541 this.snapshot = this.snapshot || this.data;
11542 this.data = this.queryBy(fn, scope||this);
11543 this.fireEvent("datachanged", this);
11547 * Query 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
11552 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11554 query : function(property, value, anyMatch){
11555 var fn = this.createFilterFn(property, value, anyMatch);
11556 return fn ? this.queryBy(fn) : this.data.clone();
11560 * Query by a function. The specified function will be called with each
11561 * record in this data source. If the function returns true the record is included
11563 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11564 * @param {Object} scope (optional) The scope of the function (defaults to this)
11565 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11567 queryBy : function(fn, scope){
11568 var data = this.snapshot || this.data;
11569 return data.filterBy(fn, scope||this);
11573 * Collects unique values for a particular dataIndex from this store.
11574 * @param {String} dataIndex The property to collect
11575 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11576 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11577 * @return {Array} An array of the unique values
11579 collect : function(dataIndex, allowNull, bypassFilter){
11580 var d = (bypassFilter === true && this.snapshot) ?
11581 this.snapshot.items : this.data.items;
11582 var v, sv, r = [], l = {};
11583 for(var i = 0, len = d.length; i < len; i++){
11584 v = d[i].data[dataIndex];
11586 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11595 * Revert to a view of the Record cache with no filtering applied.
11596 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11598 clearFilter : function(suppressEvent){
11599 if(this.snapshot && this.snapshot != this.data){
11600 this.data = this.snapshot;
11601 delete this.snapshot;
11602 if(suppressEvent !== true){
11603 this.fireEvent("datachanged", this);
11609 afterEdit : function(record){
11610 if(this.modified.indexOf(record) == -1){
11611 this.modified.push(record);
11613 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11617 afterReject : function(record){
11618 this.modified.remove(record);
11619 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11623 afterCommit : function(record){
11624 this.modified.remove(record);
11625 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11629 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11630 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11632 commitChanges : function(){
11633 var m = this.modified.slice(0);
11634 this.modified = [];
11635 for(var i = 0, len = m.length; i < len; i++){
11641 * Cancel outstanding changes on all changed records.
11643 rejectChanges : function(){
11644 var m = this.modified.slice(0);
11645 this.modified = [];
11646 for(var i = 0, len = m.length; i < len; i++){
11651 onMetaChange : function(meta, rtype, o){
11652 this.recordType = rtype;
11653 this.fields = rtype.prototype.fields;
11654 delete this.snapshot;
11655 this.sortInfo = meta.sortInfo || this.sortInfo;
11656 this.modified = [];
11657 this.fireEvent('metachange', this, this.reader.meta);
11660 moveIndex : function(data, type)
11662 var index = this.indexOf(data);
11664 var newIndex = index + type;
11668 this.insert(newIndex, data);
11673 * Ext JS Library 1.1.1
11674 * Copyright(c) 2006-2007, Ext JS, LLC.
11676 * Originally Released Under LGPL - original licence link has changed is not relivant.
11679 * <script type="text/javascript">
11683 * @class Roo.data.SimpleStore
11684 * @extends Roo.data.Store
11685 * Small helper class to make creating Stores from Array data easier.
11686 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11687 * @cfg {Array} fields An array of field definition objects, or field name strings.
11688 * @cfg {Array} data The multi-dimensional array of data
11690 * @param {Object} config
11692 Roo.data.SimpleStore = function(config){
11693 Roo.data.SimpleStore.superclass.constructor.call(this, {
11695 reader: new Roo.data.ArrayReader({
11698 Roo.data.Record.create(config.fields)
11700 proxy : new Roo.data.MemoryProxy(config.data)
11704 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11706 * Ext JS Library 1.1.1
11707 * Copyright(c) 2006-2007, Ext JS, LLC.
11709 * Originally Released Under LGPL - original licence link has changed is not relivant.
11712 * <script type="text/javascript">
11717 * @extends Roo.data.Store
11718 * @class Roo.data.JsonStore
11719 * Small helper class to make creating Stores for JSON data easier. <br/>
11721 var store = new Roo.data.JsonStore({
11722 url: 'get-images.php',
11724 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11727 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11728 * JsonReader and HttpProxy (unless inline data is provided).</b>
11729 * @cfg {Array} fields An array of field definition objects, or field name strings.
11731 * @param {Object} config
11733 Roo.data.JsonStore = function(c){
11734 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11735 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11736 reader: new Roo.data.JsonReader(c, c.fields)
11739 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11741 * Ext JS Library 1.1.1
11742 * Copyright(c) 2006-2007, Ext JS, LLC.
11744 * Originally Released Under LGPL - original licence link has changed is not relivant.
11747 * <script type="text/javascript">
11751 Roo.data.Field = function(config){
11752 if(typeof config == "string"){
11753 config = {name: config};
11755 Roo.apply(this, config);
11758 this.type = "auto";
11761 var st = Roo.data.SortTypes;
11762 // named sortTypes are supported, here we look them up
11763 if(typeof this.sortType == "string"){
11764 this.sortType = st[this.sortType];
11767 // set default sortType for strings and dates
11768 if(!this.sortType){
11771 this.sortType = st.asUCString;
11774 this.sortType = st.asDate;
11777 this.sortType = st.none;
11782 var stripRe = /[\$,%]/g;
11784 // prebuilt conversion function for this field, instead of
11785 // switching every time we're reading a value
11787 var cv, dateFormat = this.dateFormat;
11792 cv = function(v){ return v; };
11795 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11799 return v !== undefined && v !== null && v !== '' ?
11800 parseInt(String(v).replace(stripRe, ""), 10) : '';
11805 return v !== undefined && v !== null && v !== '' ?
11806 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11811 cv = function(v){ return v === true || v === "true" || v == 1; };
11818 if(v instanceof Date){
11822 if(dateFormat == "timestamp"){
11823 return new Date(v*1000);
11825 return Date.parseDate(v, dateFormat);
11827 var parsed = Date.parse(v);
11828 return parsed ? new Date(parsed) : null;
11837 Roo.data.Field.prototype = {
11845 * Ext JS Library 1.1.1
11846 * Copyright(c) 2006-2007, Ext JS, LLC.
11848 * Originally Released Under LGPL - original licence link has changed is not relivant.
11851 * <script type="text/javascript">
11854 // Base class for reading structured data from a data source. This class is intended to be
11855 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11858 * @class Roo.data.DataReader
11859 * Base class for reading structured data from a data source. This class is intended to be
11860 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11863 Roo.data.DataReader = function(meta, recordType){
11867 this.recordType = recordType instanceof Array ?
11868 Roo.data.Record.create(recordType) : recordType;
11871 Roo.data.DataReader.prototype = {
11873 * Create an empty record
11874 * @param {Object} data (optional) - overlay some values
11875 * @return {Roo.data.Record} record created.
11877 newRow : function(d) {
11879 this.recordType.prototype.fields.each(function(c) {
11881 case 'int' : da[c.name] = 0; break;
11882 case 'date' : da[c.name] = new Date(); break;
11883 case 'float' : da[c.name] = 0.0; break;
11884 case 'boolean' : da[c.name] = false; break;
11885 default : da[c.name] = ""; break;
11889 return new this.recordType(Roo.apply(da, d));
11894 * Ext JS Library 1.1.1
11895 * Copyright(c) 2006-2007, Ext JS, LLC.
11897 * Originally Released Under LGPL - original licence link has changed is not relivant.
11900 * <script type="text/javascript">
11904 * @class Roo.data.DataProxy
11905 * @extends Roo.data.Observable
11906 * This class is an abstract base class for implementations which provide retrieval of
11907 * unformatted data objects.<br>
11909 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11910 * (of the appropriate type which knows how to parse the data object) to provide a block of
11911 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11913 * Custom implementations must implement the load method as described in
11914 * {@link Roo.data.HttpProxy#load}.
11916 Roo.data.DataProxy = function(){
11919 * @event beforeload
11920 * Fires before a network request is made to retrieve a data object.
11921 * @param {Object} This DataProxy object.
11922 * @param {Object} params The params parameter to the load function.
11927 * Fires before the load method's callback is called.
11928 * @param {Object} This DataProxy object.
11929 * @param {Object} o The data object.
11930 * @param {Object} arg The callback argument object passed to the load function.
11934 * @event loadexception
11935 * Fires if an Exception occurs during data retrieval.
11936 * @param {Object} This DataProxy object.
11937 * @param {Object} o The data object.
11938 * @param {Object} arg The callback argument object passed to the load function.
11939 * @param {Object} e The Exception.
11941 loadexception : true
11943 Roo.data.DataProxy.superclass.constructor.call(this);
11946 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11949 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11953 * Ext JS Library 1.1.1
11954 * Copyright(c) 2006-2007, Ext JS, LLC.
11956 * Originally Released Under LGPL - original licence link has changed is not relivant.
11959 * <script type="text/javascript">
11962 * @class Roo.data.MemoryProxy
11963 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11964 * to the Reader when its load method is called.
11966 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11968 Roo.data.MemoryProxy = function(data){
11972 Roo.data.MemoryProxy.superclass.constructor.call(this);
11976 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11979 * Load data from the requested source (in this case an in-memory
11980 * data object passed to the constructor), read the data object into
11981 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11982 * process that block using the passed callback.
11983 * @param {Object} params This parameter is not used by the MemoryProxy class.
11984 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11985 * object into a block of Roo.data.Records.
11986 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11987 * The function must be passed <ul>
11988 * <li>The Record block object</li>
11989 * <li>The "arg" argument from the load function</li>
11990 * <li>A boolean success indicator</li>
11992 * @param {Object} scope The scope in which to call the callback
11993 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11995 load : function(params, reader, callback, scope, arg){
11996 params = params || {};
11999 result = reader.readRecords(this.data);
12001 this.fireEvent("loadexception", this, arg, null, e);
12002 callback.call(scope, null, arg, false);
12005 callback.call(scope, result, arg, true);
12009 update : function(params, records){
12014 * Ext JS Library 1.1.1
12015 * Copyright(c) 2006-2007, Ext JS, LLC.
12017 * Originally Released Under LGPL - original licence link has changed is not relivant.
12020 * <script type="text/javascript">
12023 * @class Roo.data.HttpProxy
12024 * @extends Roo.data.DataProxy
12025 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12026 * configured to reference a certain URL.<br><br>
12028 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12029 * from which the running page was served.<br><br>
12031 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12033 * Be aware that to enable the browser to parse an XML document, the server must set
12034 * the Content-Type header in the HTTP response to "text/xml".
12036 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12037 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12038 * will be used to make the request.
12040 Roo.data.HttpProxy = function(conn){
12041 Roo.data.HttpProxy.superclass.constructor.call(this);
12042 // is conn a conn config or a real conn?
12044 this.useAjax = !conn || !conn.events;
12048 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12049 // thse are take from connection...
12052 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12055 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12056 * extra parameters to each request made by this object. (defaults to undefined)
12059 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12060 * to each request made by this object. (defaults to undefined)
12063 * @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)
12066 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12069 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12075 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12079 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12080 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12081 * a finer-grained basis than the DataProxy events.
12083 getConnection : function(){
12084 return this.useAjax ? Roo.Ajax : this.conn;
12088 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12089 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12090 * process that block using the passed callback.
12091 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12092 * for the request to the remote server.
12093 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12094 * object into a block of Roo.data.Records.
12095 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12096 * The function must be passed <ul>
12097 * <li>The Record block object</li>
12098 * <li>The "arg" argument from the load function</li>
12099 * <li>A boolean success indicator</li>
12101 * @param {Object} scope The scope in which to call the callback
12102 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12104 load : function(params, reader, callback, scope, arg){
12105 if(this.fireEvent("beforeload", this, params) !== false){
12107 params : params || {},
12109 callback : callback,
12114 callback : this.loadResponse,
12118 Roo.applyIf(o, this.conn);
12119 if(this.activeRequest){
12120 Roo.Ajax.abort(this.activeRequest);
12122 this.activeRequest = Roo.Ajax.request(o);
12124 this.conn.request(o);
12127 callback.call(scope||this, null, arg, false);
12132 loadResponse : function(o, success, response){
12133 delete this.activeRequest;
12135 this.fireEvent("loadexception", this, o, response);
12136 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12141 result = o.reader.read(response);
12143 this.fireEvent("loadexception", this, o, response, e);
12144 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12148 this.fireEvent("load", this, o, o.request.arg);
12149 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12153 update : function(dataSet){
12158 updateResponse : function(dataSet){
12163 * Ext JS Library 1.1.1
12164 * Copyright(c) 2006-2007, Ext JS, LLC.
12166 * Originally Released Under LGPL - original licence link has changed is not relivant.
12169 * <script type="text/javascript">
12173 * @class Roo.data.ScriptTagProxy
12174 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12175 * other than the originating domain of the running page.<br><br>
12177 * <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
12178 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12180 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12181 * source code that is used as the source inside a <script> tag.<br><br>
12183 * In order for the browser to process the returned data, the server must wrap the data object
12184 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12185 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12186 * depending on whether the callback name was passed:
12189 boolean scriptTag = false;
12190 String cb = request.getParameter("callback");
12193 response.setContentType("text/javascript");
12195 response.setContentType("application/x-json");
12197 Writer out = response.getWriter();
12199 out.write(cb + "(");
12201 out.print(dataBlock.toJsonString());
12208 * @param {Object} config A configuration object.
12210 Roo.data.ScriptTagProxy = function(config){
12211 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12212 Roo.apply(this, config);
12213 this.head = document.getElementsByTagName("head")[0];
12216 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12218 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12220 * @cfg {String} url The URL from which to request the data object.
12223 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12227 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12228 * the server the name of the callback function set up by the load call to process the returned data object.
12229 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12230 * javascript output which calls this named function passing the data object as its only parameter.
12232 callbackParam : "callback",
12234 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12235 * name to the request.
12240 * Load data from the configured URL, read the data object into
12241 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12242 * process that block using the passed callback.
12243 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12244 * for the request to the remote server.
12245 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12246 * object into a block of Roo.data.Records.
12247 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12248 * The function must be passed <ul>
12249 * <li>The Record block object</li>
12250 * <li>The "arg" argument from the load function</li>
12251 * <li>A boolean success indicator</li>
12253 * @param {Object} scope The scope in which to call the callback
12254 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12256 load : function(params, reader, callback, scope, arg){
12257 if(this.fireEvent("beforeload", this, params) !== false){
12259 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12261 var url = this.url;
12262 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12264 url += "&_dc=" + (new Date().getTime());
12266 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12269 cb : "stcCallback"+transId,
12270 scriptId : "stcScript"+transId,
12274 callback : callback,
12280 window[trans.cb] = function(o){
12281 conn.handleResponse(o, trans);
12284 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12286 if(this.autoAbort !== false){
12290 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12292 var script = document.createElement("script");
12293 script.setAttribute("src", url);
12294 script.setAttribute("type", "text/javascript");
12295 script.setAttribute("id", trans.scriptId);
12296 this.head.appendChild(script);
12298 this.trans = trans;
12300 callback.call(scope||this, null, arg, false);
12305 isLoading : function(){
12306 return this.trans ? true : false;
12310 * Abort the current server request.
12312 abort : function(){
12313 if(this.isLoading()){
12314 this.destroyTrans(this.trans);
12319 destroyTrans : function(trans, isLoaded){
12320 this.head.removeChild(document.getElementById(trans.scriptId));
12321 clearTimeout(trans.timeoutId);
12323 window[trans.cb] = undefined;
12325 delete window[trans.cb];
12328 // if hasn't been loaded, wait for load to remove it to prevent script error
12329 window[trans.cb] = function(){
12330 window[trans.cb] = undefined;
12332 delete window[trans.cb];
12339 handleResponse : function(o, trans){
12340 this.trans = false;
12341 this.destroyTrans(trans, true);
12344 result = trans.reader.readRecords(o);
12346 this.fireEvent("loadexception", this, o, trans.arg, e);
12347 trans.callback.call(trans.scope||window, null, trans.arg, false);
12350 this.fireEvent("load", this, o, trans.arg);
12351 trans.callback.call(trans.scope||window, result, trans.arg, true);
12355 handleFailure : function(trans){
12356 this.trans = false;
12357 this.destroyTrans(trans, false);
12358 this.fireEvent("loadexception", this, null, trans.arg);
12359 trans.callback.call(trans.scope||window, null, trans.arg, false);
12363 * Ext JS Library 1.1.1
12364 * Copyright(c) 2006-2007, Ext JS, LLC.
12366 * Originally Released Under LGPL - original licence link has changed is not relivant.
12369 * <script type="text/javascript">
12373 * @class Roo.data.JsonReader
12374 * @extends Roo.data.DataReader
12375 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12376 * based on mappings in a provided Roo.data.Record constructor.
12378 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12379 * in the reply previously.
12384 var RecordDef = Roo.data.Record.create([
12385 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12386 {name: 'occupation'} // This field will use "occupation" as the mapping.
12388 var myReader = new Roo.data.JsonReader({
12389 totalProperty: "results", // The property which contains the total dataset size (optional)
12390 root: "rows", // The property which contains an Array of row objects
12391 id: "id" // The property within each row object that provides an ID for the record (optional)
12395 * This would consume a JSON file like this:
12397 { 'results': 2, 'rows': [
12398 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12399 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12402 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12403 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12404 * paged from the remote server.
12405 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12406 * @cfg {String} root name of the property which contains the Array of row objects.
12407 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12408 * @cfg {Array} fields Array of field definition objects
12410 * Create a new JsonReader
12411 * @param {Object} meta Metadata configuration options
12412 * @param {Object} recordType Either an Array of field definition objects,
12413 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12415 Roo.data.JsonReader = function(meta, recordType){
12418 // set some defaults:
12419 Roo.applyIf(meta, {
12420 totalProperty: 'total',
12421 successProperty : 'success',
12426 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12428 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12431 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12432 * Used by Store query builder to append _requestMeta to params.
12435 metaFromRemote : false,
12437 * This method is only used by a DataProxy which has retrieved data from a remote server.
12438 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12439 * @return {Object} data A data block which is used by an Roo.data.Store object as
12440 * a cache of Roo.data.Records.
12442 read : function(response){
12443 var json = response.responseText;
12445 var o = /* eval:var:o */ eval("("+json+")");
12447 throw {message: "JsonReader.read: Json object not found"};
12453 this.metaFromRemote = true;
12454 this.meta = o.metaData;
12455 this.recordType = Roo.data.Record.create(o.metaData.fields);
12456 this.onMetaChange(this.meta, this.recordType, o);
12458 return this.readRecords(o);
12461 // private function a store will implement
12462 onMetaChange : function(meta, recordType, o){
12469 simpleAccess: function(obj, subsc) {
12476 getJsonAccessor: function(){
12478 return function(expr) {
12480 return(re.test(expr))
12481 ? new Function("obj", "return obj." + expr)
12486 return Roo.emptyFn;
12491 * Create a data block containing Roo.data.Records from an XML document.
12492 * @param {Object} o An object which contains an Array of row objects in the property specified
12493 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12494 * which contains the total size of the dataset.
12495 * @return {Object} data A data block which is used by an Roo.data.Store object as
12496 * a cache of Roo.data.Records.
12498 readRecords : function(o){
12500 * After any data loads, the raw JSON data is available for further custom processing.
12504 var s = this.meta, Record = this.recordType,
12505 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12507 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12509 if(s.totalProperty) {
12510 this.getTotal = this.getJsonAccessor(s.totalProperty);
12512 if(s.successProperty) {
12513 this.getSuccess = this.getJsonAccessor(s.successProperty);
12515 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12517 var g = this.getJsonAccessor(s.id);
12518 this.getId = function(rec) {
12520 return (r === undefined || r === "") ? null : r;
12523 this.getId = function(){return null;};
12526 for(var jj = 0; jj < fl; jj++){
12528 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12529 this.ef[jj] = this.getJsonAccessor(map);
12533 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12534 if(s.totalProperty){
12535 var vt = parseInt(this.getTotal(o), 10);
12540 if(s.successProperty){
12541 var vs = this.getSuccess(o);
12542 if(vs === false || vs === 'false'){
12547 for(var i = 0; i < c; i++){
12550 var id = this.getId(n);
12551 for(var j = 0; j < fl; j++){
12553 var v = this.ef[j](n);
12555 Roo.log('missing convert for ' + f.name);
12559 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12561 var record = new Record(values, id);
12563 records[i] = record;
12569 totalRecords : totalRecords
12574 * Ext JS Library 1.1.1
12575 * Copyright(c) 2006-2007, Ext JS, LLC.
12577 * Originally Released Under LGPL - original licence link has changed is not relivant.
12580 * <script type="text/javascript">
12584 * @class Roo.data.ArrayReader
12585 * @extends Roo.data.DataReader
12586 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12587 * Each element of that Array represents a row of data fields. The
12588 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12589 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12593 var RecordDef = Roo.data.Record.create([
12594 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12595 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12597 var myReader = new Roo.data.ArrayReader({
12598 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12602 * This would consume an Array like this:
12604 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12606 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12608 * Create a new JsonReader
12609 * @param {Object} meta Metadata configuration options.
12610 * @param {Object} recordType Either an Array of field definition objects
12611 * as specified to {@link Roo.data.Record#create},
12612 * or an {@link Roo.data.Record} object
12613 * created using {@link Roo.data.Record#create}.
12615 Roo.data.ArrayReader = function(meta, recordType){
12616 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12619 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12621 * Create a data block containing Roo.data.Records from an XML document.
12622 * @param {Object} o An Array of row objects which represents the dataset.
12623 * @return {Object} data A data block which is used by an Roo.data.Store object as
12624 * a cache of Roo.data.Records.
12626 readRecords : function(o){
12627 var sid = this.meta ? this.meta.id : null;
12628 var recordType = this.recordType, fields = recordType.prototype.fields;
12631 for(var i = 0; i < root.length; i++){
12634 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12635 for(var j = 0, jlen = fields.length; j < jlen; j++){
12636 var f = fields.items[j];
12637 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12638 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12640 values[f.name] = v;
12642 var record = new recordType(values, id);
12644 records[records.length] = record;
12648 totalRecords : records.length
12657 * @class Roo.bootstrap.ComboBox
12658 * @extends Roo.bootstrap.TriggerField
12659 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12660 * @cfg {Boolean} append (true|false) default false
12661 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12662 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12663 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12664 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12665 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12666 * @cfg {Boolean} animate default true
12667 * @cfg {Boolean} emptyResultText only for touch device
12668 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12669 * @cfg {String} emptyTitle default ''
12671 * Create a new ComboBox.
12672 * @param {Object} config Configuration options
12674 Roo.bootstrap.ComboBox = function(config){
12675 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12679 * Fires when the dropdown list is expanded
12680 * @param {Roo.bootstrap.ComboBox} combo This combo box
12685 * Fires when the dropdown list is collapsed
12686 * @param {Roo.bootstrap.ComboBox} combo This combo box
12690 * @event beforeselect
12691 * Fires before a list item is selected. Return false to cancel the selection.
12692 * @param {Roo.bootstrap.ComboBox} combo This combo box
12693 * @param {Roo.data.Record} record The data record returned from the underlying store
12694 * @param {Number} index The index of the selected item in the dropdown list
12696 'beforeselect' : true,
12699 * Fires when a list item is selected
12700 * @param {Roo.bootstrap.ComboBox} combo This combo box
12701 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12702 * @param {Number} index The index of the selected item in the dropdown list
12706 * @event beforequery
12707 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12708 * The event object passed has these properties:
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12710 * @param {String} query The query
12711 * @param {Boolean} forceAll true to force "all" query
12712 * @param {Boolean} cancel true to cancel the query
12713 * @param {Object} e The query event object
12715 'beforequery': true,
12718 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12719 * @param {Roo.bootstrap.ComboBox} combo This combo box
12724 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12725 * @param {Roo.bootstrap.ComboBox} combo This combo box
12726 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12731 * Fires when the remove value from the combobox array
12732 * @param {Roo.bootstrap.ComboBox} combo This combo box
12736 * @event afterremove
12737 * Fires when the remove value from the combobox array
12738 * @param {Roo.bootstrap.ComboBox} combo This combo box
12740 'afterremove' : true,
12742 * @event specialfilter
12743 * Fires when specialfilter
12744 * @param {Roo.bootstrap.ComboBox} combo This combo box
12746 'specialfilter' : true,
12749 * Fires when tick the element
12750 * @param {Roo.bootstrap.ComboBox} combo This combo box
12754 * @event touchviewdisplay
12755 * Fires when touch view require special display (default is using displayField)
12756 * @param {Roo.bootstrap.ComboBox} combo This combo box
12757 * @param {Object} cfg set html .
12759 'touchviewdisplay' : true
12764 this.tickItems = [];
12766 this.selectedIndex = -1;
12767 if(this.mode == 'local'){
12768 if(config.queryDelay === undefined){
12769 this.queryDelay = 10;
12771 if(config.minChars === undefined){
12777 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12780 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12781 * rendering into an Roo.Editor, defaults to false)
12784 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12785 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12788 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12791 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12792 * the dropdown list (defaults to undefined, with no header element)
12796 * @cfg {String/Roo.Template} tpl The template to use to render the output
12800 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12802 listWidth: undefined,
12804 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12805 * mode = 'remote' or 'text' if mode = 'local')
12807 displayField: undefined,
12810 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12811 * mode = 'remote' or 'value' if mode = 'local').
12812 * Note: use of a valueField requires the user make a selection
12813 * in order for a value to be mapped.
12815 valueField: undefined,
12817 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12822 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12823 * field's data value (defaults to the underlying DOM element's name)
12825 hiddenName: undefined,
12827 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12831 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12833 selectedClass: 'active',
12836 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12840 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12841 * anchor positions (defaults to 'tl-bl')
12843 listAlign: 'tl-bl?',
12845 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12849 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12850 * query specified by the allQuery config option (defaults to 'query')
12852 triggerAction: 'query',
12854 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12855 * (defaults to 4, does not apply if editable = false)
12859 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12860 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12864 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12865 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12869 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12870 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12874 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12875 * when editable = true (defaults to false)
12877 selectOnFocus:false,
12879 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12881 queryParam: 'query',
12883 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12884 * when mode = 'remote' (defaults to 'Loading...')
12886 loadingText: 'Loading...',
12888 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12892 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12896 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12897 * traditional select (defaults to true)
12901 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12905 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12909 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12910 * listWidth has a higher value)
12914 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12915 * allow the user to set arbitrary text into the field (defaults to false)
12917 forceSelection:false,
12919 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12920 * if typeAhead = true (defaults to 250)
12922 typeAheadDelay : 250,
12924 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12925 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12927 valueNotFoundText : undefined,
12929 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12931 blockFocus : false,
12934 * @cfg {Boolean} disableClear Disable showing of clear button.
12936 disableClear : false,
12938 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12940 alwaysQuery : false,
12943 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12948 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12950 invalidClass : "has-warning",
12953 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12955 validClass : "has-success",
12958 * @cfg {Boolean} specialFilter (true|false) special filter default false
12960 specialFilter : false,
12963 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12965 mobileTouchView : true,
12968 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12970 useNativeIOS : false,
12972 ios_options : false,
12984 btnPosition : 'right',
12985 triggerList : true,
12986 showToggleBtn : true,
12988 emptyResultText: 'Empty',
12989 triggerText : 'Select',
12992 // element that contains real text value.. (when hidden is used..)
12994 getAutoCreate : function()
12999 * Render classic select for iso
13002 if(Roo.isIOS && this.useNativeIOS){
13003 cfg = this.getAutoCreateNativeIOS();
13011 if(Roo.isTouch && this.mobileTouchView){
13012 cfg = this.getAutoCreateTouchView();
13019 if(!this.tickable){
13020 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13025 * ComboBox with tickable selections
13028 var align = this.labelAlign || this.parentLabelAlign();
13031 cls : 'form-group roo-combobox-tickable' //input-group
13034 var btn_text_select = '';
13035 var btn_text_done = '';
13036 var btn_text_cancel = '';
13038 if (this.btn_text_show) {
13039 btn_text_select = 'Select';
13040 btn_text_done = 'Done';
13041 btn_text_cancel = 'Cancel';
13046 cls : 'tickable-buttons',
13051 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13052 //html : this.triggerText
13053 html: btn_text_select
13059 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13061 html: btn_text_done
13067 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13069 html: btn_text_cancel
13075 buttons.cn.unshift({
13077 cls: 'roo-select2-search-field-input'
13083 Roo.each(buttons.cn, function(c){
13085 c.cls += ' btn-' + _this.size;
13088 if (_this.disabled) {
13099 cls: 'form-hidden-field'
13103 cls: 'roo-select2-choices',
13107 cls: 'roo-select2-search-field',
13118 cls: 'roo-select2-container input-group roo-select2-container-multi',
13123 // cls: 'typeahead typeahead-long dropdown-menu',
13124 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13129 if(this.hasFeedback && !this.allowBlank){
13133 cls: 'glyphicon form-control-feedback'
13136 combobox.cn.push(feedback);
13140 if (align ==='left' && this.fieldLabel.length) {
13142 cfg.cls += ' roo-form-group-label-left';
13147 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13148 tooltip : 'This field is required'
13153 cls : 'control-label',
13154 html : this.fieldLabel
13166 var labelCfg = cfg.cn[1];
13167 var contentCfg = cfg.cn[2];
13170 if(this.indicatorpos == 'right'){
13176 cls : 'control-label',
13180 html : this.fieldLabel
13184 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13185 tooltip : 'This field is required'
13200 labelCfg = cfg.cn[0];
13201 contentCfg = cfg.cn[1];
13205 if(this.labelWidth > 12){
13206 labelCfg.style = "width: " + this.labelWidth + 'px';
13209 if(this.labelWidth < 13 && this.labelmd == 0){
13210 this.labelmd = this.labelWidth;
13213 if(this.labellg > 0){
13214 labelCfg.cls += ' col-lg-' + this.labellg;
13215 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13218 if(this.labelmd > 0){
13219 labelCfg.cls += ' col-md-' + this.labelmd;
13220 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13223 if(this.labelsm > 0){
13224 labelCfg.cls += ' col-sm-' + this.labelsm;
13225 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13228 if(this.labelxs > 0){
13229 labelCfg.cls += ' col-xs-' + this.labelxs;
13230 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13234 } else if ( this.fieldLabel.length) {
13235 // Roo.log(" label");
13239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13240 tooltip : 'This field is required'
13244 //cls : 'input-group-addon',
13245 html : this.fieldLabel
13250 if(this.indicatorpos == 'right'){
13254 //cls : 'input-group-addon',
13255 html : this.fieldLabel
13259 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13260 tooltip : 'This field is required'
13269 // Roo.log(" no label && no align");
13276 ['xs','sm','md','lg'].map(function(size){
13277 if (settings[size]) {
13278 cfg.cls += ' col-' + size + '-' + settings[size];
13286 _initEventsCalled : false,
13289 initEvents: function()
13291 if (this._initEventsCalled) { // as we call render... prevent looping...
13294 this._initEventsCalled = true;
13297 throw "can not find store for combo";
13300 this.indicator = this.indicatorEl();
13302 this.store = Roo.factory(this.store, Roo.data);
13303 this.store.parent = this;
13305 // if we are building from html. then this element is so complex, that we can not really
13306 // use the rendered HTML.
13307 // so we have to trash and replace the previous code.
13308 if (Roo.XComponent.build_from_html) {
13309 // remove this element....
13310 var e = this.el.dom, k=0;
13311 while (e ) { e = e.previousSibling; ++k;}
13316 this.rendered = false;
13318 this.render(this.parent().getChildContainer(true), k);
13321 if(Roo.isIOS && this.useNativeIOS){
13322 this.initIOSView();
13330 if(Roo.isTouch && this.mobileTouchView){
13331 this.initTouchView();
13336 this.initTickableEvents();
13340 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13342 if(this.hiddenName){
13344 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13346 this.hiddenField.dom.value =
13347 this.hiddenValue !== undefined ? this.hiddenValue :
13348 this.value !== undefined ? this.value : '';
13350 // prevent input submission
13351 this.el.dom.removeAttribute('name');
13352 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13357 // this.el.dom.setAttribute('autocomplete', 'off');
13360 var cls = 'x-combo-list';
13362 //this.list = new Roo.Layer({
13363 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13369 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13370 _this.list.setWidth(lw);
13373 this.list.on('mouseover', this.onViewOver, this);
13374 this.list.on('mousemove', this.onViewMove, this);
13375 this.list.on('scroll', this.onViewScroll, this);
13378 this.list.swallowEvent('mousewheel');
13379 this.assetHeight = 0;
13382 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13383 this.assetHeight += this.header.getHeight();
13386 this.innerList = this.list.createChild({cls:cls+'-inner'});
13387 this.innerList.on('mouseover', this.onViewOver, this);
13388 this.innerList.on('mousemove', this.onViewMove, this);
13389 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13391 if(this.allowBlank && !this.pageSize && !this.disableClear){
13392 this.footer = this.list.createChild({cls:cls+'-ft'});
13393 this.pageTb = new Roo.Toolbar(this.footer);
13397 this.footer = this.list.createChild({cls:cls+'-ft'});
13398 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13399 {pageSize: this.pageSize});
13403 if (this.pageTb && this.allowBlank && !this.disableClear) {
13405 this.pageTb.add(new Roo.Toolbar.Fill(), {
13406 cls: 'x-btn-icon x-btn-clear',
13408 handler: function()
13411 _this.clearValue();
13412 _this.onSelect(false, -1);
13417 this.assetHeight += this.footer.getHeight();
13422 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13425 this.view = new Roo.View(this.list, this.tpl, {
13426 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13428 //this.view.wrapEl.setDisplayed(false);
13429 this.view.on('click', this.onViewClick, this);
13432 this.store.on('beforeload', this.onBeforeLoad, this);
13433 this.store.on('load', this.onLoad, this);
13434 this.store.on('loadexception', this.onLoadException, this);
13436 if(this.resizable){
13437 this.resizer = new Roo.Resizable(this.list, {
13438 pinned:true, handles:'se'
13440 this.resizer.on('resize', function(r, w, h){
13441 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13442 this.listWidth = w;
13443 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13444 this.restrictHeight();
13446 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13449 if(!this.editable){
13450 this.editable = true;
13451 this.setEditable(false);
13456 if (typeof(this.events.add.listeners) != 'undefined') {
13458 this.addicon = this.wrap.createChild(
13459 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13461 this.addicon.on('click', function(e) {
13462 this.fireEvent('add', this);
13465 if (typeof(this.events.edit.listeners) != 'undefined') {
13467 this.editicon = this.wrap.createChild(
13468 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13469 if (this.addicon) {
13470 this.editicon.setStyle('margin-left', '40px');
13472 this.editicon.on('click', function(e) {
13474 // we fire even if inothing is selected..
13475 this.fireEvent('edit', this, this.lastData );
13481 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13482 "up" : function(e){
13483 this.inKeyMode = true;
13487 "down" : function(e){
13488 if(!this.isExpanded()){
13489 this.onTriggerClick();
13491 this.inKeyMode = true;
13496 "enter" : function(e){
13497 // this.onViewClick();
13501 if(this.fireEvent("specialkey", this, e)){
13502 this.onViewClick(false);
13508 "esc" : function(e){
13512 "tab" : function(e){
13515 if(this.fireEvent("specialkey", this, e)){
13516 this.onViewClick(false);
13524 doRelay : function(foo, bar, hname){
13525 if(hname == 'down' || this.scope.isExpanded()){
13526 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13535 this.queryDelay = Math.max(this.queryDelay || 10,
13536 this.mode == 'local' ? 10 : 250);
13539 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13541 if(this.typeAhead){
13542 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13544 if(this.editable !== false){
13545 this.inputEl().on("keyup", this.onKeyUp, this);
13547 if(this.forceSelection){
13548 this.inputEl().on('blur', this.doForce, this);
13552 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13553 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13557 initTickableEvents: function()
13561 if(this.hiddenName){
13563 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13565 this.hiddenField.dom.value =
13566 this.hiddenValue !== undefined ? this.hiddenValue :
13567 this.value !== undefined ? this.value : '';
13569 // prevent input submission
13570 this.el.dom.removeAttribute('name');
13571 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13576 // this.list = this.el.select('ul.dropdown-menu',true).first();
13578 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13579 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13580 if(this.triggerList){
13581 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13584 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13585 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13587 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13588 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13590 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13591 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13593 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13594 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13595 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13598 this.cancelBtn.hide();
13603 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13604 _this.list.setWidth(lw);
13607 this.list.on('mouseover', this.onViewOver, this);
13608 this.list.on('mousemove', this.onViewMove, this);
13610 this.list.on('scroll', this.onViewScroll, this);
13613 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13614 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13617 this.view = new Roo.View(this.list, this.tpl, {
13622 selectedClass: this.selectedClass
13625 //this.view.wrapEl.setDisplayed(false);
13626 this.view.on('click', this.onViewClick, this);
13630 this.store.on('beforeload', this.onBeforeLoad, this);
13631 this.store.on('load', this.onLoad, this);
13632 this.store.on('loadexception', this.onLoadException, this);
13635 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13636 "up" : function(e){
13637 this.inKeyMode = true;
13641 "down" : function(e){
13642 this.inKeyMode = true;
13646 "enter" : function(e){
13647 if(this.fireEvent("specialkey", this, e)){
13648 this.onViewClick(false);
13654 "esc" : function(e){
13655 this.onTickableFooterButtonClick(e, false, false);
13658 "tab" : function(e){
13659 this.fireEvent("specialkey", this, e);
13661 this.onTickableFooterButtonClick(e, false, false);
13668 doRelay : function(e, fn, key){
13669 if(this.scope.isExpanded()){
13670 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13679 this.queryDelay = Math.max(this.queryDelay || 10,
13680 this.mode == 'local' ? 10 : 250);
13683 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13685 if(this.typeAhead){
13686 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13689 if(this.editable !== false){
13690 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13693 this.indicator = this.indicatorEl();
13695 if(this.indicator){
13696 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13697 this.indicator.hide();
13702 onDestroy : function(){
13704 this.view.setStore(null);
13705 this.view.el.removeAllListeners();
13706 this.view.el.remove();
13707 this.view.purgeListeners();
13710 this.list.dom.innerHTML = '';
13714 this.store.un('beforeload', this.onBeforeLoad, this);
13715 this.store.un('load', this.onLoad, this);
13716 this.store.un('loadexception', this.onLoadException, this);
13718 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13722 fireKey : function(e){
13723 if(e.isNavKeyPress() && !this.list.isVisible()){
13724 this.fireEvent("specialkey", this, e);
13729 onResize: function(w, h){
13730 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13732 // if(typeof w != 'number'){
13733 // // we do not handle it!?!?
13736 // var tw = this.trigger.getWidth();
13737 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13738 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13740 // this.inputEl().setWidth( this.adjustWidth('input', x));
13742 // //this.trigger.setStyle('left', x+'px');
13744 // if(this.list && this.listWidth === undefined){
13745 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13746 // this.list.setWidth(lw);
13747 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13755 * Allow or prevent the user from directly editing the field text. If false is passed,
13756 * the user will only be able to select from the items defined in the dropdown list. This method
13757 * is the runtime equivalent of setting the 'editable' config option at config time.
13758 * @param {Boolean} value True to allow the user to directly edit the field text
13760 setEditable : function(value){
13761 if(value == this.editable){
13764 this.editable = value;
13766 this.inputEl().dom.setAttribute('readOnly', true);
13767 this.inputEl().on('mousedown', this.onTriggerClick, this);
13768 this.inputEl().addClass('x-combo-noedit');
13770 this.inputEl().dom.setAttribute('readOnly', false);
13771 this.inputEl().un('mousedown', this.onTriggerClick, this);
13772 this.inputEl().removeClass('x-combo-noedit');
13778 onBeforeLoad : function(combo,opts){
13779 if(!this.hasFocus){
13783 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13785 this.restrictHeight();
13786 this.selectedIndex = -1;
13790 onLoad : function(){
13792 this.hasQuery = false;
13794 if(!this.hasFocus){
13798 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13799 this.loading.hide();
13802 if(this.store.getCount() > 0){
13805 this.restrictHeight();
13806 if(this.lastQuery == this.allQuery){
13807 if(this.editable && !this.tickable){
13808 this.inputEl().dom.select();
13812 !this.selectByValue(this.value, true) &&
13815 !this.store.lastOptions ||
13816 typeof(this.store.lastOptions.add) == 'undefined' ||
13817 this.store.lastOptions.add != true
13820 this.select(0, true);
13823 if(this.autoFocus){
13826 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13827 this.taTask.delay(this.typeAheadDelay);
13831 this.onEmptyResults();
13837 onLoadException : function()
13839 this.hasQuery = false;
13841 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13842 this.loading.hide();
13845 if(this.tickable && this.editable){
13850 // only causes errors at present
13851 //Roo.log(this.store.reader.jsonData);
13852 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13854 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13860 onTypeAhead : function(){
13861 if(this.store.getCount() > 0){
13862 var r = this.store.getAt(0);
13863 var newValue = r.data[this.displayField];
13864 var len = newValue.length;
13865 var selStart = this.getRawValue().length;
13867 if(selStart != len){
13868 this.setRawValue(newValue);
13869 this.selectText(selStart, newValue.length);
13875 onSelect : function(record, index){
13877 if(this.fireEvent('beforeselect', this, record, index) !== false){
13879 this.setFromData(index > -1 ? record.data : false);
13882 this.fireEvent('select', this, record, index);
13887 * Returns the currently selected field value or empty string if no value is set.
13888 * @return {String} value The selected value
13890 getValue : function()
13892 if(Roo.isIOS && this.useNativeIOS){
13893 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13897 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13900 if(this.valueField){
13901 return typeof this.value != 'undefined' ? this.value : '';
13903 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13907 getRawValue : function()
13909 if(Roo.isIOS && this.useNativeIOS){
13910 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13913 var v = this.inputEl().getValue();
13919 * Clears any text/value currently set in the field
13921 clearValue : function(){
13923 if(this.hiddenField){
13924 this.hiddenField.dom.value = '';
13927 this.setRawValue('');
13928 this.lastSelectionText = '';
13929 this.lastData = false;
13931 var close = this.closeTriggerEl();
13942 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13943 * will be displayed in the field. If the value does not match the data value of an existing item,
13944 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13945 * Otherwise the field will be blank (although the value will still be set).
13946 * @param {String} value The value to match
13948 setValue : function(v)
13950 if(Roo.isIOS && this.useNativeIOS){
13951 this.setIOSValue(v);
13961 if(this.valueField){
13962 var r = this.findRecord(this.valueField, v);
13964 text = r.data[this.displayField];
13965 }else if(this.valueNotFoundText !== undefined){
13966 text = this.valueNotFoundText;
13969 this.lastSelectionText = text;
13970 if(this.hiddenField){
13971 this.hiddenField.dom.value = v;
13973 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13976 var close = this.closeTriggerEl();
13979 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13985 * @property {Object} the last set data for the element
13990 * Sets the value of the field based on a object which is related to the record format for the store.
13991 * @param {Object} value the value to set as. or false on reset?
13993 setFromData : function(o){
14000 var dv = ''; // display value
14001 var vv = ''; // value value..
14003 if (this.displayField) {
14004 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14006 // this is an error condition!!!
14007 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14010 if(this.valueField){
14011 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14014 var close = this.closeTriggerEl();
14017 if(dv.length || vv * 1 > 0){
14019 this.blockFocus=true;
14025 if(this.hiddenField){
14026 this.hiddenField.dom.value = vv;
14028 this.lastSelectionText = dv;
14029 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14033 // no hidden field.. - we store the value in 'value', but still display
14034 // display field!!!!
14035 this.lastSelectionText = dv;
14036 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14043 reset : function(){
14044 // overridden so that last data is reset..
14051 this.setValue(this.originalValue);
14052 //this.clearInvalid();
14053 this.lastData = false;
14055 this.view.clearSelections();
14061 findRecord : function(prop, value){
14063 if(this.store.getCount() > 0){
14064 this.store.each(function(r){
14065 if(r.data[prop] == value){
14075 getName: function()
14077 // returns hidden if it's set..
14078 if (!this.rendered) {return ''};
14079 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14083 onViewMove : function(e, t){
14084 this.inKeyMode = false;
14088 onViewOver : function(e, t){
14089 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14092 var item = this.view.findItemFromChild(t);
14095 var index = this.view.indexOf(item);
14096 this.select(index, false);
14101 onViewClick : function(view, doFocus, el, e)
14103 var index = this.view.getSelectedIndexes()[0];
14105 var r = this.store.getAt(index);
14109 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14116 Roo.each(this.tickItems, function(v,k){
14118 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14120 _this.tickItems.splice(k, 1);
14122 if(typeof(e) == 'undefined' && view == false){
14123 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14135 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14136 this.tickItems.push(r.data);
14139 if(typeof(e) == 'undefined' && view == false){
14140 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14147 this.onSelect(r, index);
14149 if(doFocus !== false && !this.blockFocus){
14150 this.inputEl().focus();
14155 restrictHeight : function(){
14156 //this.innerList.dom.style.height = '';
14157 //var inner = this.innerList.dom;
14158 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14159 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14160 //this.list.beginUpdate();
14161 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14162 this.list.alignTo(this.inputEl(), this.listAlign);
14163 this.list.alignTo(this.inputEl(), this.listAlign);
14164 //this.list.endUpdate();
14168 onEmptyResults : function(){
14170 if(this.tickable && this.editable){
14171 this.hasFocus = false;
14172 this.restrictHeight();
14180 * Returns true if the dropdown list is expanded, else false.
14182 isExpanded : function(){
14183 return this.list.isVisible();
14187 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14188 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14189 * @param {String} value The data value of the item to select
14190 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14191 * selected item if it is not currently in view (defaults to true)
14192 * @return {Boolean} True if the value matched an item in the list, else false
14194 selectByValue : function(v, scrollIntoView){
14195 if(v !== undefined && v !== null){
14196 var r = this.findRecord(this.valueField || this.displayField, v);
14198 this.select(this.store.indexOf(r), scrollIntoView);
14206 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14207 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14208 * @param {Number} index The zero-based index of the list item to select
14209 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14210 * selected item if it is not currently in view (defaults to true)
14212 select : function(index, scrollIntoView){
14213 this.selectedIndex = index;
14214 this.view.select(index);
14215 if(scrollIntoView !== false){
14216 var el = this.view.getNode(index);
14218 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14221 this.list.scrollChildIntoView(el, false);
14227 selectNext : function(){
14228 var ct = this.store.getCount();
14230 if(this.selectedIndex == -1){
14232 }else if(this.selectedIndex < ct-1){
14233 this.select(this.selectedIndex+1);
14239 selectPrev : function(){
14240 var ct = this.store.getCount();
14242 if(this.selectedIndex == -1){
14244 }else if(this.selectedIndex != 0){
14245 this.select(this.selectedIndex-1);
14251 onKeyUp : function(e){
14252 if(this.editable !== false && !e.isSpecialKey()){
14253 this.lastKey = e.getKey();
14254 this.dqTask.delay(this.queryDelay);
14259 validateBlur : function(){
14260 return !this.list || !this.list.isVisible();
14264 initQuery : function(){
14266 var v = this.getRawValue();
14268 if(this.tickable && this.editable){
14269 v = this.tickableInputEl().getValue();
14276 doForce : function(){
14277 if(this.inputEl().dom.value.length > 0){
14278 this.inputEl().dom.value =
14279 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14285 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14286 * query allowing the query action to be canceled if needed.
14287 * @param {String} query The SQL query to execute
14288 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14289 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14290 * saved in the current store (defaults to false)
14292 doQuery : function(q, forceAll){
14294 if(q === undefined || q === null){
14299 forceAll: forceAll,
14303 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14308 forceAll = qe.forceAll;
14309 if(forceAll === true || (q.length >= this.minChars)){
14311 this.hasQuery = true;
14313 if(this.lastQuery != q || this.alwaysQuery){
14314 this.lastQuery = q;
14315 if(this.mode == 'local'){
14316 this.selectedIndex = -1;
14318 this.store.clearFilter();
14321 if(this.specialFilter){
14322 this.fireEvent('specialfilter', this);
14327 this.store.filter(this.displayField, q);
14330 this.store.fireEvent("datachanged", this.store);
14337 this.store.baseParams[this.queryParam] = q;
14339 var options = {params : this.getParams(q)};
14342 options.add = true;
14343 options.params.start = this.page * this.pageSize;
14346 this.store.load(options);
14349 * this code will make the page width larger, at the beginning, the list not align correctly,
14350 * we should expand the list on onLoad
14351 * so command out it
14356 this.selectedIndex = -1;
14361 this.loadNext = false;
14365 getParams : function(q){
14367 //p[this.queryParam] = q;
14371 p.limit = this.pageSize;
14377 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14379 collapse : function(){
14380 if(!this.isExpanded()){
14386 this.hasFocus = false;
14390 this.cancelBtn.hide();
14391 this.trigger.show();
14394 this.tickableInputEl().dom.value = '';
14395 this.tickableInputEl().blur();
14400 Roo.get(document).un('mousedown', this.collapseIf, this);
14401 Roo.get(document).un('mousewheel', this.collapseIf, this);
14402 if (!this.editable) {
14403 Roo.get(document).un('keydown', this.listKeyPress, this);
14405 this.fireEvent('collapse', this);
14411 collapseIf : function(e){
14412 var in_combo = e.within(this.el);
14413 var in_list = e.within(this.list);
14414 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14416 if (in_combo || in_list || is_list) {
14417 //e.stopPropagation();
14422 this.onTickableFooterButtonClick(e, false, false);
14430 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14432 expand : function(){
14434 if(this.isExpanded() || !this.hasFocus){
14438 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14439 this.list.setWidth(lw);
14445 this.restrictHeight();
14449 this.tickItems = Roo.apply([], this.item);
14452 this.cancelBtn.show();
14453 this.trigger.hide();
14456 this.tickableInputEl().focus();
14461 Roo.get(document).on('mousedown', this.collapseIf, this);
14462 Roo.get(document).on('mousewheel', this.collapseIf, this);
14463 if (!this.editable) {
14464 Roo.get(document).on('keydown', this.listKeyPress, this);
14467 this.fireEvent('expand', this);
14471 // Implements the default empty TriggerField.onTriggerClick function
14472 onTriggerClick : function(e)
14474 Roo.log('trigger click');
14476 if(this.disabled || !this.triggerList){
14481 this.loadNext = false;
14483 if(this.isExpanded()){
14485 if (!this.blockFocus) {
14486 this.inputEl().focus();
14490 this.hasFocus = true;
14491 if(this.triggerAction == 'all') {
14492 this.doQuery(this.allQuery, true);
14494 this.doQuery(this.getRawValue());
14496 if (!this.blockFocus) {
14497 this.inputEl().focus();
14502 onTickableTriggerClick : function(e)
14509 this.loadNext = false;
14510 this.hasFocus = true;
14512 if(this.triggerAction == 'all') {
14513 this.doQuery(this.allQuery, true);
14515 this.doQuery(this.getRawValue());
14519 onSearchFieldClick : function(e)
14521 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14522 this.onTickableFooterButtonClick(e, false, false);
14526 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14531 this.loadNext = false;
14532 this.hasFocus = true;
14534 if(this.triggerAction == 'all') {
14535 this.doQuery(this.allQuery, true);
14537 this.doQuery(this.getRawValue());
14541 listKeyPress : function(e)
14543 //Roo.log('listkeypress');
14544 // scroll to first matching element based on key pres..
14545 if (e.isSpecialKey()) {
14548 var k = String.fromCharCode(e.getKey()).toUpperCase();
14551 var csel = this.view.getSelectedNodes();
14552 var cselitem = false;
14554 var ix = this.view.indexOf(csel[0]);
14555 cselitem = this.store.getAt(ix);
14556 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14562 this.store.each(function(v) {
14564 // start at existing selection.
14565 if (cselitem.id == v.id) {
14571 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14572 match = this.store.indexOf(v);
14578 if (match === false) {
14579 return true; // no more action?
14582 this.view.select(match);
14583 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14584 sn.scrollIntoView(sn.dom.parentNode, false);
14587 onViewScroll : function(e, t){
14589 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){
14593 this.hasQuery = true;
14595 this.loading = this.list.select('.loading', true).first();
14597 if(this.loading === null){
14598 this.list.createChild({
14600 cls: 'loading roo-select2-more-results roo-select2-active',
14601 html: 'Loading more results...'
14604 this.loading = this.list.select('.loading', true).first();
14606 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14608 this.loading.hide();
14611 this.loading.show();
14616 this.loadNext = true;
14618 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14623 addItem : function(o)
14625 var dv = ''; // display value
14627 if (this.displayField) {
14628 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14630 // this is an error condition!!!
14631 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14638 var choice = this.choices.createChild({
14640 cls: 'roo-select2-search-choice',
14649 cls: 'roo-select2-search-choice-close fa fa-times',
14654 }, this.searchField);
14656 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14658 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14666 this.inputEl().dom.value = '';
14671 onRemoveItem : function(e, _self, o)
14673 e.preventDefault();
14675 this.lastItem = Roo.apply([], this.item);
14677 var index = this.item.indexOf(o.data) * 1;
14680 Roo.log('not this item?!');
14684 this.item.splice(index, 1);
14689 this.fireEvent('remove', this, e);
14695 syncValue : function()
14697 if(!this.item.length){
14704 Roo.each(this.item, function(i){
14705 if(_this.valueField){
14706 value.push(i[_this.valueField]);
14713 this.value = value.join(',');
14715 if(this.hiddenField){
14716 this.hiddenField.dom.value = this.value;
14719 this.store.fireEvent("datachanged", this.store);
14724 clearItem : function()
14726 if(!this.multiple){
14732 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14740 if(this.tickable && !Roo.isTouch){
14741 this.view.refresh();
14745 inputEl: function ()
14747 if(Roo.isIOS && this.useNativeIOS){
14748 return this.el.select('select.roo-ios-select', true).first();
14751 if(Roo.isTouch && this.mobileTouchView){
14752 return this.el.select('input.form-control',true).first();
14756 return this.searchField;
14759 return this.el.select('input.form-control',true).first();
14762 onTickableFooterButtonClick : function(e, btn, el)
14764 e.preventDefault();
14766 this.lastItem = Roo.apply([], this.item);
14768 if(btn && btn.name == 'cancel'){
14769 this.tickItems = Roo.apply([], this.item);
14778 Roo.each(this.tickItems, function(o){
14786 validate : function()
14788 if(this.getVisibilityEl().hasClass('hidden')){
14792 var v = this.getRawValue();
14795 v = this.getValue();
14798 if(this.disabled || this.allowBlank || v.length){
14803 this.markInvalid();
14807 tickableInputEl : function()
14809 if(!this.tickable || !this.editable){
14810 return this.inputEl();
14813 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14817 getAutoCreateTouchView : function()
14822 cls: 'form-group' //input-group
14828 type : this.inputType,
14829 cls : 'form-control x-combo-noedit',
14830 autocomplete: 'new-password',
14831 placeholder : this.placeholder || '',
14836 input.name = this.name;
14840 input.cls += ' input-' + this.size;
14843 if (this.disabled) {
14844 input.disabled = true;
14855 inputblock.cls += ' input-group';
14857 inputblock.cn.unshift({
14859 cls : 'input-group-addon',
14864 if(this.removable && !this.multiple){
14865 inputblock.cls += ' roo-removable';
14867 inputblock.cn.push({
14870 cls : 'roo-combo-removable-btn close'
14874 if(this.hasFeedback && !this.allowBlank){
14876 inputblock.cls += ' has-feedback';
14878 inputblock.cn.push({
14880 cls: 'glyphicon form-control-feedback'
14887 inputblock.cls += (this.before) ? '' : ' input-group';
14889 inputblock.cn.push({
14891 cls : 'input-group-addon',
14902 cls: 'form-hidden-field'
14916 cls: 'form-hidden-field'
14920 cls: 'roo-select2-choices',
14924 cls: 'roo-select2-search-field',
14937 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14943 if(!this.multiple && this.showToggleBtn){
14950 if (this.caret != false) {
14953 cls: 'fa fa-' + this.caret
14960 cls : 'input-group-addon btn dropdown-toggle',
14965 cls: 'combobox-clear',
14979 combobox.cls += ' roo-select2-container-multi';
14982 var align = this.labelAlign || this.parentLabelAlign();
14984 if (align ==='left' && this.fieldLabel.length) {
14989 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14990 tooltip : 'This field is required'
14994 cls : 'control-label',
14995 html : this.fieldLabel
15006 var labelCfg = cfg.cn[1];
15007 var contentCfg = cfg.cn[2];
15010 if(this.indicatorpos == 'right'){
15015 cls : 'control-label',
15019 html : this.fieldLabel
15023 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15024 tooltip : 'This field is required'
15037 labelCfg = cfg.cn[0];
15038 contentCfg = cfg.cn[1];
15043 if(this.labelWidth > 12){
15044 labelCfg.style = "width: " + this.labelWidth + 'px';
15047 if(this.labelWidth < 13 && this.labelmd == 0){
15048 this.labelmd = this.labelWidth;
15051 if(this.labellg > 0){
15052 labelCfg.cls += ' col-lg-' + this.labellg;
15053 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15056 if(this.labelmd > 0){
15057 labelCfg.cls += ' col-md-' + this.labelmd;
15058 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15061 if(this.labelsm > 0){
15062 labelCfg.cls += ' col-sm-' + this.labelsm;
15063 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15066 if(this.labelxs > 0){
15067 labelCfg.cls += ' col-xs-' + this.labelxs;
15068 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15072 } else if ( this.fieldLabel.length) {
15076 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15077 tooltip : 'This field is required'
15081 cls : 'control-label',
15082 html : this.fieldLabel
15093 if(this.indicatorpos == 'right'){
15097 cls : 'control-label',
15098 html : this.fieldLabel,
15102 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15103 tooltip : 'This field is required'
15120 var settings = this;
15122 ['xs','sm','md','lg'].map(function(size){
15123 if (settings[size]) {
15124 cfg.cls += ' col-' + size + '-' + settings[size];
15131 initTouchView : function()
15133 this.renderTouchView();
15135 this.touchViewEl.on('scroll', function(){
15136 this.el.dom.scrollTop = 0;
15139 this.originalValue = this.getValue();
15141 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15143 this.inputEl().on("click", this.showTouchView, this);
15144 if (this.triggerEl) {
15145 this.triggerEl.on("click", this.showTouchView, this);
15149 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15150 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15152 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15154 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15155 this.store.on('load', this.onTouchViewLoad, this);
15156 this.store.on('loadexception', this.onTouchViewLoadException, this);
15158 if(this.hiddenName){
15160 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15162 this.hiddenField.dom.value =
15163 this.hiddenValue !== undefined ? this.hiddenValue :
15164 this.value !== undefined ? this.value : '';
15166 this.el.dom.removeAttribute('name');
15167 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15171 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15172 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15175 if(this.removable && !this.multiple){
15176 var close = this.closeTriggerEl();
15178 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15179 close.on('click', this.removeBtnClick, this, close);
15183 * fix the bug in Safari iOS8
15185 this.inputEl().on("focus", function(e){
15186 document.activeElement.blur();
15194 renderTouchView : function()
15196 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15197 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15199 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15200 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15202 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15203 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15204 this.touchViewBodyEl.setStyle('overflow', 'auto');
15206 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15207 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15209 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15210 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15214 showTouchView : function()
15220 this.touchViewHeaderEl.hide();
15222 if(this.modalTitle.length){
15223 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15224 this.touchViewHeaderEl.show();
15227 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15228 this.touchViewEl.show();
15230 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15232 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15233 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15235 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15237 if(this.modalTitle.length){
15238 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15241 this.touchViewBodyEl.setHeight(bodyHeight);
15245 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15247 this.touchViewEl.addClass('in');
15250 this.doTouchViewQuery();
15254 hideTouchView : function()
15256 this.touchViewEl.removeClass('in');
15260 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15262 this.touchViewEl.setStyle('display', 'none');
15267 setTouchViewValue : function()
15274 Roo.each(this.tickItems, function(o){
15279 this.hideTouchView();
15282 doTouchViewQuery : function()
15291 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15295 if(!this.alwaysQuery || this.mode == 'local'){
15296 this.onTouchViewLoad();
15303 onTouchViewBeforeLoad : function(combo,opts)
15309 onTouchViewLoad : function()
15311 if(this.store.getCount() < 1){
15312 this.onTouchViewEmptyResults();
15316 this.clearTouchView();
15318 var rawValue = this.getRawValue();
15320 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15322 this.tickItems = [];
15324 this.store.data.each(function(d, rowIndex){
15325 var row = this.touchViewListGroup.createChild(template);
15327 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15328 row.addClass(d.data.cls);
15331 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15334 html : d.data[this.displayField]
15337 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15338 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15341 row.removeClass('selected');
15342 if(!this.multiple && this.valueField &&
15343 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15346 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15347 row.addClass('selected');
15350 if(this.multiple && this.valueField &&
15351 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15355 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15356 this.tickItems.push(d.data);
15359 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15363 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15365 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15367 if(this.modalTitle.length){
15368 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15371 var listHeight = this.touchViewListGroup.getHeight();
15375 if(firstChecked && listHeight > bodyHeight){
15376 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15381 onTouchViewLoadException : function()
15383 this.hideTouchView();
15386 onTouchViewEmptyResults : function()
15388 this.clearTouchView();
15390 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15392 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15396 clearTouchView : function()
15398 this.touchViewListGroup.dom.innerHTML = '';
15401 onTouchViewClick : function(e, el, o)
15403 e.preventDefault();
15406 var rowIndex = o.rowIndex;
15408 var r = this.store.getAt(rowIndex);
15410 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15412 if(!this.multiple){
15413 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15414 c.dom.removeAttribute('checked');
15417 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15419 this.setFromData(r.data);
15421 var close = this.closeTriggerEl();
15427 this.hideTouchView();
15429 this.fireEvent('select', this, r, rowIndex);
15434 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15435 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15436 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15440 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15441 this.addItem(r.data);
15442 this.tickItems.push(r.data);
15446 getAutoCreateNativeIOS : function()
15449 cls: 'form-group' //input-group,
15454 cls : 'roo-ios-select'
15458 combobox.name = this.name;
15461 if (this.disabled) {
15462 combobox.disabled = true;
15465 var settings = this;
15467 ['xs','sm','md','lg'].map(function(size){
15468 if (settings[size]) {
15469 cfg.cls += ' col-' + size + '-' + settings[size];
15479 initIOSView : function()
15481 this.store.on('load', this.onIOSViewLoad, this);
15486 onIOSViewLoad : function()
15488 if(this.store.getCount() < 1){
15492 this.clearIOSView();
15494 if(this.allowBlank) {
15496 var default_text = '-- SELECT --';
15498 if(this.placeholder.length){
15499 default_text = this.placeholder;
15502 if(this.emptyTitle.length){
15503 default_text += ' - ' + this.emptyTitle + ' -';
15506 var opt = this.inputEl().createChild({
15509 html : default_text
15513 o[this.valueField] = 0;
15514 o[this.displayField] = default_text;
15516 this.ios_options.push({
15523 this.store.data.each(function(d, rowIndex){
15527 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15528 html = d.data[this.displayField];
15533 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15534 value = d.data[this.valueField];
15543 if(this.value == d.data[this.valueField]){
15544 option['selected'] = true;
15547 var opt = this.inputEl().createChild(option);
15549 this.ios_options.push({
15556 this.inputEl().on('change', function(){
15557 this.fireEvent('select', this);
15562 clearIOSView: function()
15564 this.inputEl().dom.innerHTML = '';
15566 this.ios_options = [];
15569 setIOSValue: function(v)
15573 if(!this.ios_options){
15577 Roo.each(this.ios_options, function(opts){
15579 opts.el.dom.removeAttribute('selected');
15581 if(opts.data[this.valueField] != v){
15585 opts.el.dom.setAttribute('selected', true);
15591 * @cfg {Boolean} grow
15595 * @cfg {Number} growMin
15599 * @cfg {Number} growMax
15608 Roo.apply(Roo.bootstrap.ComboBox, {
15612 cls: 'modal-header',
15634 cls: 'list-group-item',
15638 cls: 'roo-combobox-list-group-item-value'
15642 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15656 listItemCheckbox : {
15658 cls: 'list-group-item',
15662 cls: 'roo-combobox-list-group-item-value'
15666 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15682 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15687 cls: 'modal-footer',
15695 cls: 'col-xs-6 text-left',
15698 cls: 'btn btn-danger roo-touch-view-cancel',
15704 cls: 'col-xs-6 text-right',
15707 cls: 'btn btn-success roo-touch-view-ok',
15718 Roo.apply(Roo.bootstrap.ComboBox, {
15720 touchViewTemplate : {
15722 cls: 'modal fade roo-combobox-touch-view',
15726 cls: 'modal-dialog',
15727 style : 'position:fixed', // we have to fix position....
15731 cls: 'modal-content',
15733 Roo.bootstrap.ComboBox.header,
15734 Roo.bootstrap.ComboBox.body,
15735 Roo.bootstrap.ComboBox.footer
15744 * Ext JS Library 1.1.1
15745 * Copyright(c) 2006-2007, Ext JS, LLC.
15747 * Originally Released Under LGPL - original licence link has changed is not relivant.
15750 * <script type="text/javascript">
15755 * @extends Roo.util.Observable
15756 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15757 * This class also supports single and multi selection modes. <br>
15758 * Create a data model bound view:
15760 var store = new Roo.data.Store(...);
15762 var view = new Roo.View({
15764 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15766 singleSelect: true,
15767 selectedClass: "ydataview-selected",
15771 // listen for node click?
15772 view.on("click", function(vw, index, node, e){
15773 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15777 dataModel.load("foobar.xml");
15779 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15781 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15782 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15784 * Note: old style constructor is still suported (container, template, config)
15787 * Create a new View
15788 * @param {Object} config The config object
15791 Roo.View = function(config, depreciated_tpl, depreciated_config){
15793 this.parent = false;
15795 if (typeof(depreciated_tpl) == 'undefined') {
15796 // new way.. - universal constructor.
15797 Roo.apply(this, config);
15798 this.el = Roo.get(this.el);
15801 this.el = Roo.get(config);
15802 this.tpl = depreciated_tpl;
15803 Roo.apply(this, depreciated_config);
15805 this.wrapEl = this.el.wrap().wrap();
15806 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15809 if(typeof(this.tpl) == "string"){
15810 this.tpl = new Roo.Template(this.tpl);
15812 // support xtype ctors..
15813 this.tpl = new Roo.factory(this.tpl, Roo);
15817 this.tpl.compile();
15822 * @event beforeclick
15823 * Fires before a click is processed. Returns false to cancel the default action.
15824 * @param {Roo.View} this
15825 * @param {Number} index The index of the target node
15826 * @param {HTMLElement} node The target node
15827 * @param {Roo.EventObject} e The raw event object
15829 "beforeclick" : true,
15832 * Fires when a template node is clicked.
15833 * @param {Roo.View} this
15834 * @param {Number} index The index of the target node
15835 * @param {HTMLElement} node The target node
15836 * @param {Roo.EventObject} e The raw event object
15841 * Fires when a template node is double clicked.
15842 * @param {Roo.View} this
15843 * @param {Number} index The index of the target node
15844 * @param {HTMLElement} node The target node
15845 * @param {Roo.EventObject} e The raw event object
15849 * @event contextmenu
15850 * Fires when a template node is right clicked.
15851 * @param {Roo.View} this
15852 * @param {Number} index The index of the target node
15853 * @param {HTMLElement} node The target node
15854 * @param {Roo.EventObject} e The raw event object
15856 "contextmenu" : true,
15858 * @event selectionchange
15859 * Fires when the selected nodes change.
15860 * @param {Roo.View} this
15861 * @param {Array} selections Array of the selected nodes
15863 "selectionchange" : true,
15866 * @event beforeselect
15867 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15868 * @param {Roo.View} this
15869 * @param {HTMLElement} node The node to be selected
15870 * @param {Array} selections Array of currently selected nodes
15872 "beforeselect" : true,
15874 * @event preparedata
15875 * Fires on every row to render, to allow you to change the data.
15876 * @param {Roo.View} this
15877 * @param {Object} data to be rendered (change this)
15879 "preparedata" : true
15887 "click": this.onClick,
15888 "dblclick": this.onDblClick,
15889 "contextmenu": this.onContextMenu,
15893 this.selections = [];
15895 this.cmp = new Roo.CompositeElementLite([]);
15897 this.store = Roo.factory(this.store, Roo.data);
15898 this.setStore(this.store, true);
15901 if ( this.footer && this.footer.xtype) {
15903 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15905 this.footer.dataSource = this.store;
15906 this.footer.container = fctr;
15907 this.footer = Roo.factory(this.footer, Roo);
15908 fctr.insertFirst(this.el);
15910 // this is a bit insane - as the paging toolbar seems to detach the el..
15911 // dom.parentNode.parentNode.parentNode
15912 // they get detached?
15916 Roo.View.superclass.constructor.call(this);
15921 Roo.extend(Roo.View, Roo.util.Observable, {
15924 * @cfg {Roo.data.Store} store Data store to load data from.
15929 * @cfg {String|Roo.Element} el The container element.
15934 * @cfg {String|Roo.Template} tpl The template used by this View
15938 * @cfg {String} dataName the named area of the template to use as the data area
15939 * Works with domtemplates roo-name="name"
15943 * @cfg {String} selectedClass The css class to add to selected nodes
15945 selectedClass : "x-view-selected",
15947 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15952 * @cfg {String} text to display on mask (default Loading)
15956 * @cfg {Boolean} multiSelect Allow multiple selection
15958 multiSelect : false,
15960 * @cfg {Boolean} singleSelect Allow single selection
15962 singleSelect: false,
15965 * @cfg {Boolean} toggleSelect - selecting
15967 toggleSelect : false,
15970 * @cfg {Boolean} tickable - selecting
15975 * Returns the element this view is bound to.
15976 * @return {Roo.Element}
15978 getEl : function(){
15979 return this.wrapEl;
15985 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15987 refresh : function(){
15988 //Roo.log('refresh');
15991 // if we are using something like 'domtemplate', then
15992 // the what gets used is:
15993 // t.applySubtemplate(NAME, data, wrapping data..)
15994 // the outer template then get' applied with
15995 // the store 'extra data'
15996 // and the body get's added to the
15997 // roo-name="data" node?
15998 // <span class='roo-tpl-{name}'></span> ?????
16002 this.clearSelections();
16003 this.el.update("");
16005 var records = this.store.getRange();
16006 if(records.length < 1) {
16008 // is this valid?? = should it render a template??
16010 this.el.update(this.emptyText);
16014 if (this.dataName) {
16015 this.el.update(t.apply(this.store.meta)); //????
16016 el = this.el.child('.roo-tpl-' + this.dataName);
16019 for(var i = 0, len = records.length; i < len; i++){
16020 var data = this.prepareData(records[i].data, i, records[i]);
16021 this.fireEvent("preparedata", this, data, i, records[i]);
16023 var d = Roo.apply({}, data);
16026 Roo.apply(d, {'roo-id' : Roo.id()});
16030 Roo.each(this.parent.item, function(item){
16031 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16034 Roo.apply(d, {'roo-data-checked' : 'checked'});
16038 html[html.length] = Roo.util.Format.trim(
16040 t.applySubtemplate(this.dataName, d, this.store.meta) :
16047 el.update(html.join(""));
16048 this.nodes = el.dom.childNodes;
16049 this.updateIndexes(0);
16054 * Function to override to reformat the data that is sent to
16055 * the template for each node.
16056 * DEPRICATED - use the preparedata event handler.
16057 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16058 * a JSON object for an UpdateManager bound view).
16060 prepareData : function(data, index, record)
16062 this.fireEvent("preparedata", this, data, index, record);
16066 onUpdate : function(ds, record){
16067 // Roo.log('on update');
16068 this.clearSelections();
16069 var index = this.store.indexOf(record);
16070 var n = this.nodes[index];
16071 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16072 n.parentNode.removeChild(n);
16073 this.updateIndexes(index, index);
16079 onAdd : function(ds, records, index)
16081 //Roo.log(['on Add', ds, records, index] );
16082 this.clearSelections();
16083 if(this.nodes.length == 0){
16087 var n = this.nodes[index];
16088 for(var i = 0, len = records.length; i < len; i++){
16089 var d = this.prepareData(records[i].data, i, records[i]);
16091 this.tpl.insertBefore(n, d);
16094 this.tpl.append(this.el, d);
16097 this.updateIndexes(index);
16100 onRemove : function(ds, record, index){
16101 // Roo.log('onRemove');
16102 this.clearSelections();
16103 var el = this.dataName ?
16104 this.el.child('.roo-tpl-' + this.dataName) :
16107 el.dom.removeChild(this.nodes[index]);
16108 this.updateIndexes(index);
16112 * Refresh an individual node.
16113 * @param {Number} index
16115 refreshNode : function(index){
16116 this.onUpdate(this.store, this.store.getAt(index));
16119 updateIndexes : function(startIndex, endIndex){
16120 var ns = this.nodes;
16121 startIndex = startIndex || 0;
16122 endIndex = endIndex || ns.length - 1;
16123 for(var i = startIndex; i <= endIndex; i++){
16124 ns[i].nodeIndex = i;
16129 * Changes the data store this view uses and refresh the view.
16130 * @param {Store} store
16132 setStore : function(store, initial){
16133 if(!initial && this.store){
16134 this.store.un("datachanged", this.refresh);
16135 this.store.un("add", this.onAdd);
16136 this.store.un("remove", this.onRemove);
16137 this.store.un("update", this.onUpdate);
16138 this.store.un("clear", this.refresh);
16139 this.store.un("beforeload", this.onBeforeLoad);
16140 this.store.un("load", this.onLoad);
16141 this.store.un("loadexception", this.onLoad);
16145 store.on("datachanged", this.refresh, this);
16146 store.on("add", this.onAdd, this);
16147 store.on("remove", this.onRemove, this);
16148 store.on("update", this.onUpdate, this);
16149 store.on("clear", this.refresh, this);
16150 store.on("beforeload", this.onBeforeLoad, this);
16151 store.on("load", this.onLoad, this);
16152 store.on("loadexception", this.onLoad, this);
16160 * onbeforeLoad - masks the loading area.
16163 onBeforeLoad : function(store,opts)
16165 //Roo.log('onBeforeLoad');
16167 this.el.update("");
16169 this.el.mask(this.mask ? this.mask : "Loading" );
16171 onLoad : function ()
16178 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16179 * @param {HTMLElement} node
16180 * @return {HTMLElement} The template node
16182 findItemFromChild : function(node){
16183 var el = this.dataName ?
16184 this.el.child('.roo-tpl-' + this.dataName,true) :
16187 if(!node || node.parentNode == el){
16190 var p = node.parentNode;
16191 while(p && p != el){
16192 if(p.parentNode == el){
16201 onClick : function(e){
16202 var item = this.findItemFromChild(e.getTarget());
16204 var index = this.indexOf(item);
16205 if(this.onItemClick(item, index, e) !== false){
16206 this.fireEvent("click", this, index, item, e);
16209 this.clearSelections();
16214 onContextMenu : function(e){
16215 var item = this.findItemFromChild(e.getTarget());
16217 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16222 onDblClick : function(e){
16223 var item = this.findItemFromChild(e.getTarget());
16225 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16229 onItemClick : function(item, index, e)
16231 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16234 if (this.toggleSelect) {
16235 var m = this.isSelected(item) ? 'unselect' : 'select';
16238 _t[m](item, true, false);
16241 if(this.multiSelect || this.singleSelect){
16242 if(this.multiSelect && e.shiftKey && this.lastSelection){
16243 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16245 this.select(item, this.multiSelect && e.ctrlKey);
16246 this.lastSelection = item;
16249 if(!this.tickable){
16250 e.preventDefault();
16258 * Get the number of selected nodes.
16261 getSelectionCount : function(){
16262 return this.selections.length;
16266 * Get the currently selected nodes.
16267 * @return {Array} An array of HTMLElements
16269 getSelectedNodes : function(){
16270 return this.selections;
16274 * Get the indexes of the selected nodes.
16277 getSelectedIndexes : function(){
16278 var indexes = [], s = this.selections;
16279 for(var i = 0, len = s.length; i < len; i++){
16280 indexes.push(s[i].nodeIndex);
16286 * Clear all selections
16287 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16289 clearSelections : function(suppressEvent){
16290 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16291 this.cmp.elements = this.selections;
16292 this.cmp.removeClass(this.selectedClass);
16293 this.selections = [];
16294 if(!suppressEvent){
16295 this.fireEvent("selectionchange", this, this.selections);
16301 * Returns true if the passed node is selected
16302 * @param {HTMLElement/Number} node The node or node index
16303 * @return {Boolean}
16305 isSelected : function(node){
16306 var s = this.selections;
16310 node = this.getNode(node);
16311 return s.indexOf(node) !== -1;
16316 * @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
16317 * @param {Boolean} keepExisting (optional) true to keep existing selections
16318 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16320 select : function(nodeInfo, keepExisting, suppressEvent){
16321 if(nodeInfo instanceof Array){
16323 this.clearSelections(true);
16325 for(var i = 0, len = nodeInfo.length; i < len; i++){
16326 this.select(nodeInfo[i], true, true);
16330 var node = this.getNode(nodeInfo);
16331 if(!node || this.isSelected(node)){
16332 return; // already selected.
16335 this.clearSelections(true);
16338 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16339 Roo.fly(node).addClass(this.selectedClass);
16340 this.selections.push(node);
16341 if(!suppressEvent){
16342 this.fireEvent("selectionchange", this, this.selections);
16350 * @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
16351 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16352 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16354 unselect : function(nodeInfo, keepExisting, suppressEvent)
16356 if(nodeInfo instanceof Array){
16357 Roo.each(this.selections, function(s) {
16358 this.unselect(s, nodeInfo);
16362 var node = this.getNode(nodeInfo);
16363 if(!node || !this.isSelected(node)){
16364 //Roo.log("not selected");
16365 return; // not selected.
16369 Roo.each(this.selections, function(s) {
16371 Roo.fly(node).removeClass(this.selectedClass);
16378 this.selections= ns;
16379 this.fireEvent("selectionchange", this, this.selections);
16383 * Gets a template node.
16384 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16385 * @return {HTMLElement} The node or null if it wasn't found
16387 getNode : function(nodeInfo){
16388 if(typeof nodeInfo == "string"){
16389 return document.getElementById(nodeInfo);
16390 }else if(typeof nodeInfo == "number"){
16391 return this.nodes[nodeInfo];
16397 * Gets a range template nodes.
16398 * @param {Number} startIndex
16399 * @param {Number} endIndex
16400 * @return {Array} An array of nodes
16402 getNodes : function(start, end){
16403 var ns = this.nodes;
16404 start = start || 0;
16405 end = typeof end == "undefined" ? ns.length - 1 : end;
16408 for(var i = start; i <= end; i++){
16412 for(var i = start; i >= end; i--){
16420 * Finds the index of the passed node
16421 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16422 * @return {Number} The index of the node or -1
16424 indexOf : function(node){
16425 node = this.getNode(node);
16426 if(typeof node.nodeIndex == "number"){
16427 return node.nodeIndex;
16429 var ns = this.nodes;
16430 for(var i = 0, len = ns.length; i < len; i++){
16441 * based on jquery fullcalendar
16445 Roo.bootstrap = Roo.bootstrap || {};
16447 * @class Roo.bootstrap.Calendar
16448 * @extends Roo.bootstrap.Component
16449 * Bootstrap Calendar class
16450 * @cfg {Boolean} loadMask (true|false) default false
16451 * @cfg {Object} header generate the user specific header of the calendar, default false
16454 * Create a new Container
16455 * @param {Object} config The config object
16460 Roo.bootstrap.Calendar = function(config){
16461 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16465 * Fires when a date is selected
16466 * @param {DatePicker} this
16467 * @param {Date} date The selected date
16471 * @event monthchange
16472 * Fires when the displayed month changes
16473 * @param {DatePicker} this
16474 * @param {Date} date The selected month
16476 'monthchange': true,
16478 * @event evententer
16479 * Fires when mouse over an event
16480 * @param {Calendar} this
16481 * @param {event} Event
16483 'evententer': true,
16485 * @event eventleave
16486 * Fires when the mouse leaves an
16487 * @param {Calendar} this
16490 'eventleave': true,
16492 * @event eventclick
16493 * Fires when the mouse click an
16494 * @param {Calendar} this
16503 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16506 * @cfg {Number} startDay
16507 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16515 getAutoCreate : function(){
16518 var fc_button = function(name, corner, style, content ) {
16519 return Roo.apply({},{
16521 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16523 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16526 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16537 style : 'width:100%',
16544 cls : 'fc-header-left',
16546 fc_button('prev', 'left', 'arrow', '‹' ),
16547 fc_button('next', 'right', 'arrow', '›' ),
16548 { tag: 'span', cls: 'fc-header-space' },
16549 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16557 cls : 'fc-header-center',
16561 cls: 'fc-header-title',
16564 html : 'month / year'
16572 cls : 'fc-header-right',
16574 /* fc_button('month', 'left', '', 'month' ),
16575 fc_button('week', '', '', 'week' ),
16576 fc_button('day', 'right', '', 'day' )
16588 header = this.header;
16591 var cal_heads = function() {
16593 // fixme - handle this.
16595 for (var i =0; i < Date.dayNames.length; i++) {
16596 var d = Date.dayNames[i];
16599 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16600 html : d.substring(0,3)
16604 ret[0].cls += ' fc-first';
16605 ret[6].cls += ' fc-last';
16608 var cal_cell = function(n) {
16611 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16616 cls: 'fc-day-number',
16620 cls: 'fc-day-content',
16624 style: 'position: relative;' // height: 17px;
16636 var cal_rows = function() {
16639 for (var r = 0; r < 6; r++) {
16646 for (var i =0; i < Date.dayNames.length; i++) {
16647 var d = Date.dayNames[i];
16648 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16651 row.cn[0].cls+=' fc-first';
16652 row.cn[0].cn[0].style = 'min-height:90px';
16653 row.cn[6].cls+=' fc-last';
16657 ret[0].cls += ' fc-first';
16658 ret[4].cls += ' fc-prev-last';
16659 ret[5].cls += ' fc-last';
16666 cls: 'fc-border-separate',
16667 style : 'width:100%',
16675 cls : 'fc-first fc-last',
16693 cls : 'fc-content',
16694 style : "position: relative;",
16697 cls : 'fc-view fc-view-month fc-grid',
16698 style : 'position: relative',
16699 unselectable : 'on',
16702 cls : 'fc-event-container',
16703 style : 'position:absolute;z-index:8;top:0;left:0;'
16721 initEvents : function()
16724 throw "can not find store for calendar";
16730 style: "text-align:center",
16734 style: "background-color:white;width:50%;margin:250 auto",
16738 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16749 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16751 var size = this.el.select('.fc-content', true).first().getSize();
16752 this.maskEl.setSize(size.width, size.height);
16753 this.maskEl.enableDisplayMode("block");
16754 if(!this.loadMask){
16755 this.maskEl.hide();
16758 this.store = Roo.factory(this.store, Roo.data);
16759 this.store.on('load', this.onLoad, this);
16760 this.store.on('beforeload', this.onBeforeLoad, this);
16764 this.cells = this.el.select('.fc-day',true);
16765 //Roo.log(this.cells);
16766 this.textNodes = this.el.query('.fc-day-number');
16767 this.cells.addClassOnOver('fc-state-hover');
16769 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16770 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16771 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16772 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16774 this.on('monthchange', this.onMonthChange, this);
16776 this.update(new Date().clearTime());
16779 resize : function() {
16780 var sz = this.el.getSize();
16782 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16783 this.el.select('.fc-day-content div',true).setHeight(34);
16788 showPrevMonth : function(e){
16789 this.update(this.activeDate.add("mo", -1));
16791 showToday : function(e){
16792 this.update(new Date().clearTime());
16795 showNextMonth : function(e){
16796 this.update(this.activeDate.add("mo", 1));
16800 showPrevYear : function(){
16801 this.update(this.activeDate.add("y", -1));
16805 showNextYear : function(){
16806 this.update(this.activeDate.add("y", 1));
16811 update : function(date)
16813 var vd = this.activeDate;
16814 this.activeDate = date;
16815 // if(vd && this.el){
16816 // var t = date.getTime();
16817 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16818 // Roo.log('using add remove');
16820 // this.fireEvent('monthchange', this, date);
16822 // this.cells.removeClass("fc-state-highlight");
16823 // this.cells.each(function(c){
16824 // if(c.dateValue == t){
16825 // c.addClass("fc-state-highlight");
16826 // setTimeout(function(){
16827 // try{c.dom.firstChild.focus();}catch(e){}
16837 var days = date.getDaysInMonth();
16839 var firstOfMonth = date.getFirstDateOfMonth();
16840 var startingPos = firstOfMonth.getDay()-this.startDay;
16842 if(startingPos < this.startDay){
16846 var pm = date.add(Date.MONTH, -1);
16847 var prevStart = pm.getDaysInMonth()-startingPos;
16849 this.cells = this.el.select('.fc-day',true);
16850 this.textNodes = this.el.query('.fc-day-number');
16851 this.cells.addClassOnOver('fc-state-hover');
16853 var cells = this.cells.elements;
16854 var textEls = this.textNodes;
16856 Roo.each(cells, function(cell){
16857 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16860 days += startingPos;
16862 // convert everything to numbers so it's fast
16863 var day = 86400000;
16864 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16867 //Roo.log(prevStart);
16869 var today = new Date().clearTime().getTime();
16870 var sel = date.clearTime().getTime();
16871 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16872 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16873 var ddMatch = this.disabledDatesRE;
16874 var ddText = this.disabledDatesText;
16875 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16876 var ddaysText = this.disabledDaysText;
16877 var format = this.format;
16879 var setCellClass = function(cal, cell){
16883 //Roo.log('set Cell Class');
16885 var t = d.getTime();
16889 cell.dateValue = t;
16891 cell.className += " fc-today";
16892 cell.className += " fc-state-highlight";
16893 cell.title = cal.todayText;
16896 // disable highlight in other month..
16897 //cell.className += " fc-state-highlight";
16902 cell.className = " fc-state-disabled";
16903 cell.title = cal.minText;
16907 cell.className = " fc-state-disabled";
16908 cell.title = cal.maxText;
16912 if(ddays.indexOf(d.getDay()) != -1){
16913 cell.title = ddaysText;
16914 cell.className = " fc-state-disabled";
16917 if(ddMatch && format){
16918 var fvalue = d.dateFormat(format);
16919 if(ddMatch.test(fvalue)){
16920 cell.title = ddText.replace("%0", fvalue);
16921 cell.className = " fc-state-disabled";
16925 if (!cell.initialClassName) {
16926 cell.initialClassName = cell.dom.className;
16929 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16934 for(; i < startingPos; i++) {
16935 textEls[i].innerHTML = (++prevStart);
16936 d.setDate(d.getDate()+1);
16938 cells[i].className = "fc-past fc-other-month";
16939 setCellClass(this, cells[i]);
16944 for(; i < days; i++){
16945 intDay = i - startingPos + 1;
16946 textEls[i].innerHTML = (intDay);
16947 d.setDate(d.getDate()+1);
16949 cells[i].className = ''; // "x-date-active";
16950 setCellClass(this, cells[i]);
16954 for(; i < 42; i++) {
16955 textEls[i].innerHTML = (++extraDays);
16956 d.setDate(d.getDate()+1);
16958 cells[i].className = "fc-future fc-other-month";
16959 setCellClass(this, cells[i]);
16962 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16964 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16966 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16967 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16969 if(totalRows != 6){
16970 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16971 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16974 this.fireEvent('monthchange', this, date);
16978 if(!this.internalRender){
16979 var main = this.el.dom.firstChild;
16980 var w = main.offsetWidth;
16981 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16982 Roo.fly(main).setWidth(w);
16983 this.internalRender = true;
16984 // opera does not respect the auto grow header center column
16985 // then, after it gets a width opera refuses to recalculate
16986 // without a second pass
16987 if(Roo.isOpera && !this.secondPass){
16988 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16989 this.secondPass = true;
16990 this.update.defer(10, this, [date]);
16997 findCell : function(dt) {
16998 dt = dt.clearTime().getTime();
17000 this.cells.each(function(c){
17001 //Roo.log("check " +c.dateValue + '?=' + dt);
17002 if(c.dateValue == dt){
17012 findCells : function(ev) {
17013 var s = ev.start.clone().clearTime().getTime();
17015 var e= ev.end.clone().clearTime().getTime();
17018 this.cells.each(function(c){
17019 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17021 if(c.dateValue > e){
17024 if(c.dateValue < s){
17033 // findBestRow: function(cells)
17037 // for (var i =0 ; i < cells.length;i++) {
17038 // ret = Math.max(cells[i].rows || 0,ret);
17045 addItem : function(ev)
17047 // look for vertical location slot in
17048 var cells = this.findCells(ev);
17050 // ev.row = this.findBestRow(cells);
17052 // work out the location.
17056 for(var i =0; i < cells.length; i++) {
17058 cells[i].row = cells[0].row;
17061 cells[i].row = cells[i].row + 1;
17071 if (crow.start.getY() == cells[i].getY()) {
17073 crow.end = cells[i];
17090 cells[0].events.push(ev);
17092 this.calevents.push(ev);
17095 clearEvents: function() {
17097 if(!this.calevents){
17101 Roo.each(this.cells.elements, function(c){
17107 Roo.each(this.calevents, function(e) {
17108 Roo.each(e.els, function(el) {
17109 el.un('mouseenter' ,this.onEventEnter, this);
17110 el.un('mouseleave' ,this.onEventLeave, this);
17115 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17121 renderEvents: function()
17125 this.cells.each(function(c) {
17134 if(c.row != c.events.length){
17135 r = 4 - (4 - (c.row - c.events.length));
17138 c.events = ev.slice(0, r);
17139 c.more = ev.slice(r);
17141 if(c.more.length && c.more.length == 1){
17142 c.events.push(c.more.pop());
17145 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17149 this.cells.each(function(c) {
17151 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17154 for (var e = 0; e < c.events.length; e++){
17155 var ev = c.events[e];
17156 var rows = ev.rows;
17158 for(var i = 0; i < rows.length; i++) {
17160 // how many rows should it span..
17163 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17164 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17166 unselectable : "on",
17169 cls: 'fc-event-inner',
17173 // cls: 'fc-event-time',
17174 // html : cells.length > 1 ? '' : ev.time
17178 cls: 'fc-event-title',
17179 html : String.format('{0}', ev.title)
17186 cls: 'ui-resizable-handle ui-resizable-e',
17187 html : '  '
17194 cfg.cls += ' fc-event-start';
17196 if ((i+1) == rows.length) {
17197 cfg.cls += ' fc-event-end';
17200 var ctr = _this.el.select('.fc-event-container',true).first();
17201 var cg = ctr.createChild(cfg);
17203 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17204 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17206 var r = (c.more.length) ? 1 : 0;
17207 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17208 cg.setWidth(ebox.right - sbox.x -2);
17210 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17211 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17212 cg.on('click', _this.onEventClick, _this, ev);
17223 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17224 style : 'position: absolute',
17225 unselectable : "on",
17228 cls: 'fc-event-inner',
17232 cls: 'fc-event-title',
17240 cls: 'ui-resizable-handle ui-resizable-e',
17241 html : '  '
17247 var ctr = _this.el.select('.fc-event-container',true).first();
17248 var cg = ctr.createChild(cfg);
17250 var sbox = c.select('.fc-day-content',true).first().getBox();
17251 var ebox = c.select('.fc-day-content',true).first().getBox();
17253 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17254 cg.setWidth(ebox.right - sbox.x -2);
17256 cg.on('click', _this.onMoreEventClick, _this, c.more);
17266 onEventEnter: function (e, el,event,d) {
17267 this.fireEvent('evententer', this, el, event);
17270 onEventLeave: function (e, el,event,d) {
17271 this.fireEvent('eventleave', this, el, event);
17274 onEventClick: function (e, el,event,d) {
17275 this.fireEvent('eventclick', this, el, event);
17278 onMonthChange: function () {
17282 onMoreEventClick: function(e, el, more)
17286 this.calpopover.placement = 'right';
17287 this.calpopover.setTitle('More');
17289 this.calpopover.setContent('');
17291 var ctr = this.calpopover.el.select('.popover-content', true).first();
17293 Roo.each(more, function(m){
17295 cls : 'fc-event-hori fc-event-draggable',
17298 var cg = ctr.createChild(cfg);
17300 cg.on('click', _this.onEventClick, _this, m);
17303 this.calpopover.show(el);
17308 onLoad: function ()
17310 this.calevents = [];
17313 if(this.store.getCount() > 0){
17314 this.store.data.each(function(d){
17317 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17318 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17319 time : d.data.start_time,
17320 title : d.data.title,
17321 description : d.data.description,
17322 venue : d.data.venue
17327 this.renderEvents();
17329 if(this.calevents.length && this.loadMask){
17330 this.maskEl.hide();
17334 onBeforeLoad: function()
17336 this.clearEvents();
17338 this.maskEl.show();
17352 * @class Roo.bootstrap.Popover
17353 * @extends Roo.bootstrap.Component
17354 * Bootstrap Popover class
17355 * @cfg {String} html contents of the popover (or false to use children..)
17356 * @cfg {String} title of popover (or false to hide)
17357 * @cfg {String} placement how it is placed
17358 * @cfg {String} trigger click || hover (or false to trigger manually)
17359 * @cfg {String} over what (parent or false to trigger manually.)
17360 * @cfg {Number} delay - delay before showing
17363 * Create a new Popover
17364 * @param {Object} config The config object
17367 Roo.bootstrap.Popover = function(config){
17368 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17374 * After the popover show
17376 * @param {Roo.bootstrap.Popover} this
17381 * After the popover hide
17383 * @param {Roo.bootstrap.Popover} this
17389 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17391 title: 'Fill in a title',
17394 placement : 'right',
17395 trigger : 'hover', // hover
17401 can_build_overlaid : false,
17403 getChildContainer : function()
17405 return this.el.select('.popover-content',true).first();
17408 getAutoCreate : function(){
17411 cls : 'popover roo-dynamic',
17412 style: 'display:block',
17418 cls : 'popover-inner',
17422 cls: 'popover-title',
17426 cls : 'popover-content',
17437 setTitle: function(str)
17440 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17442 setContent: function(str)
17445 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17447 // as it get's added to the bottom of the page.
17448 onRender : function(ct, position)
17450 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17452 var cfg = Roo.apply({}, this.getAutoCreate());
17456 cfg.cls += ' ' + this.cls;
17459 cfg.style = this.style;
17461 //Roo.log("adding to ");
17462 this.el = Roo.get(document.body).createChild(cfg, position);
17463 // Roo.log(this.el);
17468 initEvents : function()
17470 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17471 this.el.enableDisplayMode('block');
17473 if (this.over === false) {
17476 if (this.triggers === false) {
17479 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17480 var triggers = this.trigger ? this.trigger.split(' ') : [];
17481 Roo.each(triggers, function(trigger) {
17483 if (trigger == 'click') {
17484 on_el.on('click', this.toggle, this);
17485 } else if (trigger != 'manual') {
17486 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17487 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17489 on_el.on(eventIn ,this.enter, this);
17490 on_el.on(eventOut, this.leave, this);
17501 toggle : function () {
17502 this.hoverState == 'in' ? this.leave() : this.enter();
17505 enter : function () {
17507 clearTimeout(this.timeout);
17509 this.hoverState = 'in';
17511 if (!this.delay || !this.delay.show) {
17516 this.timeout = setTimeout(function () {
17517 if (_t.hoverState == 'in') {
17520 }, this.delay.show)
17523 leave : function() {
17524 clearTimeout(this.timeout);
17526 this.hoverState = 'out';
17528 if (!this.delay || !this.delay.hide) {
17533 this.timeout = setTimeout(function () {
17534 if (_t.hoverState == 'out') {
17537 }, this.delay.hide)
17540 show : function (on_el)
17543 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17547 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17548 if (this.html !== false) {
17549 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17551 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17552 if (!this.title.length) {
17553 this.el.select('.popover-title',true).hide();
17556 var placement = typeof this.placement == 'function' ?
17557 this.placement.call(this, this.el, on_el) :
17560 var autoToken = /\s?auto?\s?/i;
17561 var autoPlace = autoToken.test(placement);
17563 placement = placement.replace(autoToken, '') || 'top';
17567 //this.el.setXY([0,0]);
17569 this.el.dom.style.display='block';
17570 this.el.addClass(placement);
17572 //this.el.appendTo(on_el);
17574 var p = this.getPosition();
17575 var box = this.el.getBox();
17580 var align = Roo.bootstrap.Popover.alignment[placement];
17583 this.el.alignTo(on_el, align[0],align[1]);
17584 //var arrow = this.el.select('.arrow',true).first();
17585 //arrow.set(align[2],
17587 this.el.addClass('in');
17590 if (this.el.hasClass('fade')) {
17594 this.hoverState = 'in';
17596 this.fireEvent('show', this);
17601 this.el.setXY([0,0]);
17602 this.el.removeClass('in');
17604 this.hoverState = null;
17606 this.fireEvent('hide', this);
17611 Roo.bootstrap.Popover.alignment = {
17612 'left' : ['r-l', [-10,0], 'right'],
17613 'right' : ['l-r', [10,0], 'left'],
17614 'bottom' : ['t-b', [0,10], 'top'],
17615 'top' : [ 'b-t', [0,-10], 'bottom']
17626 * @class Roo.bootstrap.Progress
17627 * @extends Roo.bootstrap.Component
17628 * Bootstrap Progress class
17629 * @cfg {Boolean} striped striped of the progress bar
17630 * @cfg {Boolean} active animated of the progress bar
17634 * Create a new Progress
17635 * @param {Object} config The config object
17638 Roo.bootstrap.Progress = function(config){
17639 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17642 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17647 getAutoCreate : function(){
17655 cfg.cls += ' progress-striped';
17659 cfg.cls += ' active';
17678 * @class Roo.bootstrap.ProgressBar
17679 * @extends Roo.bootstrap.Component
17680 * Bootstrap ProgressBar class
17681 * @cfg {Number} aria_valuenow aria-value now
17682 * @cfg {Number} aria_valuemin aria-value min
17683 * @cfg {Number} aria_valuemax aria-value max
17684 * @cfg {String} label label for the progress bar
17685 * @cfg {String} panel (success | info | warning | danger )
17686 * @cfg {String} role role of the progress bar
17687 * @cfg {String} sr_only text
17691 * Create a new ProgressBar
17692 * @param {Object} config The config object
17695 Roo.bootstrap.ProgressBar = function(config){
17696 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17699 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17703 aria_valuemax : 100,
17709 getAutoCreate : function()
17714 cls: 'progress-bar',
17715 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17727 cfg.role = this.role;
17730 if(this.aria_valuenow){
17731 cfg['aria-valuenow'] = this.aria_valuenow;
17734 if(this.aria_valuemin){
17735 cfg['aria-valuemin'] = this.aria_valuemin;
17738 if(this.aria_valuemax){
17739 cfg['aria-valuemax'] = this.aria_valuemax;
17742 if(this.label && !this.sr_only){
17743 cfg.html = this.label;
17747 cfg.cls += ' progress-bar-' + this.panel;
17753 update : function(aria_valuenow)
17755 this.aria_valuenow = aria_valuenow;
17757 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17772 * @class Roo.bootstrap.TabGroup
17773 * @extends Roo.bootstrap.Column
17774 * Bootstrap Column class
17775 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17776 * @cfg {Boolean} carousel true to make the group behave like a carousel
17777 * @cfg {Boolean} bullets show bullets for the panels
17778 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17779 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17780 * @cfg {Boolean} showarrow (true|false) show arrow default true
17783 * Create a new TabGroup
17784 * @param {Object} config The config object
17787 Roo.bootstrap.TabGroup = function(config){
17788 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17790 this.navId = Roo.id();
17793 Roo.bootstrap.TabGroup.register(this);
17797 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17800 transition : false,
17805 slideOnTouch : false,
17808 getAutoCreate : function()
17810 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17812 cfg.cls += ' tab-content';
17814 if (this.carousel) {
17815 cfg.cls += ' carousel slide';
17818 cls : 'carousel-inner',
17822 if(this.bullets && !Roo.isTouch){
17825 cls : 'carousel-bullets',
17829 if(this.bullets_cls){
17830 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17837 cfg.cn[0].cn.push(bullets);
17840 if(this.showarrow){
17841 cfg.cn[0].cn.push({
17843 class : 'carousel-arrow',
17847 class : 'carousel-prev',
17851 class : 'fa fa-chevron-left'
17857 class : 'carousel-next',
17861 class : 'fa fa-chevron-right'
17874 initEvents: function()
17876 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17877 // this.el.on("touchstart", this.onTouchStart, this);
17880 if(this.autoslide){
17883 this.slideFn = window.setInterval(function() {
17884 _this.showPanelNext();
17888 if(this.showarrow){
17889 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17890 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17896 // onTouchStart : function(e, el, o)
17898 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17902 // this.showPanelNext();
17906 getChildContainer : function()
17908 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17912 * register a Navigation item
17913 * @param {Roo.bootstrap.NavItem} the navitem to add
17915 register : function(item)
17917 this.tabs.push( item);
17918 item.navId = this.navId; // not really needed..
17923 getActivePanel : function()
17926 Roo.each(this.tabs, function(t) {
17936 getPanelByName : function(n)
17939 Roo.each(this.tabs, function(t) {
17940 if (t.tabId == n) {
17948 indexOfPanel : function(p)
17951 Roo.each(this.tabs, function(t,i) {
17952 if (t.tabId == p.tabId) {
17961 * show a specific panel
17962 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17963 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17965 showPanel : function (pan)
17967 if(this.transition || typeof(pan) == 'undefined'){
17968 Roo.log("waiting for the transitionend");
17972 if (typeof(pan) == 'number') {
17973 pan = this.tabs[pan];
17976 if (typeof(pan) == 'string') {
17977 pan = this.getPanelByName(pan);
17980 var cur = this.getActivePanel();
17983 Roo.log('pan or acitve pan is undefined');
17987 if (pan.tabId == this.getActivePanel().tabId) {
17991 if (false === cur.fireEvent('beforedeactivate')) {
17995 if(this.bullets > 0 && !Roo.isTouch){
17996 this.setActiveBullet(this.indexOfPanel(pan));
17999 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18001 this.transition = true;
18002 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18003 var lr = dir == 'next' ? 'left' : 'right';
18004 pan.el.addClass(dir); // or prev
18005 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18006 cur.el.addClass(lr); // or right
18007 pan.el.addClass(lr);
18010 cur.el.on('transitionend', function() {
18011 Roo.log("trans end?");
18013 pan.el.removeClass([lr,dir]);
18014 pan.setActive(true);
18016 cur.el.removeClass([lr]);
18017 cur.setActive(false);
18019 _this.transition = false;
18021 }, this, { single: true } );
18026 cur.setActive(false);
18027 pan.setActive(true);
18032 showPanelNext : function()
18034 var i = this.indexOfPanel(this.getActivePanel());
18036 if (i >= this.tabs.length - 1 && !this.autoslide) {
18040 if (i >= this.tabs.length - 1 && this.autoslide) {
18044 this.showPanel(this.tabs[i+1]);
18047 showPanelPrev : function()
18049 var i = this.indexOfPanel(this.getActivePanel());
18051 if (i < 1 && !this.autoslide) {
18055 if (i < 1 && this.autoslide) {
18056 i = this.tabs.length;
18059 this.showPanel(this.tabs[i-1]);
18063 addBullet: function()
18065 if(!this.bullets || Roo.isTouch){
18068 var ctr = this.el.select('.carousel-bullets',true).first();
18069 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18070 var bullet = ctr.createChild({
18071 cls : 'bullet bullet-' + i
18072 },ctr.dom.lastChild);
18077 bullet.on('click', (function(e, el, o, ii, t){
18079 e.preventDefault();
18081 this.showPanel(ii);
18083 if(this.autoslide && this.slideFn){
18084 clearInterval(this.slideFn);
18085 this.slideFn = window.setInterval(function() {
18086 _this.showPanelNext();
18090 }).createDelegate(this, [i, bullet], true));
18095 setActiveBullet : function(i)
18101 Roo.each(this.el.select('.bullet', true).elements, function(el){
18102 el.removeClass('selected');
18105 var bullet = this.el.select('.bullet-' + i, true).first();
18111 bullet.addClass('selected');
18122 Roo.apply(Roo.bootstrap.TabGroup, {
18126 * register a Navigation Group
18127 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18129 register : function(navgrp)
18131 this.groups[navgrp.navId] = navgrp;
18135 * fetch a Navigation Group based on the navigation ID
18136 * if one does not exist , it will get created.
18137 * @param {string} the navgroup to add
18138 * @returns {Roo.bootstrap.NavGroup} the navgroup
18140 get: function(navId) {
18141 if (typeof(this.groups[navId]) == 'undefined') {
18142 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18144 return this.groups[navId] ;
18159 * @class Roo.bootstrap.TabPanel
18160 * @extends Roo.bootstrap.Component
18161 * Bootstrap TabPanel class
18162 * @cfg {Boolean} active panel active
18163 * @cfg {String} html panel content
18164 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18165 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18166 * @cfg {String} href click to link..
18170 * Create a new TabPanel
18171 * @param {Object} config The config object
18174 Roo.bootstrap.TabPanel = function(config){
18175 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18179 * Fires when the active status changes
18180 * @param {Roo.bootstrap.TabPanel} this
18181 * @param {Boolean} state the new state
18186 * @event beforedeactivate
18187 * Fires before a tab is de-activated - can be used to do validation on a form.
18188 * @param {Roo.bootstrap.TabPanel} this
18189 * @return {Boolean} false if there is an error
18192 'beforedeactivate': true
18195 this.tabId = this.tabId || Roo.id();
18199 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18207 getAutoCreate : function(){
18210 // item is needed for carousel - not sure if it has any effect otherwise
18211 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18212 html: this.html || ''
18216 cfg.cls += ' active';
18220 cfg.tabId = this.tabId;
18227 initEvents: function()
18229 var p = this.parent();
18231 this.navId = this.navId || p.navId;
18233 if (typeof(this.navId) != 'undefined') {
18234 // not really needed.. but just in case.. parent should be a NavGroup.
18235 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18239 var i = tg.tabs.length - 1;
18241 if(this.active && tg.bullets > 0 && i < tg.bullets){
18242 tg.setActiveBullet(i);
18246 this.el.on('click', this.onClick, this);
18249 this.el.on("touchstart", this.onTouchStart, this);
18250 this.el.on("touchmove", this.onTouchMove, this);
18251 this.el.on("touchend", this.onTouchEnd, this);
18256 onRender : function(ct, position)
18258 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18261 setActive : function(state)
18263 Roo.log("panel - set active " + this.tabId + "=" + state);
18265 this.active = state;
18267 this.el.removeClass('active');
18269 } else if (!this.el.hasClass('active')) {
18270 this.el.addClass('active');
18273 this.fireEvent('changed', this, state);
18276 onClick : function(e)
18278 e.preventDefault();
18280 if(!this.href.length){
18284 window.location.href = this.href;
18293 onTouchStart : function(e)
18295 this.swiping = false;
18297 this.startX = e.browserEvent.touches[0].clientX;
18298 this.startY = e.browserEvent.touches[0].clientY;
18301 onTouchMove : function(e)
18303 this.swiping = true;
18305 this.endX = e.browserEvent.touches[0].clientX;
18306 this.endY = e.browserEvent.touches[0].clientY;
18309 onTouchEnd : function(e)
18316 var tabGroup = this.parent();
18318 if(this.endX > this.startX){ // swiping right
18319 tabGroup.showPanelPrev();
18323 if(this.startX > this.endX){ // swiping left
18324 tabGroup.showPanelNext();
18343 * @class Roo.bootstrap.DateField
18344 * @extends Roo.bootstrap.Input
18345 * Bootstrap DateField class
18346 * @cfg {Number} weekStart default 0
18347 * @cfg {String} viewMode default empty, (months|years)
18348 * @cfg {String} minViewMode default empty, (months|years)
18349 * @cfg {Number} startDate default -Infinity
18350 * @cfg {Number} endDate default Infinity
18351 * @cfg {Boolean} todayHighlight default false
18352 * @cfg {Boolean} todayBtn default false
18353 * @cfg {Boolean} calendarWeeks default false
18354 * @cfg {Object} daysOfWeekDisabled default empty
18355 * @cfg {Boolean} singleMode default false (true | false)
18357 * @cfg {Boolean} keyboardNavigation default true
18358 * @cfg {String} language default en
18361 * Create a new DateField
18362 * @param {Object} config The config object
18365 Roo.bootstrap.DateField = function(config){
18366 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18370 * Fires when this field show.
18371 * @param {Roo.bootstrap.DateField} this
18372 * @param {Mixed} date The date value
18377 * Fires when this field hide.
18378 * @param {Roo.bootstrap.DateField} this
18379 * @param {Mixed} date The date value
18384 * Fires when select a date.
18385 * @param {Roo.bootstrap.DateField} this
18386 * @param {Mixed} date The date value
18390 * @event beforeselect
18391 * Fires when before select a date.
18392 * @param {Roo.bootstrap.DateField} this
18393 * @param {Mixed} date The date value
18395 beforeselect : true
18399 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18402 * @cfg {String} format
18403 * The default date format string which can be overriden for localization support. The format must be
18404 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18408 * @cfg {String} altFormats
18409 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18410 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18412 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18420 todayHighlight : false,
18426 keyboardNavigation: true,
18428 calendarWeeks: false,
18430 startDate: -Infinity,
18434 daysOfWeekDisabled: [],
18438 singleMode : false,
18440 UTCDate: function()
18442 return new Date(Date.UTC.apply(Date, arguments));
18445 UTCToday: function()
18447 var today = new Date();
18448 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18451 getDate: function() {
18452 var d = this.getUTCDate();
18453 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18456 getUTCDate: function() {
18460 setDate: function(d) {
18461 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18464 setUTCDate: function(d) {
18466 this.setValue(this.formatDate(this.date));
18469 onRender: function(ct, position)
18472 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18474 this.language = this.language || 'en';
18475 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18476 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18478 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18479 this.format = this.format || 'm/d/y';
18480 this.isInline = false;
18481 this.isInput = true;
18482 this.component = this.el.select('.add-on', true).first() || false;
18483 this.component = (this.component && this.component.length === 0) ? false : this.component;
18484 this.hasInput = this.component && this.inputEl().length;
18486 if (typeof(this.minViewMode === 'string')) {
18487 switch (this.minViewMode) {
18489 this.minViewMode = 1;
18492 this.minViewMode = 2;
18495 this.minViewMode = 0;
18500 if (typeof(this.viewMode === 'string')) {
18501 switch (this.viewMode) {
18514 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18516 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18518 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18520 this.picker().on('mousedown', this.onMousedown, this);
18521 this.picker().on('click', this.onClick, this);
18523 this.picker().addClass('datepicker-dropdown');
18525 this.startViewMode = this.viewMode;
18527 if(this.singleMode){
18528 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18529 v.setVisibilityMode(Roo.Element.DISPLAY);
18533 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18534 v.setStyle('width', '189px');
18538 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18539 if(!this.calendarWeeks){
18544 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18545 v.attr('colspan', function(i, val){
18546 return parseInt(val) + 1;
18551 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18553 this.setStartDate(this.startDate);
18554 this.setEndDate(this.endDate);
18556 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18563 if(this.isInline) {
18568 picker : function()
18570 return this.pickerEl;
18571 // return this.el.select('.datepicker', true).first();
18574 fillDow: function()
18576 var dowCnt = this.weekStart;
18585 if(this.calendarWeeks){
18593 while (dowCnt < this.weekStart + 7) {
18597 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18601 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18604 fillMonths: function()
18607 var months = this.picker().select('>.datepicker-months td', true).first();
18609 months.dom.innerHTML = '';
18615 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18618 months.createChild(month);
18625 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;
18627 if (this.date < this.startDate) {
18628 this.viewDate = new Date(this.startDate);
18629 } else if (this.date > this.endDate) {
18630 this.viewDate = new Date(this.endDate);
18632 this.viewDate = new Date(this.date);
18640 var d = new Date(this.viewDate),
18641 year = d.getUTCFullYear(),
18642 month = d.getUTCMonth(),
18643 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18644 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18645 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18646 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18647 currentDate = this.date && this.date.valueOf(),
18648 today = this.UTCToday();
18650 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18652 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18654 // this.picker.select('>tfoot th.today').
18655 // .text(dates[this.language].today)
18656 // .toggle(this.todayBtn !== false);
18658 this.updateNavArrows();
18661 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18663 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18665 prevMonth.setUTCDate(day);
18667 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18669 var nextMonth = new Date(prevMonth);
18671 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18673 nextMonth = nextMonth.valueOf();
18675 var fillMonths = false;
18677 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18679 while(prevMonth.valueOf() <= nextMonth) {
18682 if (prevMonth.getUTCDay() === this.weekStart) {
18684 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18692 if(this.calendarWeeks){
18693 // ISO 8601: First week contains first thursday.
18694 // ISO also states week starts on Monday, but we can be more abstract here.
18696 // Start of current week: based on weekstart/current date
18697 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18698 // Thursday of this week
18699 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18700 // First Thursday of year, year from thursday
18701 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18702 // Calendar week: ms between thursdays, div ms per day, div 7 days
18703 calWeek = (th - yth) / 864e5 / 7 + 1;
18705 fillMonths.cn.push({
18713 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18715 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18718 if (this.todayHighlight &&
18719 prevMonth.getUTCFullYear() == today.getFullYear() &&
18720 prevMonth.getUTCMonth() == today.getMonth() &&
18721 prevMonth.getUTCDate() == today.getDate()) {
18722 clsName += ' today';
18725 if (currentDate && prevMonth.valueOf() === currentDate) {
18726 clsName += ' active';
18729 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18730 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18731 clsName += ' disabled';
18734 fillMonths.cn.push({
18736 cls: 'day ' + clsName,
18737 html: prevMonth.getDate()
18740 prevMonth.setDate(prevMonth.getDate()+1);
18743 var currentYear = this.date && this.date.getUTCFullYear();
18744 var currentMonth = this.date && this.date.getUTCMonth();
18746 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18748 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18749 v.removeClass('active');
18751 if(currentYear === year && k === currentMonth){
18752 v.addClass('active');
18755 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18756 v.addClass('disabled');
18762 year = parseInt(year/10, 10) * 10;
18764 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18766 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18769 for (var i = -1; i < 11; i++) {
18770 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18772 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18780 showMode: function(dir)
18783 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18786 Roo.each(this.picker().select('>div',true).elements, function(v){
18787 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18790 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18795 if(this.isInline) {
18799 this.picker().removeClass(['bottom', 'top']);
18801 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18803 * place to the top of element!
18807 this.picker().addClass('top');
18808 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18813 this.picker().addClass('bottom');
18815 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18818 parseDate : function(value)
18820 if(!value || value instanceof Date){
18823 var v = Date.parseDate(value, this.format);
18824 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18825 v = Date.parseDate(value, 'Y-m-d');
18827 if(!v && this.altFormats){
18828 if(!this.altFormatsArray){
18829 this.altFormatsArray = this.altFormats.split("|");
18831 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18832 v = Date.parseDate(value, this.altFormatsArray[i]);
18838 formatDate : function(date, fmt)
18840 return (!date || !(date instanceof Date)) ?
18841 date : date.dateFormat(fmt || this.format);
18844 onFocus : function()
18846 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18850 onBlur : function()
18852 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18854 var d = this.inputEl().getValue();
18861 showPopup : function()
18863 this.picker().show();
18867 this.fireEvent('showpopup', this, this.date);
18870 hidePopup : function()
18872 if(this.isInline) {
18875 this.picker().hide();
18876 this.viewMode = this.startViewMode;
18879 this.fireEvent('hidepopup', this, this.date);
18883 onMousedown: function(e)
18885 e.stopPropagation();
18886 e.preventDefault();
18891 Roo.bootstrap.DateField.superclass.keyup.call(this);
18895 setValue: function(v)
18897 if(this.fireEvent('beforeselect', this, v) !== false){
18898 var d = new Date(this.parseDate(v) ).clearTime();
18900 if(isNaN(d.getTime())){
18901 this.date = this.viewDate = '';
18902 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18906 v = this.formatDate(d);
18908 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18910 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18914 this.fireEvent('select', this, this.date);
18918 getValue: function()
18920 return this.formatDate(this.date);
18923 fireKey: function(e)
18925 if (!this.picker().isVisible()){
18926 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18932 var dateChanged = false,
18934 newDate, newViewDate;
18939 e.preventDefault();
18943 if (!this.keyboardNavigation) {
18946 dir = e.keyCode == 37 ? -1 : 1;
18949 newDate = this.moveYear(this.date, dir);
18950 newViewDate = this.moveYear(this.viewDate, dir);
18951 } else if (e.shiftKey){
18952 newDate = this.moveMonth(this.date, dir);
18953 newViewDate = this.moveMonth(this.viewDate, dir);
18955 newDate = new Date(this.date);
18956 newDate.setUTCDate(this.date.getUTCDate() + dir);
18957 newViewDate = new Date(this.viewDate);
18958 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18960 if (this.dateWithinRange(newDate)){
18961 this.date = newDate;
18962 this.viewDate = newViewDate;
18963 this.setValue(this.formatDate(this.date));
18965 e.preventDefault();
18966 dateChanged = true;
18971 if (!this.keyboardNavigation) {
18974 dir = e.keyCode == 38 ? -1 : 1;
18976 newDate = this.moveYear(this.date, dir);
18977 newViewDate = this.moveYear(this.viewDate, dir);
18978 } else if (e.shiftKey){
18979 newDate = this.moveMonth(this.date, dir);
18980 newViewDate = this.moveMonth(this.viewDate, dir);
18982 newDate = new Date(this.date);
18983 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18984 newViewDate = new Date(this.viewDate);
18985 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18987 if (this.dateWithinRange(newDate)){
18988 this.date = newDate;
18989 this.viewDate = newViewDate;
18990 this.setValue(this.formatDate(this.date));
18992 e.preventDefault();
18993 dateChanged = true;
18997 this.setValue(this.formatDate(this.date));
18999 e.preventDefault();
19002 this.setValue(this.formatDate(this.date));
19016 onClick: function(e)
19018 e.stopPropagation();
19019 e.preventDefault();
19021 var target = e.getTarget();
19023 if(target.nodeName.toLowerCase() === 'i'){
19024 target = Roo.get(target).dom.parentNode;
19027 var nodeName = target.nodeName;
19028 var className = target.className;
19029 var html = target.innerHTML;
19030 //Roo.log(nodeName);
19032 switch(nodeName.toLowerCase()) {
19034 switch(className) {
19040 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19041 switch(this.viewMode){
19043 this.viewDate = this.moveMonth(this.viewDate, dir);
19047 this.viewDate = this.moveYear(this.viewDate, dir);
19053 var date = new Date();
19054 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19056 this.setValue(this.formatDate(this.date));
19063 if (className.indexOf('disabled') < 0) {
19064 this.viewDate.setUTCDate(1);
19065 if (className.indexOf('month') > -1) {
19066 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19068 var year = parseInt(html, 10) || 0;
19069 this.viewDate.setUTCFullYear(year);
19073 if(this.singleMode){
19074 this.setValue(this.formatDate(this.viewDate));
19085 //Roo.log(className);
19086 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19087 var day = parseInt(html, 10) || 1;
19088 var year = this.viewDate.getUTCFullYear(),
19089 month = this.viewDate.getUTCMonth();
19091 if (className.indexOf('old') > -1) {
19098 } else if (className.indexOf('new') > -1) {
19106 //Roo.log([year,month,day]);
19107 this.date = this.UTCDate(year, month, day,0,0,0,0);
19108 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19110 //Roo.log(this.formatDate(this.date));
19111 this.setValue(this.formatDate(this.date));
19118 setStartDate: function(startDate)
19120 this.startDate = startDate || -Infinity;
19121 if (this.startDate !== -Infinity) {
19122 this.startDate = this.parseDate(this.startDate);
19125 this.updateNavArrows();
19128 setEndDate: function(endDate)
19130 this.endDate = endDate || Infinity;
19131 if (this.endDate !== Infinity) {
19132 this.endDate = this.parseDate(this.endDate);
19135 this.updateNavArrows();
19138 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19140 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19141 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19142 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19144 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19145 return parseInt(d, 10);
19148 this.updateNavArrows();
19151 updateNavArrows: function()
19153 if(this.singleMode){
19157 var d = new Date(this.viewDate),
19158 year = d.getUTCFullYear(),
19159 month = d.getUTCMonth();
19161 Roo.each(this.picker().select('.prev', true).elements, function(v){
19163 switch (this.viewMode) {
19166 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19172 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19179 Roo.each(this.picker().select('.next', true).elements, function(v){
19181 switch (this.viewMode) {
19184 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19190 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19198 moveMonth: function(date, dir)
19203 var new_date = new Date(date.valueOf()),
19204 day = new_date.getUTCDate(),
19205 month = new_date.getUTCMonth(),
19206 mag = Math.abs(dir),
19208 dir = dir > 0 ? 1 : -1;
19211 // If going back one month, make sure month is not current month
19212 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19214 return new_date.getUTCMonth() == month;
19216 // If going forward one month, make sure month is as expected
19217 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19219 return new_date.getUTCMonth() != new_month;
19221 new_month = month + dir;
19222 new_date.setUTCMonth(new_month);
19223 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19224 if (new_month < 0 || new_month > 11) {
19225 new_month = (new_month + 12) % 12;
19228 // For magnitudes >1, move one month at a time...
19229 for (var i=0; i<mag; i++) {
19230 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19231 new_date = this.moveMonth(new_date, dir);
19233 // ...then reset the day, keeping it in the new month
19234 new_month = new_date.getUTCMonth();
19235 new_date.setUTCDate(day);
19237 return new_month != new_date.getUTCMonth();
19240 // Common date-resetting loop -- if date is beyond end of month, make it
19243 new_date.setUTCDate(--day);
19244 new_date.setUTCMonth(new_month);
19249 moveYear: function(date, dir)
19251 return this.moveMonth(date, dir*12);
19254 dateWithinRange: function(date)
19256 return date >= this.startDate && date <= this.endDate;
19262 this.picker().remove();
19265 validateValue : function(value)
19267 if(this.getVisibilityEl().hasClass('hidden')){
19271 if(value.length < 1) {
19272 if(this.allowBlank){
19278 if(value.length < this.minLength){
19281 if(value.length > this.maxLength){
19285 var vt = Roo.form.VTypes;
19286 if(!vt[this.vtype](value, this)){
19290 if(typeof this.validator == "function"){
19291 var msg = this.validator(value);
19297 if(this.regex && !this.regex.test(value)){
19301 if(typeof(this.parseDate(value)) == 'undefined'){
19305 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19309 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19319 this.date = this.viewDate = '';
19321 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19326 Roo.apply(Roo.bootstrap.DateField, {
19337 html: '<i class="fa fa-arrow-left"/>'
19347 html: '<i class="fa fa-arrow-right"/>'
19389 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19390 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19391 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19392 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19393 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19406 navFnc: 'FullYear',
19411 navFnc: 'FullYear',
19416 Roo.apply(Roo.bootstrap.DateField, {
19420 cls: 'datepicker dropdown-menu roo-dynamic',
19424 cls: 'datepicker-days',
19428 cls: 'table-condensed',
19430 Roo.bootstrap.DateField.head,
19434 Roo.bootstrap.DateField.footer
19441 cls: 'datepicker-months',
19445 cls: 'table-condensed',
19447 Roo.bootstrap.DateField.head,
19448 Roo.bootstrap.DateField.content,
19449 Roo.bootstrap.DateField.footer
19456 cls: 'datepicker-years',
19460 cls: 'table-condensed',
19462 Roo.bootstrap.DateField.head,
19463 Roo.bootstrap.DateField.content,
19464 Roo.bootstrap.DateField.footer
19483 * @class Roo.bootstrap.TimeField
19484 * @extends Roo.bootstrap.Input
19485 * Bootstrap DateField class
19489 * Create a new TimeField
19490 * @param {Object} config The config object
19493 Roo.bootstrap.TimeField = function(config){
19494 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19498 * Fires when this field show.
19499 * @param {Roo.bootstrap.DateField} thisthis
19500 * @param {Mixed} date The date value
19505 * Fires when this field hide.
19506 * @param {Roo.bootstrap.DateField} this
19507 * @param {Mixed} date The date value
19512 * Fires when select a date.
19513 * @param {Roo.bootstrap.DateField} this
19514 * @param {Mixed} date The date value
19520 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19523 * @cfg {String} format
19524 * The default time format string which can be overriden for localization support. The format must be
19525 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19529 onRender: function(ct, position)
19532 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19534 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19536 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19538 this.pop = this.picker().select('>.datepicker-time',true).first();
19539 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19541 this.picker().on('mousedown', this.onMousedown, this);
19542 this.picker().on('click', this.onClick, this);
19544 this.picker().addClass('datepicker-dropdown');
19549 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19550 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19551 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19552 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19553 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19554 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19558 fireKey: function(e){
19559 if (!this.picker().isVisible()){
19560 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19566 e.preventDefault();
19574 this.onTogglePeriod();
19577 this.onIncrementMinutes();
19580 this.onDecrementMinutes();
19589 onClick: function(e) {
19590 e.stopPropagation();
19591 e.preventDefault();
19594 picker : function()
19596 return this.el.select('.datepicker', true).first();
19599 fillTime: function()
19601 var time = this.pop.select('tbody', true).first();
19603 time.dom.innerHTML = '';
19618 cls: 'hours-up glyphicon glyphicon-chevron-up'
19638 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19659 cls: 'timepicker-hour',
19674 cls: 'timepicker-minute',
19689 cls: 'btn btn-primary period',
19711 cls: 'hours-down glyphicon glyphicon-chevron-down'
19731 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19749 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19756 var hours = this.time.getHours();
19757 var minutes = this.time.getMinutes();
19770 hours = hours - 12;
19774 hours = '0' + hours;
19778 minutes = '0' + minutes;
19781 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19782 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19783 this.pop.select('button', true).first().dom.innerHTML = period;
19789 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19791 var cls = ['bottom'];
19793 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19800 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19805 this.picker().addClass(cls.join('-'));
19809 Roo.each(cls, function(c){
19811 _this.picker().setTop(_this.inputEl().getHeight());
19815 _this.picker().setTop(0 - _this.picker().getHeight());
19820 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19824 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19831 onFocus : function()
19833 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19837 onBlur : function()
19839 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19845 this.picker().show();
19850 this.fireEvent('show', this, this.date);
19855 this.picker().hide();
19858 this.fireEvent('hide', this, this.date);
19861 setTime : function()
19864 this.setValue(this.time.format(this.format));
19866 this.fireEvent('select', this, this.date);
19871 onMousedown: function(e){
19872 e.stopPropagation();
19873 e.preventDefault();
19876 onIncrementHours: function()
19878 Roo.log('onIncrementHours');
19879 this.time = this.time.add(Date.HOUR, 1);
19884 onDecrementHours: function()
19886 Roo.log('onDecrementHours');
19887 this.time = this.time.add(Date.HOUR, -1);
19891 onIncrementMinutes: function()
19893 Roo.log('onIncrementMinutes');
19894 this.time = this.time.add(Date.MINUTE, 1);
19898 onDecrementMinutes: function()
19900 Roo.log('onDecrementMinutes');
19901 this.time = this.time.add(Date.MINUTE, -1);
19905 onTogglePeriod: function()
19907 Roo.log('onTogglePeriod');
19908 this.time = this.time.add(Date.HOUR, 12);
19915 Roo.apply(Roo.bootstrap.TimeField, {
19945 cls: 'btn btn-info ok',
19957 Roo.apply(Roo.bootstrap.TimeField, {
19961 cls: 'datepicker dropdown-menu',
19965 cls: 'datepicker-time',
19969 cls: 'table-condensed',
19971 Roo.bootstrap.TimeField.content,
19972 Roo.bootstrap.TimeField.footer
19991 * @class Roo.bootstrap.MonthField
19992 * @extends Roo.bootstrap.Input
19993 * Bootstrap MonthField class
19995 * @cfg {String} language default en
19998 * Create a new MonthField
19999 * @param {Object} config The config object
20002 Roo.bootstrap.MonthField = function(config){
20003 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20008 * Fires when this field show.
20009 * @param {Roo.bootstrap.MonthField} this
20010 * @param {Mixed} date The date value
20015 * Fires when this field hide.
20016 * @param {Roo.bootstrap.MonthField} this
20017 * @param {Mixed} date The date value
20022 * Fires when select a date.
20023 * @param {Roo.bootstrap.MonthField} this
20024 * @param {String} oldvalue The old value
20025 * @param {String} newvalue The new value
20031 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20033 onRender: function(ct, position)
20036 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20038 this.language = this.language || 'en';
20039 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20040 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20042 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20043 this.isInline = false;
20044 this.isInput = true;
20045 this.component = this.el.select('.add-on', true).first() || false;
20046 this.component = (this.component && this.component.length === 0) ? false : this.component;
20047 this.hasInput = this.component && this.inputEL().length;
20049 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20051 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20053 this.picker().on('mousedown', this.onMousedown, this);
20054 this.picker().on('click', this.onClick, this);
20056 this.picker().addClass('datepicker-dropdown');
20058 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20059 v.setStyle('width', '189px');
20066 if(this.isInline) {
20072 setValue: function(v, suppressEvent)
20074 var o = this.getValue();
20076 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20080 if(suppressEvent !== true){
20081 this.fireEvent('select', this, o, v);
20086 getValue: function()
20091 onClick: function(e)
20093 e.stopPropagation();
20094 e.preventDefault();
20096 var target = e.getTarget();
20098 if(target.nodeName.toLowerCase() === 'i'){
20099 target = Roo.get(target).dom.parentNode;
20102 var nodeName = target.nodeName;
20103 var className = target.className;
20104 var html = target.innerHTML;
20106 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20110 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20112 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20118 picker : function()
20120 return this.pickerEl;
20123 fillMonths: function()
20126 var months = this.picker().select('>.datepicker-months td', true).first();
20128 months.dom.innerHTML = '';
20134 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20137 months.createChild(month);
20146 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20147 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20150 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20151 e.removeClass('active');
20153 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20154 e.addClass('active');
20161 if(this.isInline) {
20165 this.picker().removeClass(['bottom', 'top']);
20167 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20169 * place to the top of element!
20173 this.picker().addClass('top');
20174 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20179 this.picker().addClass('bottom');
20181 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20184 onFocus : function()
20186 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20190 onBlur : function()
20192 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20194 var d = this.inputEl().getValue();
20203 this.picker().show();
20204 this.picker().select('>.datepicker-months', true).first().show();
20208 this.fireEvent('show', this, this.date);
20213 if(this.isInline) {
20216 this.picker().hide();
20217 this.fireEvent('hide', this, this.date);
20221 onMousedown: function(e)
20223 e.stopPropagation();
20224 e.preventDefault();
20229 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20233 fireKey: function(e)
20235 if (!this.picker().isVisible()){
20236 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20247 e.preventDefault();
20251 dir = e.keyCode == 37 ? -1 : 1;
20253 this.vIndex = this.vIndex + dir;
20255 if(this.vIndex < 0){
20259 if(this.vIndex > 11){
20263 if(isNaN(this.vIndex)){
20267 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20273 dir = e.keyCode == 38 ? -1 : 1;
20275 this.vIndex = this.vIndex + dir * 4;
20277 if(this.vIndex < 0){
20281 if(this.vIndex > 11){
20285 if(isNaN(this.vIndex)){
20289 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20294 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20295 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20299 e.preventDefault();
20302 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20303 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20319 this.picker().remove();
20324 Roo.apply(Roo.bootstrap.MonthField, {
20343 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20344 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20349 Roo.apply(Roo.bootstrap.MonthField, {
20353 cls: 'datepicker dropdown-menu roo-dynamic',
20357 cls: 'datepicker-months',
20361 cls: 'table-condensed',
20363 Roo.bootstrap.DateField.content
20383 * @class Roo.bootstrap.CheckBox
20384 * @extends Roo.bootstrap.Input
20385 * Bootstrap CheckBox class
20387 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20388 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20389 * @cfg {String} boxLabel The text that appears beside the checkbox
20390 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20391 * @cfg {Boolean} checked initnal the element
20392 * @cfg {Boolean} inline inline the element (default false)
20393 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20394 * @cfg {String} tooltip label tooltip
20397 * Create a new CheckBox
20398 * @param {Object} config The config object
20401 Roo.bootstrap.CheckBox = function(config){
20402 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20407 * Fires when the element is checked or unchecked.
20408 * @param {Roo.bootstrap.CheckBox} this This input
20409 * @param {Boolean} checked The new checked value
20414 * Fires when the element is click.
20415 * @param {Roo.bootstrap.CheckBox} this This input
20422 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20424 inputType: 'checkbox',
20433 getAutoCreate : function()
20435 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20441 cfg.cls = 'form-group ' + this.inputType; //input-group
20444 cfg.cls += ' ' + this.inputType + '-inline';
20450 type : this.inputType,
20451 value : this.inputValue,
20452 cls : 'roo-' + this.inputType, //'form-box',
20453 placeholder : this.placeholder || ''
20457 if(this.inputType != 'radio'){
20461 cls : 'roo-hidden-value',
20462 value : this.checked ? this.inputValue : this.valueOff
20467 if (this.weight) { // Validity check?
20468 cfg.cls += " " + this.inputType + "-" + this.weight;
20471 if (this.disabled) {
20472 input.disabled=true;
20476 input.checked = this.checked;
20481 input.name = this.name;
20483 if(this.inputType != 'radio'){
20484 hidden.name = this.name;
20485 input.name = '_hidden_' + this.name;
20490 input.cls += ' input-' + this.size;
20495 ['xs','sm','md','lg'].map(function(size){
20496 if (settings[size]) {
20497 cfg.cls += ' col-' + size + '-' + settings[size];
20501 var inputblock = input;
20503 if (this.before || this.after) {
20506 cls : 'input-group',
20511 inputblock.cn.push({
20513 cls : 'input-group-addon',
20518 inputblock.cn.push(input);
20520 if(this.inputType != 'radio'){
20521 inputblock.cn.push(hidden);
20525 inputblock.cn.push({
20527 cls : 'input-group-addon',
20534 if (align ==='left' && this.fieldLabel.length) {
20535 // Roo.log("left and has label");
20540 cls : 'control-label',
20541 html : this.fieldLabel
20551 if(this.labelWidth > 12){
20552 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20555 if(this.labelWidth < 13 && this.labelmd == 0){
20556 this.labelmd = this.labelWidth;
20559 if(this.labellg > 0){
20560 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20561 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20564 if(this.labelmd > 0){
20565 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20566 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20569 if(this.labelsm > 0){
20570 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20571 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20574 if(this.labelxs > 0){
20575 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20576 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20579 } else if ( this.fieldLabel.length) {
20580 // Roo.log(" label");
20584 tag: this.boxLabel ? 'span' : 'label',
20586 cls: 'control-label box-input-label',
20587 //cls : 'input-group-addon',
20588 html : this.fieldLabel
20597 // Roo.log(" no label && no align");
20598 cfg.cn = [ inputblock ] ;
20604 var boxLabelCfg = {
20606 //'for': id, // box label is handled by onclick - so no for...
20608 html: this.boxLabel
20612 boxLabelCfg.tooltip = this.tooltip;
20615 cfg.cn.push(boxLabelCfg);
20618 if(this.inputType != 'radio'){
20619 cfg.cn.push(hidden);
20627 * return the real input element.
20629 inputEl: function ()
20631 return this.el.select('input.roo-' + this.inputType,true).first();
20633 hiddenEl: function ()
20635 return this.el.select('input.roo-hidden-value',true).first();
20638 labelEl: function()
20640 return this.el.select('label.control-label',true).first();
20642 /* depricated... */
20646 return this.labelEl();
20649 boxLabelEl: function()
20651 return this.el.select('label.box-label',true).first();
20654 initEvents : function()
20656 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20658 this.inputEl().on('click', this.onClick, this);
20660 if (this.boxLabel) {
20661 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20664 this.startValue = this.getValue();
20667 Roo.bootstrap.CheckBox.register(this);
20671 onClick : function(e)
20673 if(this.fireEvent('click', this, e) !== false){
20674 this.setChecked(!this.checked);
20679 setChecked : function(state,suppressEvent)
20681 this.startValue = this.getValue();
20683 if(this.inputType == 'radio'){
20685 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20686 e.dom.checked = false;
20689 this.inputEl().dom.checked = true;
20691 this.inputEl().dom.value = this.inputValue;
20693 if(suppressEvent !== true){
20694 this.fireEvent('check', this, true);
20702 this.checked = state;
20704 this.inputEl().dom.checked = state;
20707 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20709 if(suppressEvent !== true){
20710 this.fireEvent('check', this, state);
20716 getValue : function()
20718 if(this.inputType == 'radio'){
20719 return this.getGroupValue();
20722 return this.hiddenEl().dom.value;
20726 getGroupValue : function()
20728 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20732 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20735 setValue : function(v,suppressEvent)
20737 if(this.inputType == 'radio'){
20738 this.setGroupValue(v, suppressEvent);
20742 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20747 setGroupValue : function(v, suppressEvent)
20749 this.startValue = this.getValue();
20751 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20752 e.dom.checked = false;
20754 if(e.dom.value == v){
20755 e.dom.checked = true;
20759 if(suppressEvent !== true){
20760 this.fireEvent('check', this, true);
20768 validate : function()
20770 if(this.getVisibilityEl().hasClass('hidden')){
20776 (this.inputType == 'radio' && this.validateRadio()) ||
20777 (this.inputType == 'checkbox' && this.validateCheckbox())
20783 this.markInvalid();
20787 validateRadio : function()
20789 if(this.getVisibilityEl().hasClass('hidden')){
20793 if(this.allowBlank){
20799 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20800 if(!e.dom.checked){
20812 validateCheckbox : function()
20815 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20816 //return (this.getValue() == this.inputValue) ? true : false;
20819 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20827 for(var i in group){
20828 if(group[i].el.isVisible(true)){
20836 for(var i in group){
20841 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20848 * Mark this field as valid
20850 markValid : function()
20854 this.fireEvent('valid', this);
20856 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20859 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20866 if(this.inputType == 'radio'){
20867 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20868 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20869 e.findParent('.form-group', false, true).addClass(_this.validClass);
20876 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20877 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20881 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20887 for(var i in group){
20888 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20889 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20894 * Mark this field as invalid
20895 * @param {String} msg The validation message
20897 markInvalid : function(msg)
20899 if(this.allowBlank){
20905 this.fireEvent('invalid', this, msg);
20907 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20910 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20914 label.markInvalid();
20917 if(this.inputType == 'radio'){
20918 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20919 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20920 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20927 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20928 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20932 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20938 for(var i in group){
20939 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20940 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20945 clearInvalid : function()
20947 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20949 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20951 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20953 if (label && label.iconEl) {
20954 label.iconEl.removeClass(label.validClass);
20955 label.iconEl.removeClass(label.invalidClass);
20959 disable : function()
20961 if(this.inputType != 'radio'){
20962 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20969 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20970 _this.getActionEl().addClass(this.disabledClass);
20971 e.dom.disabled = true;
20975 this.disabled = true;
20976 this.fireEvent("disable", this);
20980 enable : function()
20982 if(this.inputType != 'radio'){
20983 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20990 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20991 _this.getActionEl().removeClass(this.disabledClass);
20992 e.dom.disabled = false;
20996 this.disabled = false;
20997 this.fireEvent("enable", this);
21001 setBoxLabel : function(v)
21006 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21012 Roo.apply(Roo.bootstrap.CheckBox, {
21017 * register a CheckBox Group
21018 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21020 register : function(checkbox)
21022 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21023 this.groups[checkbox.groupId] = {};
21026 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21030 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21034 * fetch a CheckBox Group based on the group ID
21035 * @param {string} the group ID
21036 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21038 get: function(groupId) {
21039 if (typeof(this.groups[groupId]) == 'undefined') {
21043 return this.groups[groupId] ;
21056 * @class Roo.bootstrap.Radio
21057 * @extends Roo.bootstrap.Component
21058 * Bootstrap Radio class
21059 * @cfg {String} boxLabel - the label associated
21060 * @cfg {String} value - the value of radio
21063 * Create a new Radio
21064 * @param {Object} config The config object
21066 Roo.bootstrap.Radio = function(config){
21067 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21071 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21077 getAutoCreate : function()
21081 cls : 'form-group radio',
21086 html : this.boxLabel
21094 initEvents : function()
21096 this.parent().register(this);
21098 this.el.on('click', this.onClick, this);
21102 onClick : function(e)
21104 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21105 this.setChecked(true);
21109 setChecked : function(state, suppressEvent)
21111 this.parent().setValue(this.value, suppressEvent);
21115 setBoxLabel : function(v)
21120 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21135 * @class Roo.bootstrap.SecurePass
21136 * @extends Roo.bootstrap.Input
21137 * Bootstrap SecurePass class
21141 * Create a new SecurePass
21142 * @param {Object} config The config object
21145 Roo.bootstrap.SecurePass = function (config) {
21146 // these go here, so the translation tool can replace them..
21148 PwdEmpty: "Please type a password, and then retype it to confirm.",
21149 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21150 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21151 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21152 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21153 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21154 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21155 TooWeak: "Your password is Too Weak."
21157 this.meterLabel = "Password strength:";
21158 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21159 this.meterClass = [
21160 "roo-password-meter-tooweak",
21161 "roo-password-meter-weak",
21162 "roo-password-meter-medium",
21163 "roo-password-meter-strong",
21164 "roo-password-meter-grey"
21169 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21172 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21174 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21176 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21177 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21178 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21179 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21180 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21181 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21182 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21192 * @cfg {String/Object} Label for the strength meter (defaults to
21193 * 'Password strength:')
21198 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21199 * ['Weak', 'Medium', 'Strong'])
21202 pwdStrengths: false,
21215 initEvents: function ()
21217 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21219 if (this.el.is('input[type=password]') && Roo.isSafari) {
21220 this.el.on('keydown', this.SafariOnKeyDown, this);
21223 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21226 onRender: function (ct, position)
21228 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21229 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21230 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21232 this.trigger.createChild({
21237 cls: 'roo-password-meter-grey col-xs-12',
21240 //width: this.meterWidth + 'px'
21244 cls: 'roo-password-meter-text'
21250 if (this.hideTrigger) {
21251 this.trigger.setDisplayed(false);
21253 this.setSize(this.width || '', this.height || '');
21256 onDestroy: function ()
21258 if (this.trigger) {
21259 this.trigger.removeAllListeners();
21260 this.trigger.remove();
21263 this.wrap.remove();
21265 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21268 checkStrength: function ()
21270 var pwd = this.inputEl().getValue();
21271 if (pwd == this._lastPwd) {
21276 if (this.ClientSideStrongPassword(pwd)) {
21278 } else if (this.ClientSideMediumPassword(pwd)) {
21280 } else if (this.ClientSideWeakPassword(pwd)) {
21286 Roo.log('strength1: ' + strength);
21288 //var pm = this.trigger.child('div/div/div').dom;
21289 var pm = this.trigger.child('div/div');
21290 pm.removeClass(this.meterClass);
21291 pm.addClass(this.meterClass[strength]);
21294 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21296 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21298 this._lastPwd = pwd;
21302 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21304 this._lastPwd = '';
21306 var pm = this.trigger.child('div/div');
21307 pm.removeClass(this.meterClass);
21308 pm.addClass('roo-password-meter-grey');
21311 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21314 this.inputEl().dom.type='password';
21317 validateValue: function (value)
21320 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21323 if (value.length == 0) {
21324 if (this.allowBlank) {
21325 this.clearInvalid();
21329 this.markInvalid(this.errors.PwdEmpty);
21330 this.errorMsg = this.errors.PwdEmpty;
21338 if ('[\x21-\x7e]*'.match(value)) {
21339 this.markInvalid(this.errors.PwdBadChar);
21340 this.errorMsg = this.errors.PwdBadChar;
21343 if (value.length < 6) {
21344 this.markInvalid(this.errors.PwdShort);
21345 this.errorMsg = this.errors.PwdShort;
21348 if (value.length > 16) {
21349 this.markInvalid(this.errors.PwdLong);
21350 this.errorMsg = this.errors.PwdLong;
21354 if (this.ClientSideStrongPassword(value)) {
21356 } else if (this.ClientSideMediumPassword(value)) {
21358 } else if (this.ClientSideWeakPassword(value)) {
21365 if (strength < 2) {
21366 //this.markInvalid(this.errors.TooWeak);
21367 this.errorMsg = this.errors.TooWeak;
21372 console.log('strength2: ' + strength);
21374 //var pm = this.trigger.child('div/div/div').dom;
21376 var pm = this.trigger.child('div/div');
21377 pm.removeClass(this.meterClass);
21378 pm.addClass(this.meterClass[strength]);
21380 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21382 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21384 this.errorMsg = '';
21388 CharacterSetChecks: function (type)
21391 this.fResult = false;
21394 isctype: function (character, type)
21397 case this.kCapitalLetter:
21398 if (character >= 'A' && character <= 'Z') {
21403 case this.kSmallLetter:
21404 if (character >= 'a' && character <= 'z') {
21410 if (character >= '0' && character <= '9') {
21415 case this.kPunctuation:
21416 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21427 IsLongEnough: function (pwd, size)
21429 return !(pwd == null || isNaN(size) || pwd.length < size);
21432 SpansEnoughCharacterSets: function (word, nb)
21434 if (!this.IsLongEnough(word, nb))
21439 var characterSetChecks = new Array(
21440 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21441 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21444 for (var index = 0; index < word.length; ++index) {
21445 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21446 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21447 characterSetChecks[nCharSet].fResult = true;
21454 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21455 if (characterSetChecks[nCharSet].fResult) {
21460 if (nCharSets < nb) {
21466 ClientSideStrongPassword: function (pwd)
21468 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21471 ClientSideMediumPassword: function (pwd)
21473 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21476 ClientSideWeakPassword: function (pwd)
21478 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21481 })//<script type="text/javascript">
21484 * Based Ext JS Library 1.1.1
21485 * Copyright(c) 2006-2007, Ext JS, LLC.
21491 * @class Roo.HtmlEditorCore
21492 * @extends Roo.Component
21493 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21495 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21498 Roo.HtmlEditorCore = function(config){
21501 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21506 * @event initialize
21507 * Fires when the editor is fully initialized (including the iframe)
21508 * @param {Roo.HtmlEditorCore} this
21513 * Fires when the editor is first receives the focus. Any insertion must wait
21514 * until after this event.
21515 * @param {Roo.HtmlEditorCore} this
21519 * @event beforesync
21520 * Fires before the textarea is updated with content from the editor iframe. Return false
21521 * to cancel the sync.
21522 * @param {Roo.HtmlEditorCore} this
21523 * @param {String} html
21527 * @event beforepush
21528 * Fires before the iframe editor is updated with content from the textarea. Return false
21529 * to cancel the push.
21530 * @param {Roo.HtmlEditorCore} this
21531 * @param {String} html
21536 * Fires when the textarea is updated with content from the editor iframe.
21537 * @param {Roo.HtmlEditorCore} this
21538 * @param {String} html
21543 * Fires when the iframe editor is updated with content from the textarea.
21544 * @param {Roo.HtmlEditorCore} this
21545 * @param {String} html
21550 * @event editorevent
21551 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21552 * @param {Roo.HtmlEditorCore} this
21558 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21560 // defaults : white / black...
21561 this.applyBlacklists();
21568 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21572 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21578 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21583 * @cfg {Number} height (in pixels)
21587 * @cfg {Number} width (in pixels)
21592 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21595 stylesheets: false,
21600 // private properties
21601 validationEvent : false,
21603 initialized : false,
21605 sourceEditMode : false,
21606 onFocus : Roo.emptyFn,
21608 hideMode:'offsets',
21612 // blacklist + whitelisted elements..
21619 * Protected method that will not generally be called directly. It
21620 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21621 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21623 getDocMarkup : function(){
21627 // inherit styels from page...??
21628 if (this.stylesheets === false) {
21630 Roo.get(document.head).select('style').each(function(node) {
21631 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21634 Roo.get(document.head).select('link').each(function(node) {
21635 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21638 } else if (!this.stylesheets.length) {
21640 st = '<style type="text/css">' +
21641 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21644 st = '<style type="text/css">' +
21649 st += '<style type="text/css">' +
21650 'IMG { cursor: pointer } ' +
21653 var cls = 'roo-htmleditor-body';
21655 if(this.bodyCls.length){
21656 cls += ' ' + this.bodyCls;
21659 return '<html><head>' + st +
21660 //<style type="text/css">' +
21661 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21663 ' </head><body class="' + cls + '"></body></html>';
21667 onRender : function(ct, position)
21670 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21671 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21674 this.el.dom.style.border = '0 none';
21675 this.el.dom.setAttribute('tabIndex', -1);
21676 this.el.addClass('x-hidden hide');
21680 if(Roo.isIE){ // fix IE 1px bogus margin
21681 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21685 this.frameId = Roo.id();
21689 var iframe = this.owner.wrap.createChild({
21691 cls: 'form-control', // bootstrap..
21693 name: this.frameId,
21694 frameBorder : 'no',
21695 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21700 this.iframe = iframe.dom;
21702 this.assignDocWin();
21704 this.doc.designMode = 'on';
21707 this.doc.write(this.getDocMarkup());
21711 var task = { // must defer to wait for browser to be ready
21713 //console.log("run task?" + this.doc.readyState);
21714 this.assignDocWin();
21715 if(this.doc.body || this.doc.readyState == 'complete'){
21717 this.doc.designMode="on";
21721 Roo.TaskMgr.stop(task);
21722 this.initEditor.defer(10, this);
21729 Roo.TaskMgr.start(task);
21734 onResize : function(w, h)
21736 Roo.log('resize: ' +w + ',' + h );
21737 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21741 if(typeof w == 'number'){
21743 this.iframe.style.width = w + 'px';
21745 if(typeof h == 'number'){
21747 this.iframe.style.height = h + 'px';
21749 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21756 * Toggles the editor between standard and source edit mode.
21757 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21759 toggleSourceEdit : function(sourceEditMode){
21761 this.sourceEditMode = sourceEditMode === true;
21763 if(this.sourceEditMode){
21765 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21768 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21769 //this.iframe.className = '';
21772 //this.setSize(this.owner.wrap.getSize());
21773 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21780 * Protected method that will not generally be called directly. If you need/want
21781 * custom HTML cleanup, this is the method you should override.
21782 * @param {String} html The HTML to be cleaned
21783 * return {String} The cleaned HTML
21785 cleanHtml : function(html){
21786 html = String(html);
21787 if(html.length > 5){
21788 if(Roo.isSafari){ // strip safari nonsense
21789 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21792 if(html == ' '){
21799 * HTML Editor -> Textarea
21800 * Protected method that will not generally be called directly. Syncs the contents
21801 * of the editor iframe with the textarea.
21803 syncValue : function(){
21804 if(this.initialized){
21805 var bd = (this.doc.body || this.doc.documentElement);
21806 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21807 var html = bd.innerHTML;
21809 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21810 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21812 html = '<div style="'+m[0]+'">' + html + '</div>';
21815 html = this.cleanHtml(html);
21816 // fix up the special chars.. normaly like back quotes in word...
21817 // however we do not want to do this with chinese..
21818 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21819 var cc = b.charCodeAt();
21821 (cc >= 0x4E00 && cc < 0xA000 ) ||
21822 (cc >= 0x3400 && cc < 0x4E00 ) ||
21823 (cc >= 0xf900 && cc < 0xfb00 )
21829 if(this.owner.fireEvent('beforesync', this, html) !== false){
21830 this.el.dom.value = html;
21831 this.owner.fireEvent('sync', this, html);
21837 * Protected method that will not generally be called directly. Pushes the value of the textarea
21838 * into the iframe editor.
21840 pushValue : function(){
21841 if(this.initialized){
21842 var v = this.el.dom.value.trim();
21844 // if(v.length < 1){
21848 if(this.owner.fireEvent('beforepush', this, v) !== false){
21849 var d = (this.doc.body || this.doc.documentElement);
21851 this.cleanUpPaste();
21852 this.el.dom.value = d.innerHTML;
21853 this.owner.fireEvent('push', this, v);
21859 deferFocus : function(){
21860 this.focus.defer(10, this);
21864 focus : function(){
21865 if(this.win && !this.sourceEditMode){
21872 assignDocWin: function()
21874 var iframe = this.iframe;
21877 this.doc = iframe.contentWindow.document;
21878 this.win = iframe.contentWindow;
21880 // if (!Roo.get(this.frameId)) {
21883 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21884 // this.win = Roo.get(this.frameId).dom.contentWindow;
21886 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21890 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21891 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21896 initEditor : function(){
21897 //console.log("INIT EDITOR");
21898 this.assignDocWin();
21902 this.doc.designMode="on";
21904 this.doc.write(this.getDocMarkup());
21907 var dbody = (this.doc.body || this.doc.documentElement);
21908 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21909 // this copies styles from the containing element into thsi one..
21910 // not sure why we need all of this..
21911 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21913 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21914 //ss['background-attachment'] = 'fixed'; // w3c
21915 dbody.bgProperties = 'fixed'; // ie
21916 //Roo.DomHelper.applyStyles(dbody, ss);
21917 Roo.EventManager.on(this.doc, {
21918 //'mousedown': this.onEditorEvent,
21919 'mouseup': this.onEditorEvent,
21920 'dblclick': this.onEditorEvent,
21921 'click': this.onEditorEvent,
21922 'keyup': this.onEditorEvent,
21927 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21929 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21930 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21932 this.initialized = true;
21934 this.owner.fireEvent('initialize', this);
21939 onDestroy : function(){
21945 //for (var i =0; i < this.toolbars.length;i++) {
21946 // // fixme - ask toolbars for heights?
21947 // this.toolbars[i].onDestroy();
21950 //this.wrap.dom.innerHTML = '';
21951 //this.wrap.remove();
21956 onFirstFocus : function(){
21958 this.assignDocWin();
21961 this.activated = true;
21964 if(Roo.isGecko){ // prevent silly gecko errors
21966 var s = this.win.getSelection();
21967 if(!s.focusNode || s.focusNode.nodeType != 3){
21968 var r = s.getRangeAt(0);
21969 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21974 this.execCmd('useCSS', true);
21975 this.execCmd('styleWithCSS', false);
21978 this.owner.fireEvent('activate', this);
21982 adjustFont: function(btn){
21983 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21984 //if(Roo.isSafari){ // safari
21987 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21988 if(Roo.isSafari){ // safari
21989 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21990 v = (v < 10) ? 10 : v;
21991 v = (v > 48) ? 48 : v;
21992 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21997 v = Math.max(1, v+adjust);
21999 this.execCmd('FontSize', v );
22002 onEditorEvent : function(e)
22004 this.owner.fireEvent('editorevent', this, e);
22005 // this.updateToolbar();
22006 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22009 insertTag : function(tg)
22011 // could be a bit smarter... -> wrap the current selected tRoo..
22012 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22014 range = this.createRange(this.getSelection());
22015 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22016 wrappingNode.appendChild(range.extractContents());
22017 range.insertNode(wrappingNode);
22024 this.execCmd("formatblock", tg);
22028 insertText : function(txt)
22032 var range = this.createRange();
22033 range.deleteContents();
22034 //alert(Sender.getAttribute('label'));
22036 range.insertNode(this.doc.createTextNode(txt));
22042 * Executes a Midas editor command on the editor document and performs necessary focus and
22043 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22044 * @param {String} cmd The Midas command
22045 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22047 relayCmd : function(cmd, value){
22049 this.execCmd(cmd, value);
22050 this.owner.fireEvent('editorevent', this);
22051 //this.updateToolbar();
22052 this.owner.deferFocus();
22056 * Executes a Midas editor command directly on the editor document.
22057 * For visual commands, you should use {@link #relayCmd} instead.
22058 * <b>This should only be called after the editor is initialized.</b>
22059 * @param {String} cmd The Midas command
22060 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22062 execCmd : function(cmd, value){
22063 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22070 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22072 * @param {String} text | dom node..
22074 insertAtCursor : function(text)
22077 if(!this.activated){
22083 var r = this.doc.selection.createRange();
22094 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22098 // from jquery ui (MIT licenced)
22100 var win = this.win;
22102 if (win.getSelection && win.getSelection().getRangeAt) {
22103 range = win.getSelection().getRangeAt(0);
22104 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22105 range.insertNode(node);
22106 } else if (win.document.selection && win.document.selection.createRange) {
22107 // no firefox support
22108 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22109 win.document.selection.createRange().pasteHTML(txt);
22111 // no firefox support
22112 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22113 this.execCmd('InsertHTML', txt);
22122 mozKeyPress : function(e){
22124 var c = e.getCharCode(), cmd;
22127 c = String.fromCharCode(c).toLowerCase();
22141 this.cleanUpPaste.defer(100, this);
22149 e.preventDefault();
22157 fixKeys : function(){ // load time branching for fastest keydown performance
22159 return function(e){
22160 var k = e.getKey(), r;
22163 r = this.doc.selection.createRange();
22166 r.pasteHTML('    ');
22173 r = this.doc.selection.createRange();
22175 var target = r.parentElement();
22176 if(!target || target.tagName.toLowerCase() != 'li'){
22178 r.pasteHTML('<br />');
22184 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22185 this.cleanUpPaste.defer(100, this);
22191 }else if(Roo.isOpera){
22192 return function(e){
22193 var k = e.getKey();
22197 this.execCmd('InsertHTML','    ');
22200 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22201 this.cleanUpPaste.defer(100, this);
22206 }else if(Roo.isSafari){
22207 return function(e){
22208 var k = e.getKey();
22212 this.execCmd('InsertText','\t');
22216 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22217 this.cleanUpPaste.defer(100, this);
22225 getAllAncestors: function()
22227 var p = this.getSelectedNode();
22230 a.push(p); // push blank onto stack..
22231 p = this.getParentElement();
22235 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22239 a.push(this.doc.body);
22243 lastSelNode : false,
22246 getSelection : function()
22248 this.assignDocWin();
22249 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22252 getSelectedNode: function()
22254 // this may only work on Gecko!!!
22256 // should we cache this!!!!
22261 var range = this.createRange(this.getSelection()).cloneRange();
22264 var parent = range.parentElement();
22266 var testRange = range.duplicate();
22267 testRange.moveToElementText(parent);
22268 if (testRange.inRange(range)) {
22271 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22274 parent = parent.parentElement;
22279 // is ancestor a text element.
22280 var ac = range.commonAncestorContainer;
22281 if (ac.nodeType == 3) {
22282 ac = ac.parentNode;
22285 var ar = ac.childNodes;
22288 var other_nodes = [];
22289 var has_other_nodes = false;
22290 for (var i=0;i<ar.length;i++) {
22291 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22294 // fullly contained node.
22296 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22301 // probably selected..
22302 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22303 other_nodes.push(ar[i]);
22307 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22312 has_other_nodes = true;
22314 if (!nodes.length && other_nodes.length) {
22315 nodes= other_nodes;
22317 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22323 createRange: function(sel)
22325 // this has strange effects when using with
22326 // top toolbar - not sure if it's a great idea.
22327 //this.editor.contentWindow.focus();
22328 if (typeof sel != "undefined") {
22330 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22332 return this.doc.createRange();
22335 return this.doc.createRange();
22338 getParentElement: function()
22341 this.assignDocWin();
22342 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22344 var range = this.createRange(sel);
22347 var p = range.commonAncestorContainer;
22348 while (p.nodeType == 3) { // text node
22359 * Range intersection.. the hard stuff...
22363 * [ -- selected range --- ]
22367 * if end is before start or hits it. fail.
22368 * if start is after end or hits it fail.
22370 * if either hits (but other is outside. - then it's not
22376 // @see http://www.thismuchiknow.co.uk/?p=64.
22377 rangeIntersectsNode : function(range, node)
22379 var nodeRange = node.ownerDocument.createRange();
22381 nodeRange.selectNode(node);
22383 nodeRange.selectNodeContents(node);
22386 var rangeStartRange = range.cloneRange();
22387 rangeStartRange.collapse(true);
22389 var rangeEndRange = range.cloneRange();
22390 rangeEndRange.collapse(false);
22392 var nodeStartRange = nodeRange.cloneRange();
22393 nodeStartRange.collapse(true);
22395 var nodeEndRange = nodeRange.cloneRange();
22396 nodeEndRange.collapse(false);
22398 return rangeStartRange.compareBoundaryPoints(
22399 Range.START_TO_START, nodeEndRange) == -1 &&
22400 rangeEndRange.compareBoundaryPoints(
22401 Range.START_TO_START, nodeStartRange) == 1;
22405 rangeCompareNode : function(range, node)
22407 var nodeRange = node.ownerDocument.createRange();
22409 nodeRange.selectNode(node);
22411 nodeRange.selectNodeContents(node);
22415 range.collapse(true);
22417 nodeRange.collapse(true);
22419 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22420 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22422 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22424 var nodeIsBefore = ss == 1;
22425 var nodeIsAfter = ee == -1;
22427 if (nodeIsBefore && nodeIsAfter) {
22430 if (!nodeIsBefore && nodeIsAfter) {
22431 return 1; //right trailed.
22434 if (nodeIsBefore && !nodeIsAfter) {
22435 return 2; // left trailed.
22441 // private? - in a new class?
22442 cleanUpPaste : function()
22444 // cleans up the whole document..
22445 Roo.log('cleanuppaste');
22447 this.cleanUpChildren(this.doc.body);
22448 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22449 if (clean != this.doc.body.innerHTML) {
22450 this.doc.body.innerHTML = clean;
22455 cleanWordChars : function(input) {// change the chars to hex code
22456 var he = Roo.HtmlEditorCore;
22458 var output = input;
22459 Roo.each(he.swapCodes, function(sw) {
22460 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22462 output = output.replace(swapper, sw[1]);
22469 cleanUpChildren : function (n)
22471 if (!n.childNodes.length) {
22474 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22475 this.cleanUpChild(n.childNodes[i]);
22482 cleanUpChild : function (node)
22485 //console.log(node);
22486 if (node.nodeName == "#text") {
22487 // clean up silly Windows -- stuff?
22490 if (node.nodeName == "#comment") {
22491 node.parentNode.removeChild(node);
22492 // clean up silly Windows -- stuff?
22495 var lcname = node.tagName.toLowerCase();
22496 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22497 // whitelist of tags..
22499 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22501 node.parentNode.removeChild(node);
22506 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22508 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22509 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22511 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22512 // remove_keep_children = true;
22515 if (remove_keep_children) {
22516 this.cleanUpChildren(node);
22517 // inserts everything just before this node...
22518 while (node.childNodes.length) {
22519 var cn = node.childNodes[0];
22520 node.removeChild(cn);
22521 node.parentNode.insertBefore(cn, node);
22523 node.parentNode.removeChild(node);
22527 if (!node.attributes || !node.attributes.length) {
22528 this.cleanUpChildren(node);
22532 function cleanAttr(n,v)
22535 if (v.match(/^\./) || v.match(/^\//)) {
22538 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22541 if (v.match(/^#/)) {
22544 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22545 node.removeAttribute(n);
22549 var cwhite = this.cwhite;
22550 var cblack = this.cblack;
22552 function cleanStyle(n,v)
22554 if (v.match(/expression/)) { //XSS?? should we even bother..
22555 node.removeAttribute(n);
22559 var parts = v.split(/;/);
22562 Roo.each(parts, function(p) {
22563 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22567 var l = p.split(':').shift().replace(/\s+/g,'');
22568 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22570 if ( cwhite.length && cblack.indexOf(l) > -1) {
22571 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22572 //node.removeAttribute(n);
22576 // only allow 'c whitelisted system attributes'
22577 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22578 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22579 //node.removeAttribute(n);
22589 if (clean.length) {
22590 node.setAttribute(n, clean.join(';'));
22592 node.removeAttribute(n);
22598 for (var i = node.attributes.length-1; i > -1 ; i--) {
22599 var a = node.attributes[i];
22602 if (a.name.toLowerCase().substr(0,2)=='on') {
22603 node.removeAttribute(a.name);
22606 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22607 node.removeAttribute(a.name);
22610 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22611 cleanAttr(a.name,a.value); // fixme..
22614 if (a.name == 'style') {
22615 cleanStyle(a.name,a.value);
22618 /// clean up MS crap..
22619 // tecnically this should be a list of valid class'es..
22622 if (a.name == 'class') {
22623 if (a.value.match(/^Mso/)) {
22624 node.className = '';
22627 if (a.value.match(/^body$/)) {
22628 node.className = '';
22639 this.cleanUpChildren(node);
22645 * Clean up MS wordisms...
22647 cleanWord : function(node)
22652 this.cleanWord(this.doc.body);
22655 if (node.nodeName == "#text") {
22656 // clean up silly Windows -- stuff?
22659 if (node.nodeName == "#comment") {
22660 node.parentNode.removeChild(node);
22661 // clean up silly Windows -- stuff?
22665 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22666 node.parentNode.removeChild(node);
22670 // remove - but keep children..
22671 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22672 while (node.childNodes.length) {
22673 var cn = node.childNodes[0];
22674 node.removeChild(cn);
22675 node.parentNode.insertBefore(cn, node);
22677 node.parentNode.removeChild(node);
22678 this.iterateChildren(node, this.cleanWord);
22682 if (node.className.length) {
22684 var cn = node.className.split(/\W+/);
22686 Roo.each(cn, function(cls) {
22687 if (cls.match(/Mso[a-zA-Z]+/)) {
22692 node.className = cna.length ? cna.join(' ') : '';
22694 node.removeAttribute("class");
22698 if (node.hasAttribute("lang")) {
22699 node.removeAttribute("lang");
22702 if (node.hasAttribute("style")) {
22704 var styles = node.getAttribute("style").split(";");
22706 Roo.each(styles, function(s) {
22707 if (!s.match(/:/)) {
22710 var kv = s.split(":");
22711 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22714 // what ever is left... we allow.
22717 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22718 if (!nstyle.length) {
22719 node.removeAttribute('style');
22722 this.iterateChildren(node, this.cleanWord);
22728 * iterateChildren of a Node, calling fn each time, using this as the scole..
22729 * @param {DomNode} node node to iterate children of.
22730 * @param {Function} fn method of this class to call on each item.
22732 iterateChildren : function(node, fn)
22734 if (!node.childNodes.length) {
22737 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22738 fn.call(this, node.childNodes[i])
22744 * cleanTableWidths.
22746 * Quite often pasting from word etc.. results in tables with column and widths.
22747 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22750 cleanTableWidths : function(node)
22755 this.cleanTableWidths(this.doc.body);
22760 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22763 Roo.log(node.tagName);
22764 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22765 this.iterateChildren(node, this.cleanTableWidths);
22768 if (node.hasAttribute('width')) {
22769 node.removeAttribute('width');
22773 if (node.hasAttribute("style")) {
22776 var styles = node.getAttribute("style").split(";");
22778 Roo.each(styles, function(s) {
22779 if (!s.match(/:/)) {
22782 var kv = s.split(":");
22783 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22786 // what ever is left... we allow.
22789 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22790 if (!nstyle.length) {
22791 node.removeAttribute('style');
22795 this.iterateChildren(node, this.cleanTableWidths);
22803 domToHTML : function(currentElement, depth, nopadtext) {
22805 depth = depth || 0;
22806 nopadtext = nopadtext || false;
22808 if (!currentElement) {
22809 return this.domToHTML(this.doc.body);
22812 //Roo.log(currentElement);
22814 var allText = false;
22815 var nodeName = currentElement.nodeName;
22816 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22818 if (nodeName == '#text') {
22820 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22825 if (nodeName != 'BODY') {
22828 // Prints the node tagName, such as <A>, <IMG>, etc
22831 for(i = 0; i < currentElement.attributes.length;i++) {
22833 var aname = currentElement.attributes.item(i).name;
22834 if (!currentElement.attributes.item(i).value.length) {
22837 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22840 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22849 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22852 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22857 // Traverse the tree
22859 var currentElementChild = currentElement.childNodes.item(i);
22860 var allText = true;
22861 var innerHTML = '';
22863 while (currentElementChild) {
22864 // Formatting code (indent the tree so it looks nice on the screen)
22865 var nopad = nopadtext;
22866 if (lastnode == 'SPAN') {
22870 if (currentElementChild.nodeName == '#text') {
22871 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22872 toadd = nopadtext ? toadd : toadd.trim();
22873 if (!nopad && toadd.length > 80) {
22874 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22876 innerHTML += toadd;
22879 currentElementChild = currentElement.childNodes.item(i);
22885 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22887 // Recursively traverse the tree structure of the child node
22888 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22889 lastnode = currentElementChild.nodeName;
22891 currentElementChild=currentElement.childNodes.item(i);
22897 // The remaining code is mostly for formatting the tree
22898 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22903 ret+= "</"+tagName+">";
22909 applyBlacklists : function()
22911 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22912 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22916 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22917 if (b.indexOf(tag) > -1) {
22920 this.white.push(tag);
22924 Roo.each(w, function(tag) {
22925 if (b.indexOf(tag) > -1) {
22928 if (this.white.indexOf(tag) > -1) {
22931 this.white.push(tag);
22936 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22937 if (w.indexOf(tag) > -1) {
22940 this.black.push(tag);
22944 Roo.each(b, function(tag) {
22945 if (w.indexOf(tag) > -1) {
22948 if (this.black.indexOf(tag) > -1) {
22951 this.black.push(tag);
22956 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22957 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22961 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22962 if (b.indexOf(tag) > -1) {
22965 this.cwhite.push(tag);
22969 Roo.each(w, function(tag) {
22970 if (b.indexOf(tag) > -1) {
22973 if (this.cwhite.indexOf(tag) > -1) {
22976 this.cwhite.push(tag);
22981 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22982 if (w.indexOf(tag) > -1) {
22985 this.cblack.push(tag);
22989 Roo.each(b, function(tag) {
22990 if (w.indexOf(tag) > -1) {
22993 if (this.cblack.indexOf(tag) > -1) {
22996 this.cblack.push(tag);
23001 setStylesheets : function(stylesheets)
23003 if(typeof(stylesheets) == 'string'){
23004 Roo.get(this.iframe.contentDocument.head).createChild({
23006 rel : 'stylesheet',
23015 Roo.each(stylesheets, function(s) {
23020 Roo.get(_this.iframe.contentDocument.head).createChild({
23022 rel : 'stylesheet',
23031 removeStylesheets : function()
23035 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23040 setStyle : function(style)
23042 Roo.get(this.iframe.contentDocument.head).createChild({
23051 // hide stuff that is not compatible
23065 * @event specialkey
23069 * @cfg {String} fieldClass @hide
23072 * @cfg {String} focusClass @hide
23075 * @cfg {String} autoCreate @hide
23078 * @cfg {String} inputType @hide
23081 * @cfg {String} invalidClass @hide
23084 * @cfg {String} invalidText @hide
23087 * @cfg {String} msgFx @hide
23090 * @cfg {String} validateOnBlur @hide
23094 Roo.HtmlEditorCore.white = [
23095 'area', 'br', 'img', 'input', 'hr', 'wbr',
23097 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23098 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23099 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23100 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23101 'table', 'ul', 'xmp',
23103 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23106 'dir', 'menu', 'ol', 'ul', 'dl',
23112 Roo.HtmlEditorCore.black = [
23113 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23115 'base', 'basefont', 'bgsound', 'blink', 'body',
23116 'frame', 'frameset', 'head', 'html', 'ilayer',
23117 'iframe', 'layer', 'link', 'meta', 'object',
23118 'script', 'style' ,'title', 'xml' // clean later..
23120 Roo.HtmlEditorCore.clean = [
23121 'script', 'style', 'title', 'xml'
23123 Roo.HtmlEditorCore.remove = [
23128 Roo.HtmlEditorCore.ablack = [
23132 Roo.HtmlEditorCore.aclean = [
23133 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23137 Roo.HtmlEditorCore.pwhite= [
23138 'http', 'https', 'mailto'
23141 // white listed style attributes.
23142 Roo.HtmlEditorCore.cwhite= [
23143 // 'text-align', /// default is to allow most things..
23149 // black listed style attributes.
23150 Roo.HtmlEditorCore.cblack= [
23151 // 'font-size' -- this can be set by the project
23155 Roo.HtmlEditorCore.swapCodes =[
23174 * @class Roo.bootstrap.HtmlEditor
23175 * @extends Roo.bootstrap.TextArea
23176 * Bootstrap HtmlEditor class
23179 * Create a new HtmlEditor
23180 * @param {Object} config The config object
23183 Roo.bootstrap.HtmlEditor = function(config){
23184 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23185 if (!this.toolbars) {
23186 this.toolbars = [];
23189 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23192 * @event initialize
23193 * Fires when the editor is fully initialized (including the iframe)
23194 * @param {HtmlEditor} this
23199 * Fires when the editor is first receives the focus. Any insertion must wait
23200 * until after this event.
23201 * @param {HtmlEditor} this
23205 * @event beforesync
23206 * Fires before the textarea is updated with content from the editor iframe. Return false
23207 * to cancel the sync.
23208 * @param {HtmlEditor} this
23209 * @param {String} html
23213 * @event beforepush
23214 * Fires before the iframe editor is updated with content from the textarea. Return false
23215 * to cancel the push.
23216 * @param {HtmlEditor} this
23217 * @param {String} html
23222 * Fires when the textarea is updated with content from the editor iframe.
23223 * @param {HtmlEditor} this
23224 * @param {String} html
23229 * Fires when the iframe editor is updated with content from the textarea.
23230 * @param {HtmlEditor} this
23231 * @param {String} html
23235 * @event editmodechange
23236 * Fires when the editor switches edit modes
23237 * @param {HtmlEditor} this
23238 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23240 editmodechange: true,
23242 * @event editorevent
23243 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23244 * @param {HtmlEditor} this
23248 * @event firstfocus
23249 * Fires when on first focus - needed by toolbars..
23250 * @param {HtmlEditor} this
23255 * Auto save the htmlEditor value as a file into Events
23256 * @param {HtmlEditor} this
23260 * @event savedpreview
23261 * preview the saved version of htmlEditor
23262 * @param {HtmlEditor} this
23269 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23273 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23278 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23283 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23288 * @cfg {Number} height (in pixels)
23292 * @cfg {Number} width (in pixels)
23297 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23300 stylesheets: false,
23305 // private properties
23306 validationEvent : false,
23308 initialized : false,
23311 onFocus : Roo.emptyFn,
23313 hideMode:'offsets',
23315 tbContainer : false,
23319 toolbarContainer :function() {
23320 return this.wrap.select('.x-html-editor-tb',true).first();
23324 * Protected method that will not generally be called directly. It
23325 * is called when the editor creates its toolbar. Override this method if you need to
23326 * add custom toolbar buttons.
23327 * @param {HtmlEditor} editor
23329 createToolbar : function(){
23330 Roo.log('renewing');
23331 Roo.log("create toolbars");
23333 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23334 this.toolbars[0].render(this.toolbarContainer());
23338 // if (!editor.toolbars || !editor.toolbars.length) {
23339 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23342 // for (var i =0 ; i < editor.toolbars.length;i++) {
23343 // editor.toolbars[i] = Roo.factory(
23344 // typeof(editor.toolbars[i]) == 'string' ?
23345 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23346 // Roo.bootstrap.HtmlEditor);
23347 // editor.toolbars[i].init(editor);
23353 onRender : function(ct, position)
23355 // Roo.log("Call onRender: " + this.xtype);
23357 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23359 this.wrap = this.inputEl().wrap({
23360 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23363 this.editorcore.onRender(ct, position);
23365 if (this.resizable) {
23366 this.resizeEl = new Roo.Resizable(this.wrap, {
23370 minHeight : this.height,
23371 height: this.height,
23372 handles : this.resizable,
23375 resize : function(r, w, h) {
23376 _t.onResize(w,h); // -something
23382 this.createToolbar(this);
23385 if(!this.width && this.resizable){
23386 this.setSize(this.wrap.getSize());
23388 if (this.resizeEl) {
23389 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23390 // should trigger onReize..
23396 onResize : function(w, h)
23398 Roo.log('resize: ' +w + ',' + h );
23399 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23403 if(this.inputEl() ){
23404 if(typeof w == 'number'){
23405 var aw = w - this.wrap.getFrameWidth('lr');
23406 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23409 if(typeof h == 'number'){
23410 var tbh = -11; // fixme it needs to tool bar size!
23411 for (var i =0; i < this.toolbars.length;i++) {
23412 // fixme - ask toolbars for heights?
23413 tbh += this.toolbars[i].el.getHeight();
23414 //if (this.toolbars[i].footer) {
23415 // tbh += this.toolbars[i].footer.el.getHeight();
23423 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23424 ah -= 5; // knock a few pixes off for look..
23425 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23429 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23430 this.editorcore.onResize(ew,eh);
23435 * Toggles the editor between standard and source edit mode.
23436 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23438 toggleSourceEdit : function(sourceEditMode)
23440 this.editorcore.toggleSourceEdit(sourceEditMode);
23442 if(this.editorcore.sourceEditMode){
23443 Roo.log('editor - showing textarea');
23446 // Roo.log(this.syncValue());
23448 this.inputEl().removeClass(['hide', 'x-hidden']);
23449 this.inputEl().dom.removeAttribute('tabIndex');
23450 this.inputEl().focus();
23452 Roo.log('editor - hiding textarea');
23454 // Roo.log(this.pushValue());
23457 this.inputEl().addClass(['hide', 'x-hidden']);
23458 this.inputEl().dom.setAttribute('tabIndex', -1);
23459 //this.deferFocus();
23462 if(this.resizable){
23463 this.setSize(this.wrap.getSize());
23466 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23469 // private (for BoxComponent)
23470 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23472 // private (for BoxComponent)
23473 getResizeEl : function(){
23477 // private (for BoxComponent)
23478 getPositionEl : function(){
23483 initEvents : function(){
23484 this.originalValue = this.getValue();
23488 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23491 // markInvalid : Roo.emptyFn,
23493 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23496 // clearInvalid : Roo.emptyFn,
23498 setValue : function(v){
23499 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23500 this.editorcore.pushValue();
23505 deferFocus : function(){
23506 this.focus.defer(10, this);
23510 focus : function(){
23511 this.editorcore.focus();
23517 onDestroy : function(){
23523 for (var i =0; i < this.toolbars.length;i++) {
23524 // fixme - ask toolbars for heights?
23525 this.toolbars[i].onDestroy();
23528 this.wrap.dom.innerHTML = '';
23529 this.wrap.remove();
23534 onFirstFocus : function(){
23535 //Roo.log("onFirstFocus");
23536 this.editorcore.onFirstFocus();
23537 for (var i =0; i < this.toolbars.length;i++) {
23538 this.toolbars[i].onFirstFocus();
23544 syncValue : function()
23546 this.editorcore.syncValue();
23549 pushValue : function()
23551 this.editorcore.pushValue();
23555 // hide stuff that is not compatible
23569 * @event specialkey
23573 * @cfg {String} fieldClass @hide
23576 * @cfg {String} focusClass @hide
23579 * @cfg {String} autoCreate @hide
23582 * @cfg {String} inputType @hide
23585 * @cfg {String} invalidClass @hide
23588 * @cfg {String} invalidText @hide
23591 * @cfg {String} msgFx @hide
23594 * @cfg {String} validateOnBlur @hide
23603 Roo.namespace('Roo.bootstrap.htmleditor');
23605 * @class Roo.bootstrap.HtmlEditorToolbar1
23610 new Roo.bootstrap.HtmlEditor({
23613 new Roo.bootstrap.HtmlEditorToolbar1({
23614 disable : { fonts: 1 , format: 1, ..., ... , ...],
23620 * @cfg {Object} disable List of elements to disable..
23621 * @cfg {Array} btns List of additional buttons.
23625 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23628 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23631 Roo.apply(this, config);
23633 // default disabled, based on 'good practice'..
23634 this.disable = this.disable || {};
23635 Roo.applyIf(this.disable, {
23638 specialElements : true
23640 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23642 this.editor = config.editor;
23643 this.editorcore = config.editor.editorcore;
23645 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23647 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23648 // dont call parent... till later.
23650 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23655 editorcore : false,
23660 "h1","h2","h3","h4","h5","h6",
23662 "abbr", "acronym", "address", "cite", "samp", "var",
23666 onRender : function(ct, position)
23668 // Roo.log("Call onRender: " + this.xtype);
23670 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23672 this.el.dom.style.marginBottom = '0';
23674 var editorcore = this.editorcore;
23675 var editor= this.editor;
23678 var btn = function(id,cmd , toggle, handler, html){
23680 var event = toggle ? 'toggle' : 'click';
23685 xns: Roo.bootstrap,
23688 enableToggle:toggle !== false,
23690 pressed : toggle ? false : null,
23693 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23694 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23700 // var cb_box = function...
23705 xns: Roo.bootstrap,
23706 glyphicon : 'font',
23710 xns: Roo.bootstrap,
23714 Roo.each(this.formats, function(f) {
23715 style.menu.items.push({
23717 xns: Roo.bootstrap,
23718 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23723 editorcore.insertTag(this.tagname);
23730 children.push(style);
23732 btn('bold',false,true);
23733 btn('italic',false,true);
23734 btn('align-left', 'justifyleft',true);
23735 btn('align-center', 'justifycenter',true);
23736 btn('align-right' , 'justifyright',true);
23737 btn('link', false, false, function(btn) {
23738 //Roo.log("create link?");
23739 var url = prompt(this.createLinkText, this.defaultLinkValue);
23740 if(url && url != 'http:/'+'/'){
23741 this.editorcore.relayCmd('createlink', url);
23744 btn('list','insertunorderedlist',true);
23745 btn('pencil', false,true, function(btn){
23747 this.toggleSourceEdit(btn.pressed);
23750 if (this.editor.btns.length > 0) {
23751 for (var i = 0; i<this.editor.btns.length; i++) {
23752 children.push(this.editor.btns[i]);
23760 xns: Roo.bootstrap,
23765 xns: Roo.bootstrap,
23770 cog.menu.items.push({
23772 xns: Roo.bootstrap,
23773 html : Clean styles,
23778 editorcore.insertTag(this.tagname);
23787 this.xtype = 'NavSimplebar';
23789 for(var i=0;i< children.length;i++) {
23791 this.buttons.add(this.addxtypeChild(children[i]));
23795 editor.on('editorevent', this.updateToolbar, this);
23797 onBtnClick : function(id)
23799 this.editorcore.relayCmd(id);
23800 this.editorcore.focus();
23804 * Protected method that will not generally be called directly. It triggers
23805 * a toolbar update by reading the markup state of the current selection in the editor.
23807 updateToolbar: function(){
23809 if(!this.editorcore.activated){
23810 this.editor.onFirstFocus(); // is this neeed?
23814 var btns = this.buttons;
23815 var doc = this.editorcore.doc;
23816 btns.get('bold').setActive(doc.queryCommandState('bold'));
23817 btns.get('italic').setActive(doc.queryCommandState('italic'));
23818 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23820 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23821 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23822 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23824 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23825 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23828 var ans = this.editorcore.getAllAncestors();
23829 if (this.formatCombo) {
23832 var store = this.formatCombo.store;
23833 this.formatCombo.setValue("");
23834 for (var i =0; i < ans.length;i++) {
23835 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23837 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23845 // hides menus... - so this cant be on a menu...
23846 Roo.bootstrap.MenuMgr.hideAll();
23848 Roo.bootstrap.MenuMgr.hideAll();
23849 //this.editorsyncValue();
23851 onFirstFocus: function() {
23852 this.buttons.each(function(item){
23856 toggleSourceEdit : function(sourceEditMode){
23859 if(sourceEditMode){
23860 Roo.log("disabling buttons");
23861 this.buttons.each( function(item){
23862 if(item.cmd != 'pencil'){
23868 Roo.log("enabling buttons");
23869 if(this.editorcore.initialized){
23870 this.buttons.each( function(item){
23876 Roo.log("calling toggole on editor");
23877 // tell the editor that it's been pressed..
23878 this.editor.toggleSourceEdit(sourceEditMode);
23888 * @class Roo.bootstrap.Table.AbstractSelectionModel
23889 * @extends Roo.util.Observable
23890 * Abstract base class for grid SelectionModels. It provides the interface that should be
23891 * implemented by descendant classes. This class should not be directly instantiated.
23894 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23895 this.locked = false;
23896 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23900 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23901 /** @ignore Called by the grid automatically. Do not call directly. */
23902 init : function(grid){
23908 * Locks the selections.
23911 this.locked = true;
23915 * Unlocks the selections.
23917 unlock : function(){
23918 this.locked = false;
23922 * Returns true if the selections are locked.
23923 * @return {Boolean}
23925 isLocked : function(){
23926 return this.locked;
23930 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23931 * @class Roo.bootstrap.Table.RowSelectionModel
23932 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23933 * It supports multiple selections and keyboard selection/navigation.
23935 * @param {Object} config
23938 Roo.bootstrap.Table.RowSelectionModel = function(config){
23939 Roo.apply(this, config);
23940 this.selections = new Roo.util.MixedCollection(false, function(o){
23945 this.lastActive = false;
23949 * @event selectionchange
23950 * Fires when the selection changes
23951 * @param {SelectionModel} this
23953 "selectionchange" : true,
23955 * @event afterselectionchange
23956 * Fires after the selection changes (eg. by key press or clicking)
23957 * @param {SelectionModel} this
23959 "afterselectionchange" : true,
23961 * @event beforerowselect
23962 * Fires when a row is selected being selected, return false to cancel.
23963 * @param {SelectionModel} this
23964 * @param {Number} rowIndex The selected index
23965 * @param {Boolean} keepExisting False if other selections will be cleared
23967 "beforerowselect" : true,
23970 * Fires when a row is selected.
23971 * @param {SelectionModel} this
23972 * @param {Number} rowIndex The selected index
23973 * @param {Roo.data.Record} r The record
23975 "rowselect" : true,
23977 * @event rowdeselect
23978 * Fires when a row is deselected.
23979 * @param {SelectionModel} this
23980 * @param {Number} rowIndex The selected index
23982 "rowdeselect" : true
23984 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23985 this.locked = false;
23988 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
23990 * @cfg {Boolean} singleSelect
23991 * True to allow selection of only one row at a time (defaults to false)
23993 singleSelect : false,
23996 initEvents : function()
23999 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24000 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24001 //}else{ // allow click to work like normal
24002 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24004 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24005 this.grid.on("rowclick", this.handleMouseDown, this);
24007 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24008 "up" : function(e){
24010 this.selectPrevious(e.shiftKey);
24011 }else if(this.last !== false && this.lastActive !== false){
24012 var last = this.last;
24013 this.selectRange(this.last, this.lastActive-1);
24014 this.grid.getView().focusRow(this.lastActive);
24015 if(last !== false){
24019 this.selectFirstRow();
24021 this.fireEvent("afterselectionchange", this);
24023 "down" : function(e){
24025 this.selectNext(e.shiftKey);
24026 }else if(this.last !== false && this.lastActive !== false){
24027 var last = this.last;
24028 this.selectRange(this.last, this.lastActive+1);
24029 this.grid.getView().focusRow(this.lastActive);
24030 if(last !== false){
24034 this.selectFirstRow();
24036 this.fireEvent("afterselectionchange", this);
24040 this.grid.store.on('load', function(){
24041 this.selections.clear();
24044 var view = this.grid.view;
24045 view.on("refresh", this.onRefresh, this);
24046 view.on("rowupdated", this.onRowUpdated, this);
24047 view.on("rowremoved", this.onRemove, this);
24052 onRefresh : function()
24054 var ds = this.grid.store, i, v = this.grid.view;
24055 var s = this.selections;
24056 s.each(function(r){
24057 if((i = ds.indexOfId(r.id)) != -1){
24066 onRemove : function(v, index, r){
24067 this.selections.remove(r);
24071 onRowUpdated : function(v, index, r){
24072 if(this.isSelected(r)){
24073 v.onRowSelect(index);
24079 * @param {Array} records The records to select
24080 * @param {Boolean} keepExisting (optional) True to keep existing selections
24082 selectRecords : function(records, keepExisting)
24085 this.clearSelections();
24087 var ds = this.grid.store;
24088 for(var i = 0, len = records.length; i < len; i++){
24089 this.selectRow(ds.indexOf(records[i]), true);
24094 * Gets the number of selected rows.
24097 getCount : function(){
24098 return this.selections.length;
24102 * Selects the first row in the grid.
24104 selectFirstRow : function(){
24109 * Select the last row.
24110 * @param {Boolean} keepExisting (optional) True to keep existing selections
24112 selectLastRow : function(keepExisting){
24113 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24114 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24118 * Selects the row immediately following the last selected row.
24119 * @param {Boolean} keepExisting (optional) True to keep existing selections
24121 selectNext : function(keepExisting)
24123 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24124 this.selectRow(this.last+1, keepExisting);
24125 this.grid.getView().focusRow(this.last);
24130 * Selects the row that precedes the last selected row.
24131 * @param {Boolean} keepExisting (optional) True to keep existing selections
24133 selectPrevious : function(keepExisting){
24135 this.selectRow(this.last-1, keepExisting);
24136 this.grid.getView().focusRow(this.last);
24141 * Returns the selected records
24142 * @return {Array} Array of selected records
24144 getSelections : function(){
24145 return [].concat(this.selections.items);
24149 * Returns the first selected record.
24152 getSelected : function(){
24153 return this.selections.itemAt(0);
24158 * Clears all selections.
24160 clearSelections : function(fast)
24166 var ds = this.grid.store;
24167 var s = this.selections;
24168 s.each(function(r){
24169 this.deselectRow(ds.indexOfId(r.id));
24173 this.selections.clear();
24180 * Selects all rows.
24182 selectAll : function(){
24186 this.selections.clear();
24187 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24188 this.selectRow(i, true);
24193 * Returns True if there is a selection.
24194 * @return {Boolean}
24196 hasSelection : function(){
24197 return this.selections.length > 0;
24201 * Returns True if the specified row is selected.
24202 * @param {Number/Record} record The record or index of the record to check
24203 * @return {Boolean}
24205 isSelected : function(index){
24206 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24207 return (r && this.selections.key(r.id) ? true : false);
24211 * Returns True if the specified record id is selected.
24212 * @param {String} id The id of record to check
24213 * @return {Boolean}
24215 isIdSelected : function(id){
24216 return (this.selections.key(id) ? true : false);
24221 handleMouseDBClick : function(e, t){
24225 handleMouseDown : function(e, t)
24227 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24228 if(this.isLocked() || rowIndex < 0 ){
24231 if(e.shiftKey && this.last !== false){
24232 var last = this.last;
24233 this.selectRange(last, rowIndex, e.ctrlKey);
24234 this.last = last; // reset the last
24238 var isSelected = this.isSelected(rowIndex);
24239 //Roo.log("select row:" + rowIndex);
24241 this.deselectRow(rowIndex);
24243 this.selectRow(rowIndex, true);
24247 if(e.button !== 0 && isSelected){
24248 alert('rowIndex 2: ' + rowIndex);
24249 view.focusRow(rowIndex);
24250 }else if(e.ctrlKey && isSelected){
24251 this.deselectRow(rowIndex);
24252 }else if(!isSelected){
24253 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24254 view.focusRow(rowIndex);
24258 this.fireEvent("afterselectionchange", this);
24261 handleDragableRowClick : function(grid, rowIndex, e)
24263 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24264 this.selectRow(rowIndex, false);
24265 grid.view.focusRow(rowIndex);
24266 this.fireEvent("afterselectionchange", this);
24271 * Selects multiple rows.
24272 * @param {Array} rows Array of the indexes of the row to select
24273 * @param {Boolean} keepExisting (optional) True to keep existing selections
24275 selectRows : function(rows, keepExisting){
24277 this.clearSelections();
24279 for(var i = 0, len = rows.length; i < len; i++){
24280 this.selectRow(rows[i], true);
24285 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24286 * @param {Number} startRow The index of the first row in the range
24287 * @param {Number} endRow The index of the last row in the range
24288 * @param {Boolean} keepExisting (optional) True to retain existing selections
24290 selectRange : function(startRow, endRow, keepExisting){
24295 this.clearSelections();
24297 if(startRow <= endRow){
24298 for(var i = startRow; i <= endRow; i++){
24299 this.selectRow(i, true);
24302 for(var i = startRow; i >= endRow; i--){
24303 this.selectRow(i, true);
24309 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24310 * @param {Number} startRow The index of the first row in the range
24311 * @param {Number} endRow The index of the last row in the range
24313 deselectRange : function(startRow, endRow, preventViewNotify){
24317 for(var i = startRow; i <= endRow; i++){
24318 this.deselectRow(i, preventViewNotify);
24324 * @param {Number} row The index of the row to select
24325 * @param {Boolean} keepExisting (optional) True to keep existing selections
24327 selectRow : function(index, keepExisting, preventViewNotify)
24329 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24332 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24333 if(!keepExisting || this.singleSelect){
24334 this.clearSelections();
24337 var r = this.grid.store.getAt(index);
24338 //console.log('selectRow - record id :' + r.id);
24340 this.selections.add(r);
24341 this.last = this.lastActive = index;
24342 if(!preventViewNotify){
24343 var proxy = new Roo.Element(
24344 this.grid.getRowDom(index)
24346 proxy.addClass('bg-info info');
24348 this.fireEvent("rowselect", this, index, r);
24349 this.fireEvent("selectionchange", this);
24355 * @param {Number} row The index of the row to deselect
24357 deselectRow : function(index, preventViewNotify)
24362 if(this.last == index){
24365 if(this.lastActive == index){
24366 this.lastActive = false;
24369 var r = this.grid.store.getAt(index);
24374 this.selections.remove(r);
24375 //.console.log('deselectRow - record id :' + r.id);
24376 if(!preventViewNotify){
24378 var proxy = new Roo.Element(
24379 this.grid.getRowDom(index)
24381 proxy.removeClass('bg-info info');
24383 this.fireEvent("rowdeselect", this, index);
24384 this.fireEvent("selectionchange", this);
24388 restoreLast : function(){
24390 this.last = this._last;
24395 acceptsNav : function(row, col, cm){
24396 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24400 onEditorKey : function(field, e){
24401 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24406 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24408 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24410 }else if(k == e.ENTER && !e.ctrlKey){
24414 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24416 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24418 }else if(k == e.ESC){
24422 g.startEditing(newCell[0], newCell[1]);
24428 * Ext JS Library 1.1.1
24429 * Copyright(c) 2006-2007, Ext JS, LLC.
24431 * Originally Released Under LGPL - original licence link has changed is not relivant.
24434 * <script type="text/javascript">
24438 * @class Roo.bootstrap.PagingToolbar
24439 * @extends Roo.bootstrap.NavSimplebar
24440 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24442 * Create a new PagingToolbar
24443 * @param {Object} config The config object
24444 * @param {Roo.data.Store} store
24446 Roo.bootstrap.PagingToolbar = function(config)
24448 // old args format still supported... - xtype is prefered..
24449 // created from xtype...
24451 this.ds = config.dataSource;
24453 if (config.store && !this.ds) {
24454 this.store= Roo.factory(config.store, Roo.data);
24455 this.ds = this.store;
24456 this.ds.xmodule = this.xmodule || false;
24459 this.toolbarItems = [];
24460 if (config.items) {
24461 this.toolbarItems = config.items;
24464 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24469 this.bind(this.ds);
24472 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24476 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24478 * @cfg {Roo.data.Store} dataSource
24479 * The underlying data store providing the paged data
24482 * @cfg {String/HTMLElement/Element} container
24483 * container The id or element that will contain the toolbar
24486 * @cfg {Boolean} displayInfo
24487 * True to display the displayMsg (defaults to false)
24490 * @cfg {Number} pageSize
24491 * The number of records to display per page (defaults to 20)
24495 * @cfg {String} displayMsg
24496 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24498 displayMsg : 'Displaying {0} - {1} of {2}',
24500 * @cfg {String} emptyMsg
24501 * The message to display when no records are found (defaults to "No data to display")
24503 emptyMsg : 'No data to display',
24505 * Customizable piece of the default paging text (defaults to "Page")
24508 beforePageText : "Page",
24510 * Customizable piece of the default paging text (defaults to "of %0")
24513 afterPageText : "of {0}",
24515 * Customizable piece of the default paging text (defaults to "First Page")
24518 firstText : "First Page",
24520 * Customizable piece of the default paging text (defaults to "Previous Page")
24523 prevText : "Previous Page",
24525 * Customizable piece of the default paging text (defaults to "Next Page")
24528 nextText : "Next Page",
24530 * Customizable piece of the default paging text (defaults to "Last Page")
24533 lastText : "Last Page",
24535 * Customizable piece of the default paging text (defaults to "Refresh")
24538 refreshText : "Refresh",
24542 onRender : function(ct, position)
24544 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24545 this.navgroup.parentId = this.id;
24546 this.navgroup.onRender(this.el, null);
24547 // add the buttons to the navgroup
24549 if(this.displayInfo){
24550 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24551 this.displayEl = this.el.select('.x-paging-info', true).first();
24552 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24553 // this.displayEl = navel.el.select('span',true).first();
24559 Roo.each(_this.buttons, function(e){ // this might need to use render????
24560 Roo.factory(e).render(_this.el);
24564 Roo.each(_this.toolbarItems, function(e) {
24565 _this.navgroup.addItem(e);
24569 this.first = this.navgroup.addItem({
24570 tooltip: this.firstText,
24572 icon : 'fa fa-backward',
24574 preventDefault: true,
24575 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24578 this.prev = this.navgroup.addItem({
24579 tooltip: this.prevText,
24581 icon : 'fa fa-step-backward',
24583 preventDefault: true,
24584 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24586 //this.addSeparator();
24589 var field = this.navgroup.addItem( {
24591 cls : 'x-paging-position',
24593 html : this.beforePageText +
24594 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24595 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24598 this.field = field.el.select('input', true).first();
24599 this.field.on("keydown", this.onPagingKeydown, this);
24600 this.field.on("focus", function(){this.dom.select();});
24603 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24604 //this.field.setHeight(18);
24605 //this.addSeparator();
24606 this.next = this.navgroup.addItem({
24607 tooltip: this.nextText,
24609 html : ' <i class="fa fa-step-forward">',
24611 preventDefault: true,
24612 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24614 this.last = this.navgroup.addItem({
24615 tooltip: this.lastText,
24616 icon : 'fa fa-forward',
24619 preventDefault: true,
24620 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24622 //this.addSeparator();
24623 this.loading = this.navgroup.addItem({
24624 tooltip: this.refreshText,
24625 icon: 'fa fa-refresh',
24626 preventDefault: true,
24627 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24633 updateInfo : function(){
24634 if(this.displayEl){
24635 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24636 var msg = count == 0 ?
24640 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24642 this.displayEl.update(msg);
24647 onLoad : function(ds, r, o)
24649 this.cursor = o.params.start ? o.params.start : 0;
24651 var d = this.getPageData(),
24656 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24657 this.field.dom.value = ap;
24658 this.first.setDisabled(ap == 1);
24659 this.prev.setDisabled(ap == 1);
24660 this.next.setDisabled(ap == ps);
24661 this.last.setDisabled(ap == ps);
24662 this.loading.enable();
24667 getPageData : function(){
24668 var total = this.ds.getTotalCount();
24671 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24672 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24677 onLoadError : function(){
24678 this.loading.enable();
24682 onPagingKeydown : function(e){
24683 var k = e.getKey();
24684 var d = this.getPageData();
24686 var v = this.field.dom.value, pageNum;
24687 if(!v || isNaN(pageNum = parseInt(v, 10))){
24688 this.field.dom.value = d.activePage;
24691 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24692 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24695 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))
24697 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24698 this.field.dom.value = pageNum;
24699 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24702 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24704 var v = this.field.dom.value, pageNum;
24705 var increment = (e.shiftKey) ? 10 : 1;
24706 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24709 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24710 this.field.dom.value = d.activePage;
24713 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24715 this.field.dom.value = parseInt(v, 10) + increment;
24716 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24717 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24724 beforeLoad : function(){
24726 this.loading.disable();
24731 onClick : function(which){
24740 ds.load({params:{start: 0, limit: this.pageSize}});
24743 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24746 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24749 var total = ds.getTotalCount();
24750 var extra = total % this.pageSize;
24751 var lastStart = extra ? (total - extra) : total-this.pageSize;
24752 ds.load({params:{start: lastStart, limit: this.pageSize}});
24755 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24761 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24762 * @param {Roo.data.Store} store The data store to unbind
24764 unbind : function(ds){
24765 ds.un("beforeload", this.beforeLoad, this);
24766 ds.un("load", this.onLoad, this);
24767 ds.un("loadexception", this.onLoadError, this);
24768 ds.un("remove", this.updateInfo, this);
24769 ds.un("add", this.updateInfo, this);
24770 this.ds = undefined;
24774 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24775 * @param {Roo.data.Store} store The data store to bind
24777 bind : function(ds){
24778 ds.on("beforeload", this.beforeLoad, this);
24779 ds.on("load", this.onLoad, this);
24780 ds.on("loadexception", this.onLoadError, this);
24781 ds.on("remove", this.updateInfo, this);
24782 ds.on("add", this.updateInfo, this);
24793 * @class Roo.bootstrap.MessageBar
24794 * @extends Roo.bootstrap.Component
24795 * Bootstrap MessageBar class
24796 * @cfg {String} html contents of the MessageBar
24797 * @cfg {String} weight (info | success | warning | danger) default info
24798 * @cfg {String} beforeClass insert the bar before the given class
24799 * @cfg {Boolean} closable (true | false) default false
24800 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24803 * Create a new Element
24804 * @param {Object} config The config object
24807 Roo.bootstrap.MessageBar = function(config){
24808 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24811 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24817 beforeClass: 'bootstrap-sticky-wrap',
24819 getAutoCreate : function(){
24823 cls: 'alert alert-dismissable alert-' + this.weight,
24828 html: this.html || ''
24834 cfg.cls += ' alert-messages-fixed';
24848 onRender : function(ct, position)
24850 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24853 var cfg = Roo.apply({}, this.getAutoCreate());
24857 cfg.cls += ' ' + this.cls;
24860 cfg.style = this.style;
24862 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24864 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24867 this.el.select('>button.close').on('click', this.hide, this);
24873 if (!this.rendered) {
24879 this.fireEvent('show', this);
24885 if (!this.rendered) {
24891 this.fireEvent('hide', this);
24894 update : function()
24896 // var e = this.el.dom.firstChild;
24898 // if(this.closable){
24899 // e = e.nextSibling;
24902 // e.data = this.html || '';
24904 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24920 * @class Roo.bootstrap.Graph
24921 * @extends Roo.bootstrap.Component
24922 * Bootstrap Graph class
24926 @cfg {String} graphtype bar | vbar | pie
24927 @cfg {number} g_x coodinator | centre x (pie)
24928 @cfg {number} g_y coodinator | centre y (pie)
24929 @cfg {number} g_r radius (pie)
24930 @cfg {number} g_height height of the chart (respected by all elements in the set)
24931 @cfg {number} g_width width of the chart (respected by all elements in the set)
24932 @cfg {Object} title The title of the chart
24935 -opts (object) options for the chart
24937 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24938 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24940 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.
24941 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24943 o stretch (boolean)
24945 -opts (object) options for the pie
24948 o startAngle (number)
24949 o endAngle (number)
24953 * Create a new Input
24954 * @param {Object} config The config object
24957 Roo.bootstrap.Graph = function(config){
24958 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24964 * The img click event for the img.
24965 * @param {Roo.EventObject} e
24971 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24982 //g_colors: this.colors,
24989 getAutoCreate : function(){
25000 onRender : function(ct,position){
25003 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25005 if (typeof(Raphael) == 'undefined') {
25006 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25010 this.raphael = Raphael(this.el.dom);
25012 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25013 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25014 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25015 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25017 r.text(160, 10, "Single Series Chart").attr(txtattr);
25018 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25019 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25020 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25022 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25023 r.barchart(330, 10, 300, 220, data1);
25024 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25025 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25028 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25029 // r.barchart(30, 30, 560, 250, xdata, {
25030 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25031 // axis : "0 0 1 1",
25032 // axisxlabels : xdata
25033 // //yvalues : cols,
25036 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25038 // this.load(null,xdata,{
25039 // axis : "0 0 1 1",
25040 // axisxlabels : xdata
25045 load : function(graphtype,xdata,opts)
25047 this.raphael.clear();
25049 graphtype = this.graphtype;
25054 var r = this.raphael,
25055 fin = function () {
25056 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25058 fout = function () {
25059 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25061 pfin = function() {
25062 this.sector.stop();
25063 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25066 this.label[0].stop();
25067 this.label[0].attr({ r: 7.5 });
25068 this.label[1].attr({ "font-weight": 800 });
25071 pfout = function() {
25072 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25075 this.label[0].animate({ r: 5 }, 500, "bounce");
25076 this.label[1].attr({ "font-weight": 400 });
25082 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25085 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25088 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25089 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25091 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25098 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25103 setTitle: function(o)
25108 initEvents: function() {
25111 this.el.on('click', this.onClick, this);
25115 onClick : function(e)
25117 Roo.log('img onclick');
25118 this.fireEvent('click', this, e);
25130 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25133 * @class Roo.bootstrap.dash.NumberBox
25134 * @extends Roo.bootstrap.Component
25135 * Bootstrap NumberBox class
25136 * @cfg {String} headline Box headline
25137 * @cfg {String} content Box content
25138 * @cfg {String} icon Box icon
25139 * @cfg {String} footer Footer text
25140 * @cfg {String} fhref Footer href
25143 * Create a new NumberBox
25144 * @param {Object} config The config object
25148 Roo.bootstrap.dash.NumberBox = function(config){
25149 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25153 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25162 getAutoCreate : function(){
25166 cls : 'small-box ',
25174 cls : 'roo-headline',
25175 html : this.headline
25179 cls : 'roo-content',
25180 html : this.content
25194 cls : 'ion ' + this.icon
25203 cls : 'small-box-footer',
25204 href : this.fhref || '#',
25208 cfg.cn.push(footer);
25215 onRender : function(ct,position){
25216 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25223 setHeadline: function (value)
25225 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25228 setFooter: function (value, href)
25230 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25233 this.el.select('a.small-box-footer',true).first().attr('href', href);
25238 setContent: function (value)
25240 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25243 initEvents: function()
25257 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25260 * @class Roo.bootstrap.dash.TabBox
25261 * @extends Roo.bootstrap.Component
25262 * Bootstrap TabBox class
25263 * @cfg {String} title Title of the TabBox
25264 * @cfg {String} icon Icon of the TabBox
25265 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25266 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25269 * Create a new TabBox
25270 * @param {Object} config The config object
25274 Roo.bootstrap.dash.TabBox = function(config){
25275 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25280 * When a pane is added
25281 * @param {Roo.bootstrap.dash.TabPane} pane
25285 * @event activatepane
25286 * When a pane is activated
25287 * @param {Roo.bootstrap.dash.TabPane} pane
25289 "activatepane" : true
25297 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25302 tabScrollable : false,
25304 getChildContainer : function()
25306 return this.el.select('.tab-content', true).first();
25309 getAutoCreate : function(){
25313 cls: 'pull-left header',
25321 cls: 'fa ' + this.icon
25327 cls: 'nav nav-tabs pull-right',
25333 if(this.tabScrollable){
25340 cls: 'nav nav-tabs pull-right',
25351 cls: 'nav-tabs-custom',
25356 cls: 'tab-content no-padding',
25364 initEvents : function()
25366 //Roo.log('add add pane handler');
25367 this.on('addpane', this.onAddPane, this);
25370 * Updates the box title
25371 * @param {String} html to set the title to.
25373 setTitle : function(value)
25375 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25377 onAddPane : function(pane)
25379 this.panes.push(pane);
25380 //Roo.log('addpane');
25382 // tabs are rendere left to right..
25383 if(!this.showtabs){
25387 var ctr = this.el.select('.nav-tabs', true).first();
25390 var existing = ctr.select('.nav-tab',true);
25391 var qty = existing.getCount();;
25394 var tab = ctr.createChild({
25396 cls : 'nav-tab' + (qty ? '' : ' active'),
25404 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25407 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25409 pane.el.addClass('active');
25414 onTabClick : function(ev,un,ob,pane)
25416 //Roo.log('tab - prev default');
25417 ev.preventDefault();
25420 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25421 pane.tab.addClass('active');
25422 //Roo.log(pane.title);
25423 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25424 // technically we should have a deactivate event.. but maybe add later.
25425 // and it should not de-activate the selected tab...
25426 this.fireEvent('activatepane', pane);
25427 pane.el.addClass('active');
25428 pane.fireEvent('activate');
25433 getActivePane : function()
25436 Roo.each(this.panes, function(p) {
25437 if(p.el.hasClass('active')){
25458 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25460 * @class Roo.bootstrap.TabPane
25461 * @extends Roo.bootstrap.Component
25462 * Bootstrap TabPane class
25463 * @cfg {Boolean} active (false | true) Default false
25464 * @cfg {String} title title of panel
25468 * Create a new TabPane
25469 * @param {Object} config The config object
25472 Roo.bootstrap.dash.TabPane = function(config){
25473 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25479 * When a pane is activated
25480 * @param {Roo.bootstrap.dash.TabPane} pane
25487 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25492 // the tabBox that this is attached to.
25495 getAutoCreate : function()
25503 cfg.cls += ' active';
25508 initEvents : function()
25510 //Roo.log('trigger add pane handler');
25511 this.parent().fireEvent('addpane', this)
25515 * Updates the tab title
25516 * @param {String} html to set the title to.
25518 setTitle: function(str)
25524 this.tab.select('a', true).first().dom.innerHTML = str;
25541 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25544 * @class Roo.bootstrap.menu.Menu
25545 * @extends Roo.bootstrap.Component
25546 * Bootstrap Menu class - container for Menu
25547 * @cfg {String} html Text of the menu
25548 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25549 * @cfg {String} icon Font awesome icon
25550 * @cfg {String} pos Menu align to (top | bottom) default bottom
25554 * Create a new Menu
25555 * @param {Object} config The config object
25559 Roo.bootstrap.menu.Menu = function(config){
25560 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25564 * @event beforeshow
25565 * Fires before this menu is displayed
25566 * @param {Roo.bootstrap.menu.Menu} this
25570 * @event beforehide
25571 * Fires before this menu is hidden
25572 * @param {Roo.bootstrap.menu.Menu} this
25577 * Fires after this menu is displayed
25578 * @param {Roo.bootstrap.menu.Menu} this
25583 * Fires after this menu is hidden
25584 * @param {Roo.bootstrap.menu.Menu} this
25589 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25590 * @param {Roo.bootstrap.menu.Menu} this
25591 * @param {Roo.EventObject} e
25598 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25602 weight : 'default',
25607 getChildContainer : function() {
25608 if(this.isSubMenu){
25612 return this.el.select('ul.dropdown-menu', true).first();
25615 getAutoCreate : function()
25620 cls : 'roo-menu-text',
25628 cls : 'fa ' + this.icon
25639 cls : 'dropdown-button btn btn-' + this.weight,
25644 cls : 'dropdown-toggle btn btn-' + this.weight,
25654 cls : 'dropdown-menu'
25660 if(this.pos == 'top'){
25661 cfg.cls += ' dropup';
25664 if(this.isSubMenu){
25667 cls : 'dropdown-menu'
25674 onRender : function(ct, position)
25676 this.isSubMenu = ct.hasClass('dropdown-submenu');
25678 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25681 initEvents : function()
25683 if(this.isSubMenu){
25687 this.hidden = true;
25689 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25690 this.triggerEl.on('click', this.onTriggerPress, this);
25692 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25693 this.buttonEl.on('click', this.onClick, this);
25699 if(this.isSubMenu){
25703 return this.el.select('ul.dropdown-menu', true).first();
25706 onClick : function(e)
25708 this.fireEvent("click", this, e);
25711 onTriggerPress : function(e)
25713 if (this.isVisible()) {
25720 isVisible : function(){
25721 return !this.hidden;
25726 this.fireEvent("beforeshow", this);
25728 this.hidden = false;
25729 this.el.addClass('open');
25731 Roo.get(document).on("mouseup", this.onMouseUp, this);
25733 this.fireEvent("show", this);
25740 this.fireEvent("beforehide", this);
25742 this.hidden = true;
25743 this.el.removeClass('open');
25745 Roo.get(document).un("mouseup", this.onMouseUp);
25747 this.fireEvent("hide", this);
25750 onMouseUp : function()
25764 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25767 * @class Roo.bootstrap.menu.Item
25768 * @extends Roo.bootstrap.Component
25769 * Bootstrap MenuItem class
25770 * @cfg {Boolean} submenu (true | false) default false
25771 * @cfg {String} html text of the item
25772 * @cfg {String} href the link
25773 * @cfg {Boolean} disable (true | false) default false
25774 * @cfg {Boolean} preventDefault (true | false) default true
25775 * @cfg {String} icon Font awesome icon
25776 * @cfg {String} pos Submenu align to (left | right) default right
25780 * Create a new Item
25781 * @param {Object} config The config object
25785 Roo.bootstrap.menu.Item = function(config){
25786 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25790 * Fires when the mouse is hovering over this menu
25791 * @param {Roo.bootstrap.menu.Item} this
25792 * @param {Roo.EventObject} e
25797 * Fires when the mouse exits this menu
25798 * @param {Roo.bootstrap.menu.Item} this
25799 * @param {Roo.EventObject} e
25805 * The raw click event for the entire grid.
25806 * @param {Roo.EventObject} e
25812 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25817 preventDefault: true,
25822 getAutoCreate : function()
25827 cls : 'roo-menu-item-text',
25835 cls : 'fa ' + this.icon
25844 href : this.href || '#',
25851 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25855 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25857 if(this.pos == 'left'){
25858 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25865 initEvents : function()
25867 this.el.on('mouseover', this.onMouseOver, this);
25868 this.el.on('mouseout', this.onMouseOut, this);
25870 this.el.select('a', true).first().on('click', this.onClick, this);
25874 onClick : function(e)
25876 if(this.preventDefault){
25877 e.preventDefault();
25880 this.fireEvent("click", this, e);
25883 onMouseOver : function(e)
25885 if(this.submenu && this.pos == 'left'){
25886 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25889 this.fireEvent("mouseover", this, e);
25892 onMouseOut : function(e)
25894 this.fireEvent("mouseout", this, e);
25906 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25909 * @class Roo.bootstrap.menu.Separator
25910 * @extends Roo.bootstrap.Component
25911 * Bootstrap Separator class
25914 * Create a new Separator
25915 * @param {Object} config The config object
25919 Roo.bootstrap.menu.Separator = function(config){
25920 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25923 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25925 getAutoCreate : function(){
25946 * @class Roo.bootstrap.Tooltip
25947 * Bootstrap Tooltip class
25948 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25949 * to determine which dom element triggers the tooltip.
25951 * It needs to add support for additional attributes like tooltip-position
25954 * Create a new Toolti
25955 * @param {Object} config The config object
25958 Roo.bootstrap.Tooltip = function(config){
25959 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25961 this.alignment = Roo.bootstrap.Tooltip.alignment;
25963 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25964 this.alignment = config.alignment;
25969 Roo.apply(Roo.bootstrap.Tooltip, {
25971 * @function init initialize tooltip monitoring.
25975 currentTip : false,
25976 currentRegion : false,
25982 Roo.get(document).on('mouseover', this.enter ,this);
25983 Roo.get(document).on('mouseout', this.leave, this);
25986 this.currentTip = new Roo.bootstrap.Tooltip();
25989 enter : function(ev)
25991 var dom = ev.getTarget();
25993 //Roo.log(['enter',dom]);
25994 var el = Roo.fly(dom);
25995 if (this.currentEl) {
25997 //Roo.log(this.currentEl);
25998 //Roo.log(this.currentEl.contains(dom));
25999 if (this.currentEl == el) {
26002 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26008 if (this.currentTip.el) {
26009 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26013 if(!el || el.dom == document){
26019 // you can not look for children, as if el is the body.. then everythign is the child..
26020 if (!el.attr('tooltip')) { //
26021 if (!el.select("[tooltip]").elements.length) {
26024 // is the mouse over this child...?
26025 bindEl = el.select("[tooltip]").first();
26026 var xy = ev.getXY();
26027 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26028 //Roo.log("not in region.");
26031 //Roo.log("child element over..");
26034 this.currentEl = bindEl;
26035 this.currentTip.bind(bindEl);
26036 this.currentRegion = Roo.lib.Region.getRegion(dom);
26037 this.currentTip.enter();
26040 leave : function(ev)
26042 var dom = ev.getTarget();
26043 //Roo.log(['leave',dom]);
26044 if (!this.currentEl) {
26049 if (dom != this.currentEl.dom) {
26052 var xy = ev.getXY();
26053 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26056 // only activate leave if mouse cursor is outside... bounding box..
26061 if (this.currentTip) {
26062 this.currentTip.leave();
26064 //Roo.log('clear currentEl');
26065 this.currentEl = false;
26070 'left' : ['r-l', [-2,0], 'right'],
26071 'right' : ['l-r', [2,0], 'left'],
26072 'bottom' : ['t-b', [0,2], 'top'],
26073 'top' : [ 'b-t', [0,-2], 'bottom']
26079 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26084 delay : null, // can be { show : 300 , hide: 500}
26088 hoverState : null, //???
26090 placement : 'bottom',
26094 getAutoCreate : function(){
26101 cls : 'tooltip-arrow'
26104 cls : 'tooltip-inner'
26111 bind : function(el)
26117 enter : function () {
26119 if (this.timeout != null) {
26120 clearTimeout(this.timeout);
26123 this.hoverState = 'in';
26124 //Roo.log("enter - show");
26125 if (!this.delay || !this.delay.show) {
26130 this.timeout = setTimeout(function () {
26131 if (_t.hoverState == 'in') {
26134 }, this.delay.show);
26138 clearTimeout(this.timeout);
26140 this.hoverState = 'out';
26141 if (!this.delay || !this.delay.hide) {
26147 this.timeout = setTimeout(function () {
26148 //Roo.log("leave - timeout");
26150 if (_t.hoverState == 'out') {
26152 Roo.bootstrap.Tooltip.currentEl = false;
26157 show : function (msg)
26160 this.render(document.body);
26163 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26165 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26167 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26169 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26171 var placement = typeof this.placement == 'function' ?
26172 this.placement.call(this, this.el, on_el) :
26175 var autoToken = /\s?auto?\s?/i;
26176 var autoPlace = autoToken.test(placement);
26178 placement = placement.replace(autoToken, '') || 'top';
26182 //this.el.setXY([0,0]);
26184 //this.el.dom.style.display='block';
26186 //this.el.appendTo(on_el);
26188 var p = this.getPosition();
26189 var box = this.el.getBox();
26195 var align = this.alignment[placement];
26197 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26199 if(placement == 'top' || placement == 'bottom'){
26201 placement = 'right';
26204 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26205 placement = 'left';
26208 var scroll = Roo.select('body', true).first().getScroll();
26210 Roo.log(this.el.getHeight());
26212 if(xy[1] + this.el.getHeight() >= Roo.lib.Dom.getViewHeight() + scroll.top){
26218 this.el.alignTo(this.bindEl, align[0],align[1]);
26219 //var arrow = this.el.select('.arrow',true).first();
26220 //arrow.set(align[2],
26222 this.el.addClass(placement);
26224 this.el.addClass('in fade');
26226 this.hoverState = null;
26228 if (this.el.hasClass('fade')) {
26239 //this.el.setXY([0,0]);
26240 this.el.removeClass('in');
26256 * @class Roo.bootstrap.LocationPicker
26257 * @extends Roo.bootstrap.Component
26258 * Bootstrap LocationPicker class
26259 * @cfg {Number} latitude Position when init default 0
26260 * @cfg {Number} longitude Position when init default 0
26261 * @cfg {Number} zoom default 15
26262 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26263 * @cfg {Boolean} mapTypeControl default false
26264 * @cfg {Boolean} disableDoubleClickZoom default false
26265 * @cfg {Boolean} scrollwheel default true
26266 * @cfg {Boolean} streetViewControl default false
26267 * @cfg {Number} radius default 0
26268 * @cfg {String} locationName
26269 * @cfg {Boolean} draggable default true
26270 * @cfg {Boolean} enableAutocomplete default false
26271 * @cfg {Boolean} enableReverseGeocode default true
26272 * @cfg {String} markerTitle
26275 * Create a new LocationPicker
26276 * @param {Object} config The config object
26280 Roo.bootstrap.LocationPicker = function(config){
26282 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26287 * Fires when the picker initialized.
26288 * @param {Roo.bootstrap.LocationPicker} this
26289 * @param {Google Location} location
26293 * @event positionchanged
26294 * Fires when the picker position changed.
26295 * @param {Roo.bootstrap.LocationPicker} this
26296 * @param {Google Location} location
26298 positionchanged : true,
26301 * Fires when the map resize.
26302 * @param {Roo.bootstrap.LocationPicker} this
26307 * Fires when the map show.
26308 * @param {Roo.bootstrap.LocationPicker} this
26313 * Fires when the map hide.
26314 * @param {Roo.bootstrap.LocationPicker} this
26319 * Fires when click the map.
26320 * @param {Roo.bootstrap.LocationPicker} this
26321 * @param {Map event} e
26325 * @event mapRightClick
26326 * Fires when right click the map.
26327 * @param {Roo.bootstrap.LocationPicker} this
26328 * @param {Map event} e
26330 mapRightClick : true,
26332 * @event markerClick
26333 * Fires when click the marker.
26334 * @param {Roo.bootstrap.LocationPicker} this
26335 * @param {Map event} e
26337 markerClick : true,
26339 * @event markerRightClick
26340 * Fires when right click the marker.
26341 * @param {Roo.bootstrap.LocationPicker} this
26342 * @param {Map event} e
26344 markerRightClick : true,
26346 * @event OverlayViewDraw
26347 * Fires when OverlayView Draw
26348 * @param {Roo.bootstrap.LocationPicker} this
26350 OverlayViewDraw : true,
26352 * @event OverlayViewOnAdd
26353 * Fires when OverlayView Draw
26354 * @param {Roo.bootstrap.LocationPicker} this
26356 OverlayViewOnAdd : true,
26358 * @event OverlayViewOnRemove
26359 * Fires when OverlayView Draw
26360 * @param {Roo.bootstrap.LocationPicker} this
26362 OverlayViewOnRemove : true,
26364 * @event OverlayViewShow
26365 * Fires when OverlayView Draw
26366 * @param {Roo.bootstrap.LocationPicker} this
26367 * @param {Pixel} cpx
26369 OverlayViewShow : true,
26371 * @event OverlayViewHide
26372 * Fires when OverlayView Draw
26373 * @param {Roo.bootstrap.LocationPicker} this
26375 OverlayViewHide : true,
26377 * @event loadexception
26378 * Fires when load google lib failed.
26379 * @param {Roo.bootstrap.LocationPicker} this
26381 loadexception : true
26386 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26388 gMapContext: false,
26394 mapTypeControl: false,
26395 disableDoubleClickZoom: false,
26397 streetViewControl: false,
26401 enableAutocomplete: false,
26402 enableReverseGeocode: true,
26405 getAutoCreate: function()
26410 cls: 'roo-location-picker'
26416 initEvents: function(ct, position)
26418 if(!this.el.getWidth() || this.isApplied()){
26422 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26427 initial: function()
26429 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26430 this.fireEvent('loadexception', this);
26434 if(!this.mapTypeId){
26435 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26438 this.gMapContext = this.GMapContext();
26440 this.initOverlayView();
26442 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26446 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26447 _this.setPosition(_this.gMapContext.marker.position);
26450 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26451 _this.fireEvent('mapClick', this, event);
26455 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26456 _this.fireEvent('mapRightClick', this, event);
26460 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26461 _this.fireEvent('markerClick', this, event);
26465 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26466 _this.fireEvent('markerRightClick', this, event);
26470 this.setPosition(this.gMapContext.location);
26472 this.fireEvent('initial', this, this.gMapContext.location);
26475 initOverlayView: function()
26479 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26483 _this.fireEvent('OverlayViewDraw', _this);
26488 _this.fireEvent('OverlayViewOnAdd', _this);
26491 onRemove: function()
26493 _this.fireEvent('OverlayViewOnRemove', _this);
26496 show: function(cpx)
26498 _this.fireEvent('OverlayViewShow', _this, cpx);
26503 _this.fireEvent('OverlayViewHide', _this);
26509 fromLatLngToContainerPixel: function(event)
26511 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26514 isApplied: function()
26516 return this.getGmapContext() == false ? false : true;
26519 getGmapContext: function()
26521 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26524 GMapContext: function()
26526 var position = new google.maps.LatLng(this.latitude, this.longitude);
26528 var _map = new google.maps.Map(this.el.dom, {
26531 mapTypeId: this.mapTypeId,
26532 mapTypeControl: this.mapTypeControl,
26533 disableDoubleClickZoom: this.disableDoubleClickZoom,
26534 scrollwheel: this.scrollwheel,
26535 streetViewControl: this.streetViewControl,
26536 locationName: this.locationName,
26537 draggable: this.draggable,
26538 enableAutocomplete: this.enableAutocomplete,
26539 enableReverseGeocode: this.enableReverseGeocode
26542 var _marker = new google.maps.Marker({
26543 position: position,
26545 title: this.markerTitle,
26546 draggable: this.draggable
26553 location: position,
26554 radius: this.radius,
26555 locationName: this.locationName,
26556 addressComponents: {
26557 formatted_address: null,
26558 addressLine1: null,
26559 addressLine2: null,
26561 streetNumber: null,
26565 stateOrProvince: null
26568 domContainer: this.el.dom,
26569 geodecoder: new google.maps.Geocoder()
26573 drawCircle: function(center, radius, options)
26575 if (this.gMapContext.circle != null) {
26576 this.gMapContext.circle.setMap(null);
26580 options = Roo.apply({}, options, {
26581 strokeColor: "#0000FF",
26582 strokeOpacity: .35,
26584 fillColor: "#0000FF",
26588 options.map = this.gMapContext.map;
26589 options.radius = radius;
26590 options.center = center;
26591 this.gMapContext.circle = new google.maps.Circle(options);
26592 return this.gMapContext.circle;
26598 setPosition: function(location)
26600 this.gMapContext.location = location;
26601 this.gMapContext.marker.setPosition(location);
26602 this.gMapContext.map.panTo(location);
26603 this.drawCircle(location, this.gMapContext.radius, {});
26607 if (this.gMapContext.settings.enableReverseGeocode) {
26608 this.gMapContext.geodecoder.geocode({
26609 latLng: this.gMapContext.location
26610 }, function(results, status) {
26612 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26613 _this.gMapContext.locationName = results[0].formatted_address;
26614 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26616 _this.fireEvent('positionchanged', this, location);
26623 this.fireEvent('positionchanged', this, location);
26628 google.maps.event.trigger(this.gMapContext.map, "resize");
26630 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26632 this.fireEvent('resize', this);
26635 setPositionByLatLng: function(latitude, longitude)
26637 this.setPosition(new google.maps.LatLng(latitude, longitude));
26640 getCurrentPosition: function()
26643 latitude: this.gMapContext.location.lat(),
26644 longitude: this.gMapContext.location.lng()
26648 getAddressName: function()
26650 return this.gMapContext.locationName;
26653 getAddressComponents: function()
26655 return this.gMapContext.addressComponents;
26658 address_component_from_google_geocode: function(address_components)
26662 for (var i = 0; i < address_components.length; i++) {
26663 var component = address_components[i];
26664 if (component.types.indexOf("postal_code") >= 0) {
26665 result.postalCode = component.short_name;
26666 } else if (component.types.indexOf("street_number") >= 0) {
26667 result.streetNumber = component.short_name;
26668 } else if (component.types.indexOf("route") >= 0) {
26669 result.streetName = component.short_name;
26670 } else if (component.types.indexOf("neighborhood") >= 0) {
26671 result.city = component.short_name;
26672 } else if (component.types.indexOf("locality") >= 0) {
26673 result.city = component.short_name;
26674 } else if (component.types.indexOf("sublocality") >= 0) {
26675 result.district = component.short_name;
26676 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26677 result.stateOrProvince = component.short_name;
26678 } else if (component.types.indexOf("country") >= 0) {
26679 result.country = component.short_name;
26683 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26684 result.addressLine2 = "";
26688 setZoomLevel: function(zoom)
26690 this.gMapContext.map.setZoom(zoom);
26703 this.fireEvent('show', this);
26714 this.fireEvent('hide', this);
26719 Roo.apply(Roo.bootstrap.LocationPicker, {
26721 OverlayView : function(map, options)
26723 options = options || {};
26737 * @class Roo.bootstrap.Alert
26738 * @extends Roo.bootstrap.Component
26739 * Bootstrap Alert class
26740 * @cfg {String} title The title of alert
26741 * @cfg {String} html The content of alert
26742 * @cfg {String} weight ( success | info | warning | danger )
26743 * @cfg {String} faicon font-awesomeicon
26746 * Create a new alert
26747 * @param {Object} config The config object
26751 Roo.bootstrap.Alert = function(config){
26752 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26756 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26763 getAutoCreate : function()
26772 cls : 'roo-alert-icon'
26777 cls : 'roo-alert-title',
26782 cls : 'roo-alert-text',
26789 cfg.cn[0].cls += ' fa ' + this.faicon;
26793 cfg.cls += ' alert-' + this.weight;
26799 initEvents: function()
26801 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26804 setTitle : function(str)
26806 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26809 setText : function(str)
26811 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26814 setWeight : function(weight)
26817 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26820 this.weight = weight;
26822 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26825 setIcon : function(icon)
26828 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26831 this.faicon = icon;
26833 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26854 * @class Roo.bootstrap.UploadCropbox
26855 * @extends Roo.bootstrap.Component
26856 * Bootstrap UploadCropbox class
26857 * @cfg {String} emptyText show when image has been loaded
26858 * @cfg {String} rotateNotify show when image too small to rotate
26859 * @cfg {Number} errorTimeout default 3000
26860 * @cfg {Number} minWidth default 300
26861 * @cfg {Number} minHeight default 300
26862 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26863 * @cfg {Boolean} isDocument (true|false) default false
26864 * @cfg {String} url action url
26865 * @cfg {String} paramName default 'imageUpload'
26866 * @cfg {String} method default POST
26867 * @cfg {Boolean} loadMask (true|false) default true
26868 * @cfg {Boolean} loadingText default 'Loading...'
26871 * Create a new UploadCropbox
26872 * @param {Object} config The config object
26875 Roo.bootstrap.UploadCropbox = function(config){
26876 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26880 * @event beforeselectfile
26881 * Fire before select file
26882 * @param {Roo.bootstrap.UploadCropbox} this
26884 "beforeselectfile" : true,
26887 * Fire after initEvent
26888 * @param {Roo.bootstrap.UploadCropbox} this
26893 * Fire after initEvent
26894 * @param {Roo.bootstrap.UploadCropbox} this
26895 * @param {String} data
26900 * Fire when preparing the file data
26901 * @param {Roo.bootstrap.UploadCropbox} this
26902 * @param {Object} file
26907 * Fire when get exception
26908 * @param {Roo.bootstrap.UploadCropbox} this
26909 * @param {XMLHttpRequest} xhr
26911 "exception" : true,
26913 * @event beforeloadcanvas
26914 * Fire before load the canvas
26915 * @param {Roo.bootstrap.UploadCropbox} this
26916 * @param {String} src
26918 "beforeloadcanvas" : true,
26921 * Fire when trash image
26922 * @param {Roo.bootstrap.UploadCropbox} this
26927 * Fire when download the image
26928 * @param {Roo.bootstrap.UploadCropbox} this
26932 * @event footerbuttonclick
26933 * Fire when footerbuttonclick
26934 * @param {Roo.bootstrap.UploadCropbox} this
26935 * @param {String} type
26937 "footerbuttonclick" : true,
26941 * @param {Roo.bootstrap.UploadCropbox} this
26946 * Fire when rotate the image
26947 * @param {Roo.bootstrap.UploadCropbox} this
26948 * @param {String} pos
26953 * Fire when inspect the file
26954 * @param {Roo.bootstrap.UploadCropbox} this
26955 * @param {Object} file
26960 * Fire when xhr upload the file
26961 * @param {Roo.bootstrap.UploadCropbox} this
26962 * @param {Object} data
26967 * Fire when arrange the file data
26968 * @param {Roo.bootstrap.UploadCropbox} this
26969 * @param {Object} formData
26974 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26977 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26979 emptyText : 'Click to upload image',
26980 rotateNotify : 'Image is too small to rotate',
26981 errorTimeout : 3000,
26995 cropType : 'image/jpeg',
26997 canvasLoaded : false,
26998 isDocument : false,
27000 paramName : 'imageUpload',
27002 loadingText : 'Loading...',
27005 getAutoCreate : function()
27009 cls : 'roo-upload-cropbox',
27013 cls : 'roo-upload-cropbox-selector',
27018 cls : 'roo-upload-cropbox-body',
27019 style : 'cursor:pointer',
27023 cls : 'roo-upload-cropbox-preview'
27027 cls : 'roo-upload-cropbox-thumb'
27031 cls : 'roo-upload-cropbox-empty-notify',
27032 html : this.emptyText
27036 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27037 html : this.rotateNotify
27043 cls : 'roo-upload-cropbox-footer',
27046 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27056 onRender : function(ct, position)
27058 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27060 if (this.buttons.length) {
27062 Roo.each(this.buttons, function(bb) {
27064 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27066 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27072 this.maskEl = this.el;
27076 initEvents : function()
27078 this.urlAPI = (window.createObjectURL && window) ||
27079 (window.URL && URL.revokeObjectURL && URL) ||
27080 (window.webkitURL && webkitURL);
27082 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27083 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27085 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27086 this.selectorEl.hide();
27088 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27089 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27091 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27092 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27093 this.thumbEl.hide();
27095 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27096 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27099 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27100 this.errorEl.hide();
27102 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27103 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104 this.footerEl.hide();
27106 this.setThumbBoxSize();
27112 this.fireEvent('initial', this);
27119 window.addEventListener("resize", function() { _this.resize(); } );
27121 this.bodyEl.on('click', this.beforeSelectFile, this);
27124 this.bodyEl.on('touchstart', this.onTouchStart, this);
27125 this.bodyEl.on('touchmove', this.onTouchMove, this);
27126 this.bodyEl.on('touchend', this.onTouchEnd, this);
27130 this.bodyEl.on('mousedown', this.onMouseDown, this);
27131 this.bodyEl.on('mousemove', this.onMouseMove, this);
27132 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27133 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27134 Roo.get(document).on('mouseup', this.onMouseUp, this);
27137 this.selectorEl.on('change', this.onFileSelected, this);
27143 this.baseScale = 1;
27145 this.baseRotate = 1;
27146 this.dragable = false;
27147 this.pinching = false;
27150 this.cropData = false;
27151 this.notifyEl.dom.innerHTML = this.emptyText;
27153 this.selectorEl.dom.value = '';
27157 resize : function()
27159 if(this.fireEvent('resize', this) != false){
27160 this.setThumbBoxPosition();
27161 this.setCanvasPosition();
27165 onFooterButtonClick : function(e, el, o, type)
27168 case 'rotate-left' :
27169 this.onRotateLeft(e);
27171 case 'rotate-right' :
27172 this.onRotateRight(e);
27175 this.beforeSelectFile(e);
27190 this.fireEvent('footerbuttonclick', this, type);
27193 beforeSelectFile : function(e)
27195 e.preventDefault();
27197 if(this.fireEvent('beforeselectfile', this) != false){
27198 this.selectorEl.dom.click();
27202 onFileSelected : function(e)
27204 e.preventDefault();
27206 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27210 var file = this.selectorEl.dom.files[0];
27212 if(this.fireEvent('inspect', this, file) != false){
27213 this.prepare(file);
27218 trash : function(e)
27220 this.fireEvent('trash', this);
27223 download : function(e)
27225 this.fireEvent('download', this);
27228 loadCanvas : function(src)
27230 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27234 this.imageEl = document.createElement('img');
27238 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27240 this.imageEl.src = src;
27244 onLoadCanvas : function()
27246 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27247 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27249 this.bodyEl.un('click', this.beforeSelectFile, this);
27251 this.notifyEl.hide();
27252 this.thumbEl.show();
27253 this.footerEl.show();
27255 this.baseRotateLevel();
27257 if(this.isDocument){
27258 this.setThumbBoxSize();
27261 this.setThumbBoxPosition();
27263 this.baseScaleLevel();
27269 this.canvasLoaded = true;
27272 this.maskEl.unmask();
27277 setCanvasPosition : function()
27279 if(!this.canvasEl){
27283 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27284 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27286 this.previewEl.setLeft(pw);
27287 this.previewEl.setTop(ph);
27291 onMouseDown : function(e)
27295 this.dragable = true;
27296 this.pinching = false;
27298 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27299 this.dragable = false;
27303 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27304 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27308 onMouseMove : function(e)
27312 if(!this.canvasLoaded){
27316 if (!this.dragable){
27320 var minX = Math.ceil(this.thumbEl.getLeft(true));
27321 var minY = Math.ceil(this.thumbEl.getTop(true));
27323 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27324 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27326 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27327 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27329 x = x - this.mouseX;
27330 y = y - this.mouseY;
27332 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27333 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27335 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27336 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27338 this.previewEl.setLeft(bgX);
27339 this.previewEl.setTop(bgY);
27341 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27342 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27345 onMouseUp : function(e)
27349 this.dragable = false;
27352 onMouseWheel : function(e)
27356 this.startScale = this.scale;
27358 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27360 if(!this.zoomable()){
27361 this.scale = this.startScale;
27370 zoomable : function()
27372 var minScale = this.thumbEl.getWidth() / this.minWidth;
27374 if(this.minWidth < this.minHeight){
27375 minScale = this.thumbEl.getHeight() / this.minHeight;
27378 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27379 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27383 (this.rotate == 0 || this.rotate == 180) &&
27385 width > this.imageEl.OriginWidth ||
27386 height > this.imageEl.OriginHeight ||
27387 (width < this.minWidth && height < this.minHeight)
27395 (this.rotate == 90 || this.rotate == 270) &&
27397 width > this.imageEl.OriginWidth ||
27398 height > this.imageEl.OriginHeight ||
27399 (width < this.minHeight && height < this.minWidth)
27406 !this.isDocument &&
27407 (this.rotate == 0 || this.rotate == 180) &&
27409 width < this.minWidth ||
27410 width > this.imageEl.OriginWidth ||
27411 height < this.minHeight ||
27412 height > this.imageEl.OriginHeight
27419 !this.isDocument &&
27420 (this.rotate == 90 || this.rotate == 270) &&
27422 width < this.minHeight ||
27423 width > this.imageEl.OriginWidth ||
27424 height < this.minWidth ||
27425 height > this.imageEl.OriginHeight
27435 onRotateLeft : function(e)
27437 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27439 var minScale = this.thumbEl.getWidth() / this.minWidth;
27441 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27442 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27444 this.startScale = this.scale;
27446 while (this.getScaleLevel() < minScale){
27448 this.scale = this.scale + 1;
27450 if(!this.zoomable()){
27455 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27456 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27461 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27468 this.scale = this.startScale;
27470 this.onRotateFail();
27475 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27477 if(this.isDocument){
27478 this.setThumbBoxSize();
27479 this.setThumbBoxPosition();
27480 this.setCanvasPosition();
27485 this.fireEvent('rotate', this, 'left');
27489 onRotateRight : function(e)
27491 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27493 var minScale = this.thumbEl.getWidth() / this.minWidth;
27495 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27496 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27498 this.startScale = this.scale;
27500 while (this.getScaleLevel() < minScale){
27502 this.scale = this.scale + 1;
27504 if(!this.zoomable()){
27509 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27510 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27515 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27522 this.scale = this.startScale;
27524 this.onRotateFail();
27529 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27531 if(this.isDocument){
27532 this.setThumbBoxSize();
27533 this.setThumbBoxPosition();
27534 this.setCanvasPosition();
27539 this.fireEvent('rotate', this, 'right');
27542 onRotateFail : function()
27544 this.errorEl.show(true);
27548 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27553 this.previewEl.dom.innerHTML = '';
27555 var canvasEl = document.createElement("canvas");
27557 var contextEl = canvasEl.getContext("2d");
27559 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27560 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27561 var center = this.imageEl.OriginWidth / 2;
27563 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27564 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27565 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27566 center = this.imageEl.OriginHeight / 2;
27569 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27571 contextEl.translate(center, center);
27572 contextEl.rotate(this.rotate * Math.PI / 180);
27574 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27576 this.canvasEl = document.createElement("canvas");
27578 this.contextEl = this.canvasEl.getContext("2d");
27580 switch (this.rotate) {
27583 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27584 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27586 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27591 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27592 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27594 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27595 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);
27599 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27604 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27605 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27607 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27608 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);
27612 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);
27617 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27618 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27620 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27621 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27625 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);
27632 this.previewEl.appendChild(this.canvasEl);
27634 this.setCanvasPosition();
27639 if(!this.canvasLoaded){
27643 var imageCanvas = document.createElement("canvas");
27645 var imageContext = imageCanvas.getContext("2d");
27647 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27648 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27650 var center = imageCanvas.width / 2;
27652 imageContext.translate(center, center);
27654 imageContext.rotate(this.rotate * Math.PI / 180);
27656 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27658 var canvas = document.createElement("canvas");
27660 var context = canvas.getContext("2d");
27662 canvas.width = this.minWidth;
27663 canvas.height = this.minHeight;
27665 switch (this.rotate) {
27668 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27669 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27671 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27672 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27674 var targetWidth = this.minWidth - 2 * x;
27675 var targetHeight = this.minHeight - 2 * y;
27679 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27680 scale = targetWidth / width;
27683 if(x > 0 && y == 0){
27684 scale = targetHeight / height;
27687 if(x > 0 && y > 0){
27688 scale = targetWidth / width;
27690 if(width < height){
27691 scale = targetHeight / height;
27695 context.scale(scale, scale);
27697 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27698 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27700 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27701 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27703 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27708 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27709 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27711 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27712 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27714 var targetWidth = this.minWidth - 2 * x;
27715 var targetHeight = this.minHeight - 2 * y;
27719 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27720 scale = targetWidth / width;
27723 if(x > 0 && y == 0){
27724 scale = targetHeight / height;
27727 if(x > 0 && y > 0){
27728 scale = targetWidth / width;
27730 if(width < height){
27731 scale = targetHeight / height;
27735 context.scale(scale, scale);
27737 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27738 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27740 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27741 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27743 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27745 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27750 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27751 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27753 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27754 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27756 var targetWidth = this.minWidth - 2 * x;
27757 var targetHeight = this.minHeight - 2 * y;
27761 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27762 scale = targetWidth / width;
27765 if(x > 0 && y == 0){
27766 scale = targetHeight / height;
27769 if(x > 0 && y > 0){
27770 scale = targetWidth / width;
27772 if(width < height){
27773 scale = targetHeight / height;
27777 context.scale(scale, scale);
27779 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27780 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27782 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27783 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27785 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27786 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27788 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27793 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27794 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27796 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27797 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27799 var targetWidth = this.minWidth - 2 * x;
27800 var targetHeight = this.minHeight - 2 * y;
27804 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27805 scale = targetWidth / width;
27808 if(x > 0 && y == 0){
27809 scale = targetHeight / height;
27812 if(x > 0 && y > 0){
27813 scale = targetWidth / width;
27815 if(width < height){
27816 scale = targetHeight / height;
27820 context.scale(scale, scale);
27822 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27823 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27825 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27826 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27828 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27830 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27837 this.cropData = canvas.toDataURL(this.cropType);
27839 if(this.fireEvent('crop', this, this.cropData) !== false){
27840 this.process(this.file, this.cropData);
27847 setThumbBoxSize : function()
27851 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27852 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27853 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27855 this.minWidth = width;
27856 this.minHeight = height;
27858 if(this.rotate == 90 || this.rotate == 270){
27859 this.minWidth = height;
27860 this.minHeight = width;
27865 width = Math.ceil(this.minWidth * height / this.minHeight);
27867 if(this.minWidth > this.minHeight){
27869 height = Math.ceil(this.minHeight * width / this.minWidth);
27872 this.thumbEl.setStyle({
27873 width : width + 'px',
27874 height : height + 'px'
27881 setThumbBoxPosition : function()
27883 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27884 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27886 this.thumbEl.setLeft(x);
27887 this.thumbEl.setTop(y);
27891 baseRotateLevel : function()
27893 this.baseRotate = 1;
27896 typeof(this.exif) != 'undefined' &&
27897 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27898 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27900 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27903 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27907 baseScaleLevel : function()
27911 if(this.isDocument){
27913 if(this.baseRotate == 6 || this.baseRotate == 8){
27915 height = this.thumbEl.getHeight();
27916 this.baseScale = height / this.imageEl.OriginWidth;
27918 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27919 width = this.thumbEl.getWidth();
27920 this.baseScale = width / this.imageEl.OriginHeight;
27926 height = this.thumbEl.getHeight();
27927 this.baseScale = height / this.imageEl.OriginHeight;
27929 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27930 width = this.thumbEl.getWidth();
27931 this.baseScale = width / this.imageEl.OriginWidth;
27937 if(this.baseRotate == 6 || this.baseRotate == 8){
27939 width = this.thumbEl.getHeight();
27940 this.baseScale = width / this.imageEl.OriginHeight;
27942 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27943 height = this.thumbEl.getWidth();
27944 this.baseScale = height / this.imageEl.OriginHeight;
27947 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27948 height = this.thumbEl.getWidth();
27949 this.baseScale = height / this.imageEl.OriginHeight;
27951 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27952 width = this.thumbEl.getHeight();
27953 this.baseScale = width / this.imageEl.OriginWidth;
27960 width = this.thumbEl.getWidth();
27961 this.baseScale = width / this.imageEl.OriginWidth;
27963 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27964 height = this.thumbEl.getHeight();
27965 this.baseScale = height / this.imageEl.OriginHeight;
27968 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27970 height = this.thumbEl.getHeight();
27971 this.baseScale = height / this.imageEl.OriginHeight;
27973 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27974 width = this.thumbEl.getWidth();
27975 this.baseScale = width / this.imageEl.OriginWidth;
27983 getScaleLevel : function()
27985 return this.baseScale * Math.pow(1.1, this.scale);
27988 onTouchStart : function(e)
27990 if(!this.canvasLoaded){
27991 this.beforeSelectFile(e);
27995 var touches = e.browserEvent.touches;
28001 if(touches.length == 1){
28002 this.onMouseDown(e);
28006 if(touches.length != 2){
28012 for(var i = 0, finger; finger = touches[i]; i++){
28013 coords.push(finger.pageX, finger.pageY);
28016 var x = Math.pow(coords[0] - coords[2], 2);
28017 var y = Math.pow(coords[1] - coords[3], 2);
28019 this.startDistance = Math.sqrt(x + y);
28021 this.startScale = this.scale;
28023 this.pinching = true;
28024 this.dragable = false;
28028 onTouchMove : function(e)
28030 if(!this.pinching && !this.dragable){
28034 var touches = e.browserEvent.touches;
28041 this.onMouseMove(e);
28047 for(var i = 0, finger; finger = touches[i]; i++){
28048 coords.push(finger.pageX, finger.pageY);
28051 var x = Math.pow(coords[0] - coords[2], 2);
28052 var y = Math.pow(coords[1] - coords[3], 2);
28054 this.endDistance = Math.sqrt(x + y);
28056 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28058 if(!this.zoomable()){
28059 this.scale = this.startScale;
28067 onTouchEnd : function(e)
28069 this.pinching = false;
28070 this.dragable = false;
28074 process : function(file, crop)
28077 this.maskEl.mask(this.loadingText);
28080 this.xhr = new XMLHttpRequest();
28082 file.xhr = this.xhr;
28084 this.xhr.open(this.method, this.url, true);
28087 "Accept": "application/json",
28088 "Cache-Control": "no-cache",
28089 "X-Requested-With": "XMLHttpRequest"
28092 for (var headerName in headers) {
28093 var headerValue = headers[headerName];
28095 this.xhr.setRequestHeader(headerName, headerValue);
28101 this.xhr.onload = function()
28103 _this.xhrOnLoad(_this.xhr);
28106 this.xhr.onerror = function()
28108 _this.xhrOnError(_this.xhr);
28111 var formData = new FormData();
28113 formData.append('returnHTML', 'NO');
28116 formData.append('crop', crop);
28119 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28120 formData.append(this.paramName, file, file.name);
28123 if(typeof(file.filename) != 'undefined'){
28124 formData.append('filename', file.filename);
28127 if(typeof(file.mimetype) != 'undefined'){
28128 formData.append('mimetype', file.mimetype);
28131 if(this.fireEvent('arrange', this, formData) != false){
28132 this.xhr.send(formData);
28136 xhrOnLoad : function(xhr)
28139 this.maskEl.unmask();
28142 if (xhr.readyState !== 4) {
28143 this.fireEvent('exception', this, xhr);
28147 var response = Roo.decode(xhr.responseText);
28149 if(!response.success){
28150 this.fireEvent('exception', this, xhr);
28154 var response = Roo.decode(xhr.responseText);
28156 this.fireEvent('upload', this, response);
28160 xhrOnError : function()
28163 this.maskEl.unmask();
28166 Roo.log('xhr on error');
28168 var response = Roo.decode(xhr.responseText);
28174 prepare : function(file)
28177 this.maskEl.mask(this.loadingText);
28183 if(typeof(file) === 'string'){
28184 this.loadCanvas(file);
28188 if(!file || !this.urlAPI){
28193 this.cropType = file.type;
28197 if(this.fireEvent('prepare', this, this.file) != false){
28199 var reader = new FileReader();
28201 reader.onload = function (e) {
28202 if (e.target.error) {
28203 Roo.log(e.target.error);
28207 var buffer = e.target.result,
28208 dataView = new DataView(buffer),
28210 maxOffset = dataView.byteLength - 4,
28214 if (dataView.getUint16(0) === 0xffd8) {
28215 while (offset < maxOffset) {
28216 markerBytes = dataView.getUint16(offset);
28218 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28219 markerLength = dataView.getUint16(offset + 2) + 2;
28220 if (offset + markerLength > dataView.byteLength) {
28221 Roo.log('Invalid meta data: Invalid segment size.');
28225 if(markerBytes == 0xffe1){
28226 _this.parseExifData(
28233 offset += markerLength;
28243 var url = _this.urlAPI.createObjectURL(_this.file);
28245 _this.loadCanvas(url);
28250 reader.readAsArrayBuffer(this.file);
28256 parseExifData : function(dataView, offset, length)
28258 var tiffOffset = offset + 10,
28262 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28263 // No Exif data, might be XMP data instead
28267 // Check for the ASCII code for "Exif" (0x45786966):
28268 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28269 // No Exif data, might be XMP data instead
28272 if (tiffOffset + 8 > dataView.byteLength) {
28273 Roo.log('Invalid Exif data: Invalid segment size.');
28276 // Check for the two null bytes:
28277 if (dataView.getUint16(offset + 8) !== 0x0000) {
28278 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28281 // Check the byte alignment:
28282 switch (dataView.getUint16(tiffOffset)) {
28284 littleEndian = true;
28287 littleEndian = false;
28290 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28293 // Check for the TIFF tag marker (0x002A):
28294 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28295 Roo.log('Invalid Exif data: Missing TIFF marker.');
28298 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28299 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28301 this.parseExifTags(
28304 tiffOffset + dirOffset,
28309 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28314 if (dirOffset + 6 > dataView.byteLength) {
28315 Roo.log('Invalid Exif data: Invalid directory offset.');
28318 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28319 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28320 if (dirEndOffset + 4 > dataView.byteLength) {
28321 Roo.log('Invalid Exif data: Invalid directory size.');
28324 for (i = 0; i < tagsNumber; i += 1) {
28328 dirOffset + 2 + 12 * i, // tag offset
28332 // Return the offset to the next directory:
28333 return dataView.getUint32(dirEndOffset, littleEndian);
28336 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28338 var tag = dataView.getUint16(offset, littleEndian);
28340 this.exif[tag] = this.getExifValue(
28344 dataView.getUint16(offset + 2, littleEndian), // tag type
28345 dataView.getUint32(offset + 4, littleEndian), // tag length
28350 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28352 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28361 Roo.log('Invalid Exif data: Invalid tag type.');
28365 tagSize = tagType.size * length;
28366 // Determine if the value is contained in the dataOffset bytes,
28367 // or if the value at the dataOffset is a pointer to the actual data:
28368 dataOffset = tagSize > 4 ?
28369 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28370 if (dataOffset + tagSize > dataView.byteLength) {
28371 Roo.log('Invalid Exif data: Invalid data offset.');
28374 if (length === 1) {
28375 return tagType.getValue(dataView, dataOffset, littleEndian);
28378 for (i = 0; i < length; i += 1) {
28379 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28382 if (tagType.ascii) {
28384 // Concatenate the chars:
28385 for (i = 0; i < values.length; i += 1) {
28387 // Ignore the terminating NULL byte(s):
28388 if (c === '\u0000') {
28400 Roo.apply(Roo.bootstrap.UploadCropbox, {
28402 'Orientation': 0x0112
28406 1: 0, //'top-left',
28408 3: 180, //'bottom-right',
28409 // 4: 'bottom-left',
28411 6: 90, //'right-top',
28412 // 7: 'right-bottom',
28413 8: 270 //'left-bottom'
28417 // byte, 8-bit unsigned int:
28419 getValue: function (dataView, dataOffset) {
28420 return dataView.getUint8(dataOffset);
28424 // ascii, 8-bit byte:
28426 getValue: function (dataView, dataOffset) {
28427 return String.fromCharCode(dataView.getUint8(dataOffset));
28432 // short, 16 bit int:
28434 getValue: function (dataView, dataOffset, littleEndian) {
28435 return dataView.getUint16(dataOffset, littleEndian);
28439 // long, 32 bit int:
28441 getValue: function (dataView, dataOffset, littleEndian) {
28442 return dataView.getUint32(dataOffset, littleEndian);
28446 // rational = two long values, first is numerator, second is denominator:
28448 getValue: function (dataView, dataOffset, littleEndian) {
28449 return dataView.getUint32(dataOffset, littleEndian) /
28450 dataView.getUint32(dataOffset + 4, littleEndian);
28454 // slong, 32 bit signed int:
28456 getValue: function (dataView, dataOffset, littleEndian) {
28457 return dataView.getInt32(dataOffset, littleEndian);
28461 // srational, two slongs, first is numerator, second is denominator:
28463 getValue: function (dataView, dataOffset, littleEndian) {
28464 return dataView.getInt32(dataOffset, littleEndian) /
28465 dataView.getInt32(dataOffset + 4, littleEndian);
28475 cls : 'btn-group roo-upload-cropbox-rotate-left',
28476 action : 'rotate-left',
28480 cls : 'btn btn-default',
28481 html : '<i class="fa fa-undo"></i>'
28487 cls : 'btn-group roo-upload-cropbox-picture',
28488 action : 'picture',
28492 cls : 'btn btn-default',
28493 html : '<i class="fa fa-picture-o"></i>'
28499 cls : 'btn-group roo-upload-cropbox-rotate-right',
28500 action : 'rotate-right',
28504 cls : 'btn btn-default',
28505 html : '<i class="fa fa-repeat"></i>'
28513 cls : 'btn-group roo-upload-cropbox-rotate-left',
28514 action : 'rotate-left',
28518 cls : 'btn btn-default',
28519 html : '<i class="fa fa-undo"></i>'
28525 cls : 'btn-group roo-upload-cropbox-download',
28526 action : 'download',
28530 cls : 'btn btn-default',
28531 html : '<i class="fa fa-download"></i>'
28537 cls : 'btn-group roo-upload-cropbox-crop',
28542 cls : 'btn btn-default',
28543 html : '<i class="fa fa-crop"></i>'
28549 cls : 'btn-group roo-upload-cropbox-trash',
28554 cls : 'btn btn-default',
28555 html : '<i class="fa fa-trash"></i>'
28561 cls : 'btn-group roo-upload-cropbox-rotate-right',
28562 action : 'rotate-right',
28566 cls : 'btn btn-default',
28567 html : '<i class="fa fa-repeat"></i>'
28575 cls : 'btn-group roo-upload-cropbox-rotate-left',
28576 action : 'rotate-left',
28580 cls : 'btn btn-default',
28581 html : '<i class="fa fa-undo"></i>'
28587 cls : 'btn-group roo-upload-cropbox-rotate-right',
28588 action : 'rotate-right',
28592 cls : 'btn btn-default',
28593 html : '<i class="fa fa-repeat"></i>'
28606 * @class Roo.bootstrap.DocumentManager
28607 * @extends Roo.bootstrap.Component
28608 * Bootstrap DocumentManager class
28609 * @cfg {String} paramName default 'imageUpload'
28610 * @cfg {String} toolTipName default 'filename'
28611 * @cfg {String} method default POST
28612 * @cfg {String} url action url
28613 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28614 * @cfg {Boolean} multiple multiple upload default true
28615 * @cfg {Number} thumbSize default 300
28616 * @cfg {String} fieldLabel
28617 * @cfg {Number} labelWidth default 4
28618 * @cfg {String} labelAlign (left|top) default left
28619 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28620 * @cfg {Number} labellg set the width of label (1-12)
28621 * @cfg {Number} labelmd set the width of label (1-12)
28622 * @cfg {Number} labelsm set the width of label (1-12)
28623 * @cfg {Number} labelxs set the width of label (1-12)
28626 * Create a new DocumentManager
28627 * @param {Object} config The config object
28630 Roo.bootstrap.DocumentManager = function(config){
28631 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28634 this.delegates = [];
28639 * Fire when initial the DocumentManager
28640 * @param {Roo.bootstrap.DocumentManager} this
28645 * inspect selected file
28646 * @param {Roo.bootstrap.DocumentManager} this
28647 * @param {File} file
28652 * Fire when xhr load exception
28653 * @param {Roo.bootstrap.DocumentManager} this
28654 * @param {XMLHttpRequest} xhr
28656 "exception" : true,
28658 * @event afterupload
28659 * Fire when xhr load exception
28660 * @param {Roo.bootstrap.DocumentManager} this
28661 * @param {XMLHttpRequest} xhr
28663 "afterupload" : true,
28666 * prepare the form data
28667 * @param {Roo.bootstrap.DocumentManager} this
28668 * @param {Object} formData
28673 * Fire when remove the file
28674 * @param {Roo.bootstrap.DocumentManager} this
28675 * @param {Object} file
28680 * Fire after refresh the file
28681 * @param {Roo.bootstrap.DocumentManager} this
28686 * Fire after click the image
28687 * @param {Roo.bootstrap.DocumentManager} this
28688 * @param {Object} file
28693 * Fire when upload a image and editable set to true
28694 * @param {Roo.bootstrap.DocumentManager} this
28695 * @param {Object} file
28699 * @event beforeselectfile
28700 * Fire before select file
28701 * @param {Roo.bootstrap.DocumentManager} this
28703 "beforeselectfile" : true,
28706 * Fire before process file
28707 * @param {Roo.bootstrap.DocumentManager} this
28708 * @param {Object} file
28712 * @event previewrendered
28713 * Fire when preview rendered
28714 * @param {Roo.bootstrap.DocumentManager} this
28715 * @param {Object} file
28717 "previewrendered" : true,
28720 "previewResize" : true
28725 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28734 paramName : 'imageUpload',
28735 toolTipName : 'filename',
28738 labelAlign : 'left',
28748 getAutoCreate : function()
28750 var managerWidget = {
28752 cls : 'roo-document-manager',
28756 cls : 'roo-document-manager-selector',
28761 cls : 'roo-document-manager-uploader',
28765 cls : 'roo-document-manager-upload-btn',
28766 html : '<i class="fa fa-plus"></i>'
28777 cls : 'column col-md-12',
28782 if(this.fieldLabel.length){
28787 cls : 'column col-md-12',
28788 html : this.fieldLabel
28792 cls : 'column col-md-12',
28797 if(this.labelAlign == 'left'){
28802 html : this.fieldLabel
28811 if(this.labelWidth > 12){
28812 content[0].style = "width: " + this.labelWidth + 'px';
28815 if(this.labelWidth < 13 && this.labelmd == 0){
28816 this.labelmd = this.labelWidth;
28819 if(this.labellg > 0){
28820 content[0].cls += ' col-lg-' + this.labellg;
28821 content[1].cls += ' col-lg-' + (12 - this.labellg);
28824 if(this.labelmd > 0){
28825 content[0].cls += ' col-md-' + this.labelmd;
28826 content[1].cls += ' col-md-' + (12 - this.labelmd);
28829 if(this.labelsm > 0){
28830 content[0].cls += ' col-sm-' + this.labelsm;
28831 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28834 if(this.labelxs > 0){
28835 content[0].cls += ' col-xs-' + this.labelxs;
28836 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28844 cls : 'row clearfix',
28852 initEvents : function()
28854 this.managerEl = this.el.select('.roo-document-manager', true).first();
28855 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28857 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28858 this.selectorEl.hide();
28861 this.selectorEl.attr('multiple', 'multiple');
28864 this.selectorEl.on('change', this.onFileSelected, this);
28866 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28867 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28869 this.uploader.on('click', this.onUploaderClick, this);
28871 this.renderProgressDialog();
28875 window.addEventListener("resize", function() { _this.refresh(); } );
28877 this.fireEvent('initial', this);
28880 renderProgressDialog : function()
28884 this.progressDialog = new Roo.bootstrap.Modal({
28885 cls : 'roo-document-manager-progress-dialog',
28886 allow_close : false,
28896 btnclick : function() {
28897 _this.uploadCancel();
28903 this.progressDialog.render(Roo.get(document.body));
28905 this.progress = new Roo.bootstrap.Progress({
28906 cls : 'roo-document-manager-progress',
28911 this.progress.render(this.progressDialog.getChildContainer());
28913 this.progressBar = new Roo.bootstrap.ProgressBar({
28914 cls : 'roo-document-manager-progress-bar',
28917 aria_valuemax : 12,
28921 this.progressBar.render(this.progress.getChildContainer());
28924 onUploaderClick : function(e)
28926 e.preventDefault();
28928 if(this.fireEvent('beforeselectfile', this) != false){
28929 this.selectorEl.dom.click();
28934 onFileSelected : function(e)
28936 e.preventDefault();
28938 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28942 Roo.each(this.selectorEl.dom.files, function(file){
28943 if(this.fireEvent('inspect', this, file) != false){
28944 this.files.push(file);
28954 this.selectorEl.dom.value = '';
28956 if(!this.files || !this.files.length){
28960 if(this.boxes > 0 && this.files.length > this.boxes){
28961 this.files = this.files.slice(0, this.boxes);
28964 this.uploader.show();
28966 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28967 this.uploader.hide();
28976 Roo.each(this.files, function(file){
28978 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28979 var f = this.renderPreview(file);
28984 if(file.type.indexOf('image') != -1){
28985 this.delegates.push(
28987 _this.process(file);
28988 }).createDelegate(this)
28996 _this.process(file);
28997 }).createDelegate(this)
29002 this.files = files;
29004 this.delegates = this.delegates.concat(docs);
29006 if(!this.delegates.length){
29011 this.progressBar.aria_valuemax = this.delegates.length;
29018 arrange : function()
29020 if(!this.delegates.length){
29021 this.progressDialog.hide();
29026 var delegate = this.delegates.shift();
29028 this.progressDialog.show();
29030 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29032 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29037 refresh : function()
29039 this.uploader.show();
29041 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29042 this.uploader.hide();
29045 Roo.isTouch ? this.closable(false) : this.closable(true);
29047 this.fireEvent('refresh', this);
29050 onRemove : function(e, el, o)
29052 e.preventDefault();
29054 this.fireEvent('remove', this, o);
29058 remove : function(o)
29062 Roo.each(this.files, function(file){
29063 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29072 this.files = files;
29079 Roo.each(this.files, function(file){
29084 file.target.remove();
29093 onClick : function(e, el, o)
29095 e.preventDefault();
29097 this.fireEvent('click', this, o);
29101 closable : function(closable)
29103 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29105 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29117 xhrOnLoad : function(xhr)
29119 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29123 if (xhr.readyState !== 4) {
29125 this.fireEvent('exception', this, xhr);
29129 var response = Roo.decode(xhr.responseText);
29131 if(!response.success){
29133 this.fireEvent('exception', this, xhr);
29137 var file = this.renderPreview(response.data);
29139 this.files.push(file);
29143 this.fireEvent('afterupload', this, xhr);
29147 xhrOnError : function(xhr)
29149 Roo.log('xhr on error');
29151 var response = Roo.decode(xhr.responseText);
29158 process : function(file)
29160 if(this.fireEvent('process', this, file) !== false){
29161 if(this.editable && file.type.indexOf('image') != -1){
29162 this.fireEvent('edit', this, file);
29166 this.uploadStart(file, false);
29173 uploadStart : function(file, crop)
29175 this.xhr = new XMLHttpRequest();
29177 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29182 file.xhr = this.xhr;
29184 this.managerEl.createChild({
29186 cls : 'roo-document-manager-loading',
29190 tooltip : file.name,
29191 cls : 'roo-document-manager-thumb',
29192 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29198 this.xhr.open(this.method, this.url, true);
29201 "Accept": "application/json",
29202 "Cache-Control": "no-cache",
29203 "X-Requested-With": "XMLHttpRequest"
29206 for (var headerName in headers) {
29207 var headerValue = headers[headerName];
29209 this.xhr.setRequestHeader(headerName, headerValue);
29215 this.xhr.onload = function()
29217 _this.xhrOnLoad(_this.xhr);
29220 this.xhr.onerror = function()
29222 _this.xhrOnError(_this.xhr);
29225 var formData = new FormData();
29227 formData.append('returnHTML', 'NO');
29230 formData.append('crop', crop);
29233 formData.append(this.paramName, file, file.name);
29240 if(this.fireEvent('prepare', this, formData, options) != false){
29242 if(options.manually){
29246 this.xhr.send(formData);
29250 this.uploadCancel();
29253 uploadCancel : function()
29259 this.delegates = [];
29261 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29268 renderPreview : function(file)
29270 if(typeof(file.target) != 'undefined' && file.target){
29274 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29276 var previewEl = this.managerEl.createChild({
29278 cls : 'roo-document-manager-preview',
29282 tooltip : file[this.toolTipName],
29283 cls : 'roo-document-manager-thumb',
29284 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29289 html : '<i class="fa fa-times-circle"></i>'
29294 var close = previewEl.select('button.close', true).first();
29296 close.on('click', this.onRemove, this, file);
29298 file.target = previewEl;
29300 var image = previewEl.select('img', true).first();
29304 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29306 image.on('click', this.onClick, this, file);
29308 this.fireEvent('previewrendered', this, file);
29314 onPreviewLoad : function(file, image)
29316 if(typeof(file.target) == 'undefined' || !file.target){
29320 var width = image.dom.naturalWidth || image.dom.width;
29321 var height = image.dom.naturalHeight || image.dom.height;
29323 if(!this.previewResize) {
29327 if(width > height){
29328 file.target.addClass('wide');
29332 file.target.addClass('tall');
29337 uploadFromSource : function(file, crop)
29339 this.xhr = new XMLHttpRequest();
29341 this.managerEl.createChild({
29343 cls : 'roo-document-manager-loading',
29347 tooltip : file.name,
29348 cls : 'roo-document-manager-thumb',
29349 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29355 this.xhr.open(this.method, this.url, true);
29358 "Accept": "application/json",
29359 "Cache-Control": "no-cache",
29360 "X-Requested-With": "XMLHttpRequest"
29363 for (var headerName in headers) {
29364 var headerValue = headers[headerName];
29366 this.xhr.setRequestHeader(headerName, headerValue);
29372 this.xhr.onload = function()
29374 _this.xhrOnLoad(_this.xhr);
29377 this.xhr.onerror = function()
29379 _this.xhrOnError(_this.xhr);
29382 var formData = new FormData();
29384 formData.append('returnHTML', 'NO');
29386 formData.append('crop', crop);
29388 if(typeof(file.filename) != 'undefined'){
29389 formData.append('filename', file.filename);
29392 if(typeof(file.mimetype) != 'undefined'){
29393 formData.append('mimetype', file.mimetype);
29398 if(this.fireEvent('prepare', this, formData) != false){
29399 this.xhr.send(formData);
29409 * @class Roo.bootstrap.DocumentViewer
29410 * @extends Roo.bootstrap.Component
29411 * Bootstrap DocumentViewer class
29412 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29413 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29416 * Create a new DocumentViewer
29417 * @param {Object} config The config object
29420 Roo.bootstrap.DocumentViewer = function(config){
29421 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29426 * Fire after initEvent
29427 * @param {Roo.bootstrap.DocumentViewer} this
29433 * @param {Roo.bootstrap.DocumentViewer} this
29438 * Fire after download button
29439 * @param {Roo.bootstrap.DocumentViewer} this
29444 * Fire after trash button
29445 * @param {Roo.bootstrap.DocumentViewer} this
29452 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29454 showDownload : true,
29458 getAutoCreate : function()
29462 cls : 'roo-document-viewer',
29466 cls : 'roo-document-viewer-body',
29470 cls : 'roo-document-viewer-thumb',
29474 cls : 'roo-document-viewer-image'
29482 cls : 'roo-document-viewer-footer',
29485 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29489 cls : 'btn-group roo-document-viewer-download',
29493 cls : 'btn btn-default',
29494 html : '<i class="fa fa-download"></i>'
29500 cls : 'btn-group roo-document-viewer-trash',
29504 cls : 'btn btn-default',
29505 html : '<i class="fa fa-trash"></i>'
29518 initEvents : function()
29520 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29521 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29523 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29524 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29526 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29527 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29529 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29530 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29532 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29533 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29535 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29536 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29538 this.bodyEl.on('click', this.onClick, this);
29539 this.downloadBtn.on('click', this.onDownload, this);
29540 this.trashBtn.on('click', this.onTrash, this);
29542 this.downloadBtn.hide();
29543 this.trashBtn.hide();
29545 if(this.showDownload){
29546 this.downloadBtn.show();
29549 if(this.showTrash){
29550 this.trashBtn.show();
29553 if(!this.showDownload && !this.showTrash) {
29554 this.footerEl.hide();
29559 initial : function()
29561 this.fireEvent('initial', this);
29565 onClick : function(e)
29567 e.preventDefault();
29569 this.fireEvent('click', this);
29572 onDownload : function(e)
29574 e.preventDefault();
29576 this.fireEvent('download', this);
29579 onTrash : function(e)
29581 e.preventDefault();
29583 this.fireEvent('trash', this);
29595 * @class Roo.bootstrap.NavProgressBar
29596 * @extends Roo.bootstrap.Component
29597 * Bootstrap NavProgressBar class
29600 * Create a new nav progress bar
29601 * @param {Object} config The config object
29604 Roo.bootstrap.NavProgressBar = function(config){
29605 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29607 this.bullets = this.bullets || [];
29609 // Roo.bootstrap.NavProgressBar.register(this);
29613 * Fires when the active item changes
29614 * @param {Roo.bootstrap.NavProgressBar} this
29615 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29616 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29623 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29628 getAutoCreate : function()
29630 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29634 cls : 'roo-navigation-bar-group',
29638 cls : 'roo-navigation-top-bar'
29642 cls : 'roo-navigation-bullets-bar',
29646 cls : 'roo-navigation-bar'
29653 cls : 'roo-navigation-bottom-bar'
29663 initEvents: function()
29668 onRender : function(ct, position)
29670 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29672 if(this.bullets.length){
29673 Roo.each(this.bullets, function(b){
29682 addItem : function(cfg)
29684 var item = new Roo.bootstrap.NavProgressItem(cfg);
29686 item.parentId = this.id;
29687 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29690 var top = new Roo.bootstrap.Element({
29692 cls : 'roo-navigation-bar-text'
29695 var bottom = new Roo.bootstrap.Element({
29697 cls : 'roo-navigation-bar-text'
29700 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29701 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29703 var topText = new Roo.bootstrap.Element({
29705 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29708 var bottomText = new Roo.bootstrap.Element({
29710 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29713 topText.onRender(top.el, null);
29714 bottomText.onRender(bottom.el, null);
29717 item.bottomEl = bottom;
29720 this.barItems.push(item);
29725 getActive : function()
29727 var active = false;
29729 Roo.each(this.barItems, function(v){
29731 if (!v.isActive()) {
29743 setActiveItem : function(item)
29747 Roo.each(this.barItems, function(v){
29748 if (v.rid == item.rid) {
29752 if (v.isActive()) {
29753 v.setActive(false);
29758 item.setActive(true);
29760 this.fireEvent('changed', this, item, prev);
29763 getBarItem: function(rid)
29767 Roo.each(this.barItems, function(e) {
29768 if (e.rid != rid) {
29779 indexOfItem : function(item)
29783 Roo.each(this.barItems, function(v, i){
29785 if (v.rid != item.rid) {
29796 setActiveNext : function()
29798 var i = this.indexOfItem(this.getActive());
29800 if (i > this.barItems.length) {
29804 this.setActiveItem(this.barItems[i+1]);
29807 setActivePrev : function()
29809 var i = this.indexOfItem(this.getActive());
29815 this.setActiveItem(this.barItems[i-1]);
29818 format : function()
29820 if(!this.barItems.length){
29824 var width = 100 / this.barItems.length;
29826 Roo.each(this.barItems, function(i){
29827 i.el.setStyle('width', width + '%');
29828 i.topEl.el.setStyle('width', width + '%');
29829 i.bottomEl.el.setStyle('width', width + '%');
29838 * Nav Progress Item
29843 * @class Roo.bootstrap.NavProgressItem
29844 * @extends Roo.bootstrap.Component
29845 * Bootstrap NavProgressItem class
29846 * @cfg {String} rid the reference id
29847 * @cfg {Boolean} active (true|false) Is item active default false
29848 * @cfg {Boolean} disabled (true|false) Is item active default false
29849 * @cfg {String} html
29850 * @cfg {String} position (top|bottom) text position default bottom
29851 * @cfg {String} icon show icon instead of number
29854 * Create a new NavProgressItem
29855 * @param {Object} config The config object
29857 Roo.bootstrap.NavProgressItem = function(config){
29858 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29863 * The raw click event for the entire grid.
29864 * @param {Roo.bootstrap.NavProgressItem} this
29865 * @param {Roo.EventObject} e
29872 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29878 position : 'bottom',
29881 getAutoCreate : function()
29883 var iconCls = 'roo-navigation-bar-item-icon';
29885 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29889 cls: 'roo-navigation-bar-item',
29899 cfg.cls += ' active';
29902 cfg.cls += ' disabled';
29908 disable : function()
29910 this.setDisabled(true);
29913 enable : function()
29915 this.setDisabled(false);
29918 initEvents: function()
29920 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29922 this.iconEl.on('click', this.onClick, this);
29925 onClick : function(e)
29927 e.preventDefault();
29933 if(this.fireEvent('click', this, e) === false){
29937 this.parent().setActiveItem(this);
29940 isActive: function ()
29942 return this.active;
29945 setActive : function(state)
29947 if(this.active == state){
29951 this.active = state;
29954 this.el.addClass('active');
29958 this.el.removeClass('active');
29963 setDisabled : function(state)
29965 if(this.disabled == state){
29969 this.disabled = state;
29972 this.el.addClass('disabled');
29976 this.el.removeClass('disabled');
29979 tooltipEl : function()
29981 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29994 * @class Roo.bootstrap.FieldLabel
29995 * @extends Roo.bootstrap.Component
29996 * Bootstrap FieldLabel class
29997 * @cfg {String} html contents of the element
29998 * @cfg {String} tag tag of the element default label
29999 * @cfg {String} cls class of the element
30000 * @cfg {String} target label target
30001 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30002 * @cfg {String} invalidClass default "text-warning"
30003 * @cfg {String} validClass default "text-success"
30004 * @cfg {String} iconTooltip default "This field is required"
30005 * @cfg {String} indicatorpos (left|right) default left
30008 * Create a new FieldLabel
30009 * @param {Object} config The config object
30012 Roo.bootstrap.FieldLabel = function(config){
30013 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30018 * Fires after the field has been marked as invalid.
30019 * @param {Roo.form.FieldLabel} this
30020 * @param {String} msg The validation message
30025 * Fires after the field has been validated with no errors.
30026 * @param {Roo.form.FieldLabel} this
30032 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30039 invalidClass : 'has-warning',
30040 validClass : 'has-success',
30041 iconTooltip : 'This field is required',
30042 indicatorpos : 'left',
30044 getAutoCreate : function(){
30047 if (!this.allowBlank) {
30053 cls : 'roo-bootstrap-field-label ' + this.cls,
30058 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30059 tooltip : this.iconTooltip
30068 if(this.indicatorpos == 'right'){
30071 cls : 'roo-bootstrap-field-label ' + this.cls,
30080 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30081 tooltip : this.iconTooltip
30090 initEvents: function()
30092 Roo.bootstrap.Element.superclass.initEvents.call(this);
30094 this.indicator = this.indicatorEl();
30096 if(this.indicator){
30097 this.indicator.removeClass('visible');
30098 this.indicator.addClass('invisible');
30101 Roo.bootstrap.FieldLabel.register(this);
30104 indicatorEl : function()
30106 var indicator = this.el.select('i.roo-required-indicator',true).first();
30117 * Mark this field as valid
30119 markValid : function()
30121 if(this.indicator){
30122 this.indicator.removeClass('visible');
30123 this.indicator.addClass('invisible');
30126 this.el.removeClass(this.invalidClass);
30128 this.el.addClass(this.validClass);
30130 this.fireEvent('valid', this);
30134 * Mark this field as invalid
30135 * @param {String} msg The validation message
30137 markInvalid : function(msg)
30139 if(this.indicator){
30140 this.indicator.removeClass('invisible');
30141 this.indicator.addClass('visible');
30144 this.el.removeClass(this.validClass);
30146 this.el.addClass(this.invalidClass);
30148 this.fireEvent('invalid', this, msg);
30154 Roo.apply(Roo.bootstrap.FieldLabel, {
30159 * register a FieldLabel Group
30160 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30162 register : function(label)
30164 if(this.groups.hasOwnProperty(label.target)){
30168 this.groups[label.target] = label;
30172 * fetch a FieldLabel Group based on the target
30173 * @param {string} target
30174 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30176 get: function(target) {
30177 if (typeof(this.groups[target]) == 'undefined') {
30181 return this.groups[target] ;
30190 * page DateSplitField.
30196 * @class Roo.bootstrap.DateSplitField
30197 * @extends Roo.bootstrap.Component
30198 * Bootstrap DateSplitField class
30199 * @cfg {string} fieldLabel - the label associated
30200 * @cfg {Number} labelWidth set the width of label (0-12)
30201 * @cfg {String} labelAlign (top|left)
30202 * @cfg {Boolean} dayAllowBlank (true|false) default false
30203 * @cfg {Boolean} monthAllowBlank (true|false) default false
30204 * @cfg {Boolean} yearAllowBlank (true|false) default false
30205 * @cfg {string} dayPlaceholder
30206 * @cfg {string} monthPlaceholder
30207 * @cfg {string} yearPlaceholder
30208 * @cfg {string} dayFormat default 'd'
30209 * @cfg {string} monthFormat default 'm'
30210 * @cfg {string} yearFormat default 'Y'
30211 * @cfg {Number} labellg set the width of label (1-12)
30212 * @cfg {Number} labelmd set the width of label (1-12)
30213 * @cfg {Number} labelsm set the width of label (1-12)
30214 * @cfg {Number} labelxs set the width of label (1-12)
30218 * Create a new DateSplitField
30219 * @param {Object} config The config object
30222 Roo.bootstrap.DateSplitField = function(config){
30223 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30229 * getting the data of years
30230 * @param {Roo.bootstrap.DateSplitField} this
30231 * @param {Object} years
30236 * getting the data of days
30237 * @param {Roo.bootstrap.DateSplitField} this
30238 * @param {Object} days
30243 * Fires after the field has been marked as invalid.
30244 * @param {Roo.form.Field} this
30245 * @param {String} msg The validation message
30250 * Fires after the field has been validated with no errors.
30251 * @param {Roo.form.Field} this
30257 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30260 labelAlign : 'top',
30262 dayAllowBlank : false,
30263 monthAllowBlank : false,
30264 yearAllowBlank : false,
30265 dayPlaceholder : '',
30266 monthPlaceholder : '',
30267 yearPlaceholder : '',
30271 isFormField : true,
30277 getAutoCreate : function()
30281 cls : 'row roo-date-split-field-group',
30286 cls : 'form-hidden-field roo-date-split-field-group-value',
30292 var labelCls = 'col-md-12';
30293 var contentCls = 'col-md-4';
30295 if(this.fieldLabel){
30299 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30303 html : this.fieldLabel
30308 if(this.labelAlign == 'left'){
30310 if(this.labelWidth > 12){
30311 label.style = "width: " + this.labelWidth + 'px';
30314 if(this.labelWidth < 13 && this.labelmd == 0){
30315 this.labelmd = this.labelWidth;
30318 if(this.labellg > 0){
30319 labelCls = ' col-lg-' + this.labellg;
30320 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30323 if(this.labelmd > 0){
30324 labelCls = ' col-md-' + this.labelmd;
30325 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30328 if(this.labelsm > 0){
30329 labelCls = ' col-sm-' + this.labelsm;
30330 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30333 if(this.labelxs > 0){
30334 labelCls = ' col-xs-' + this.labelxs;
30335 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30339 label.cls += ' ' + labelCls;
30341 cfg.cn.push(label);
30344 Roo.each(['day', 'month', 'year'], function(t){
30347 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30354 inputEl: function ()
30356 return this.el.select('.roo-date-split-field-group-value', true).first();
30359 onRender : function(ct, position)
30363 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30365 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30367 this.dayField = new Roo.bootstrap.ComboBox({
30368 allowBlank : this.dayAllowBlank,
30369 alwaysQuery : true,
30370 displayField : 'value',
30373 forceSelection : true,
30375 placeholder : this.dayPlaceholder,
30376 selectOnFocus : true,
30377 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30378 triggerAction : 'all',
30380 valueField : 'value',
30381 store : new Roo.data.SimpleStore({
30382 data : (function() {
30384 _this.fireEvent('days', _this, days);
30387 fields : [ 'value' ]
30390 select : function (_self, record, index)
30392 _this.setValue(_this.getValue());
30397 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30399 this.monthField = new Roo.bootstrap.MonthField({
30400 after : '<i class=\"fa fa-calendar\"></i>',
30401 allowBlank : this.monthAllowBlank,
30402 placeholder : this.monthPlaceholder,
30405 render : function (_self)
30407 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30408 e.preventDefault();
30412 select : function (_self, oldvalue, newvalue)
30414 _this.setValue(_this.getValue());
30419 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30421 this.yearField = new Roo.bootstrap.ComboBox({
30422 allowBlank : this.yearAllowBlank,
30423 alwaysQuery : true,
30424 displayField : 'value',
30427 forceSelection : true,
30429 placeholder : this.yearPlaceholder,
30430 selectOnFocus : true,
30431 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30432 triggerAction : 'all',
30434 valueField : 'value',
30435 store : new Roo.data.SimpleStore({
30436 data : (function() {
30438 _this.fireEvent('years', _this, years);
30441 fields : [ 'value' ]
30444 select : function (_self, record, index)
30446 _this.setValue(_this.getValue());
30451 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30454 setValue : function(v, format)
30456 this.inputEl.dom.value = v;
30458 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30460 var d = Date.parseDate(v, f);
30467 this.setDay(d.format(this.dayFormat));
30468 this.setMonth(d.format(this.monthFormat));
30469 this.setYear(d.format(this.yearFormat));
30476 setDay : function(v)
30478 this.dayField.setValue(v);
30479 this.inputEl.dom.value = this.getValue();
30484 setMonth : function(v)
30486 this.monthField.setValue(v, true);
30487 this.inputEl.dom.value = this.getValue();
30492 setYear : function(v)
30494 this.yearField.setValue(v);
30495 this.inputEl.dom.value = this.getValue();
30500 getDay : function()
30502 return this.dayField.getValue();
30505 getMonth : function()
30507 return this.monthField.getValue();
30510 getYear : function()
30512 return this.yearField.getValue();
30515 getValue : function()
30517 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30519 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30529 this.inputEl.dom.value = '';
30534 validate : function()
30536 var d = this.dayField.validate();
30537 var m = this.monthField.validate();
30538 var y = this.yearField.validate();
30543 (!this.dayAllowBlank && !d) ||
30544 (!this.monthAllowBlank && !m) ||
30545 (!this.yearAllowBlank && !y)
30550 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30559 this.markInvalid();
30564 markValid : function()
30567 var label = this.el.select('label', true).first();
30568 var icon = this.el.select('i.fa-star', true).first();
30574 this.fireEvent('valid', this);
30578 * Mark this field as invalid
30579 * @param {String} msg The validation message
30581 markInvalid : function(msg)
30584 var label = this.el.select('label', true).first();
30585 var icon = this.el.select('i.fa-star', true).first();
30587 if(label && !icon){
30588 this.el.select('.roo-date-split-field-label', true).createChild({
30590 cls : 'text-danger fa fa-lg fa-star',
30591 tooltip : 'This field is required',
30592 style : 'margin-right:5px;'
30596 this.fireEvent('invalid', this, msg);
30599 clearInvalid : function()
30601 var label = this.el.select('label', true).first();
30602 var icon = this.el.select('i.fa-star', true).first();
30608 this.fireEvent('valid', this);
30611 getName: function()
30621 * http://masonry.desandro.com
30623 * The idea is to render all the bricks based on vertical width...
30625 * The original code extends 'outlayer' - we might need to use that....
30631 * @class Roo.bootstrap.LayoutMasonry
30632 * @extends Roo.bootstrap.Component
30633 * Bootstrap Layout Masonry class
30636 * Create a new Element
30637 * @param {Object} config The config object
30640 Roo.bootstrap.LayoutMasonry = function(config){
30642 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30646 Roo.bootstrap.LayoutMasonry.register(this);
30652 * Fire after layout the items
30653 * @param {Roo.bootstrap.LayoutMasonry} this
30654 * @param {Roo.EventObject} e
30661 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30664 * @cfg {Boolean} isLayoutInstant = no animation?
30666 isLayoutInstant : false, // needed?
30669 * @cfg {Number} boxWidth width of the columns
30674 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30679 * @cfg {Number} padWidth padding below box..
30684 * @cfg {Number} gutter gutter width..
30689 * @cfg {Number} maxCols maximum number of columns
30695 * @cfg {Boolean} isAutoInitial defalut true
30697 isAutoInitial : true,
30702 * @cfg {Boolean} isHorizontal defalut false
30704 isHorizontal : false,
30706 currentSize : null,
30712 bricks: null, //CompositeElement
30716 _isLayoutInited : false,
30718 // isAlternative : false, // only use for vertical layout...
30721 * @cfg {Number} alternativePadWidth padding below box..
30723 alternativePadWidth : 50,
30725 selectedBrick : [],
30727 getAutoCreate : function(){
30729 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30733 cls: 'blog-masonary-wrapper ' + this.cls,
30735 cls : 'mas-boxes masonary'
30742 getChildContainer: function( )
30744 if (this.boxesEl) {
30745 return this.boxesEl;
30748 this.boxesEl = this.el.select('.mas-boxes').first();
30750 return this.boxesEl;
30754 initEvents : function()
30758 if(this.isAutoInitial){
30759 Roo.log('hook children rendered');
30760 this.on('childrenrendered', function() {
30761 Roo.log('children rendered');
30767 initial : function()
30769 this.selectedBrick = [];
30771 this.currentSize = this.el.getBox(true);
30773 Roo.EventManager.onWindowResize(this.resize, this);
30775 if(!this.isAutoInitial){
30783 //this.layout.defer(500,this);
30787 resize : function()
30789 var cs = this.el.getBox(true);
30792 this.currentSize.width == cs.width &&
30793 this.currentSize.x == cs.x &&
30794 this.currentSize.height == cs.height &&
30795 this.currentSize.y == cs.y
30797 Roo.log("no change in with or X or Y");
30801 this.currentSize = cs;
30807 layout : function()
30809 this._resetLayout();
30811 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30813 this.layoutItems( isInstant );
30815 this._isLayoutInited = true;
30817 this.fireEvent('layout', this);
30821 _resetLayout : function()
30823 if(this.isHorizontal){
30824 this.horizontalMeasureColumns();
30828 this.verticalMeasureColumns();
30832 verticalMeasureColumns : function()
30834 this.getContainerWidth();
30836 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30837 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30841 var boxWidth = this.boxWidth + this.padWidth;
30843 if(this.containerWidth < this.boxWidth){
30844 boxWidth = this.containerWidth
30847 var containerWidth = this.containerWidth;
30849 var cols = Math.floor(containerWidth / boxWidth);
30851 this.cols = Math.max( cols, 1 );
30853 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30855 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30857 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30859 this.colWidth = boxWidth + avail - this.padWidth;
30861 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30862 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30865 horizontalMeasureColumns : function()
30867 this.getContainerWidth();
30869 var boxWidth = this.boxWidth;
30871 if(this.containerWidth < boxWidth){
30872 boxWidth = this.containerWidth;
30875 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30877 this.el.setHeight(boxWidth);
30881 getContainerWidth : function()
30883 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30886 layoutItems : function( isInstant )
30888 Roo.log(this.bricks);
30890 var items = Roo.apply([], this.bricks);
30892 if(this.isHorizontal){
30893 this._horizontalLayoutItems( items , isInstant );
30897 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30898 // this._verticalAlternativeLayoutItems( items , isInstant );
30902 this._verticalLayoutItems( items , isInstant );
30906 _verticalLayoutItems : function ( items , isInstant)
30908 if ( !items || !items.length ) {
30913 ['xs', 'xs', 'xs', 'tall'],
30914 ['xs', 'xs', 'tall'],
30915 ['xs', 'xs', 'sm'],
30916 ['xs', 'xs', 'xs'],
30922 ['sm', 'xs', 'xs'],
30926 ['tall', 'xs', 'xs', 'xs'],
30927 ['tall', 'xs', 'xs'],
30939 Roo.each(items, function(item, k){
30941 switch (item.size) {
30942 // these layouts take up a full box,
30953 boxes.push([item]);
30976 var filterPattern = function(box, length)
30984 var pattern = box.slice(0, length);
30988 Roo.each(pattern, function(i){
30989 format.push(i.size);
30992 Roo.each(standard, function(s){
30994 if(String(s) != String(format)){
31003 if(!match && length == 1){
31008 filterPattern(box, length - 1);
31012 queue.push(pattern);
31014 box = box.slice(length, box.length);
31016 filterPattern(box, 4);
31022 Roo.each(boxes, function(box, k){
31028 if(box.length == 1){
31033 filterPattern(box, 4);
31037 this._processVerticalLayoutQueue( queue, isInstant );
31041 // _verticalAlternativeLayoutItems : function( items , isInstant )
31043 // if ( !items || !items.length ) {
31047 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31051 _horizontalLayoutItems : function ( items , isInstant)
31053 if ( !items || !items.length || items.length < 3) {
31059 var eItems = items.slice(0, 3);
31061 items = items.slice(3, items.length);
31064 ['xs', 'xs', 'xs', 'wide'],
31065 ['xs', 'xs', 'wide'],
31066 ['xs', 'xs', 'sm'],
31067 ['xs', 'xs', 'xs'],
31073 ['sm', 'xs', 'xs'],
31077 ['wide', 'xs', 'xs', 'xs'],
31078 ['wide', 'xs', 'xs'],
31091 Roo.each(items, function(item, k){
31093 switch (item.size) {
31104 boxes.push([item]);
31128 var filterPattern = function(box, length)
31136 var pattern = box.slice(0, length);
31140 Roo.each(pattern, function(i){
31141 format.push(i.size);
31144 Roo.each(standard, function(s){
31146 if(String(s) != String(format)){
31155 if(!match && length == 1){
31160 filterPattern(box, length - 1);
31164 queue.push(pattern);
31166 box = box.slice(length, box.length);
31168 filterPattern(box, 4);
31174 Roo.each(boxes, function(box, k){
31180 if(box.length == 1){
31185 filterPattern(box, 4);
31192 var pos = this.el.getBox(true);
31196 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31198 var hit_end = false;
31200 Roo.each(queue, function(box){
31204 Roo.each(box, function(b){
31206 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31216 Roo.each(box, function(b){
31218 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31221 mx = Math.max(mx, b.x);
31225 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31229 Roo.each(box, function(b){
31231 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31245 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31248 /** Sets position of item in DOM
31249 * @param {Element} item
31250 * @param {Number} x - horizontal position
31251 * @param {Number} y - vertical position
31252 * @param {Boolean} isInstant - disables transitions
31254 _processVerticalLayoutQueue : function( queue, isInstant )
31256 var pos = this.el.getBox(true);
31261 for (var i = 0; i < this.cols; i++){
31265 Roo.each(queue, function(box, k){
31267 var col = k % this.cols;
31269 Roo.each(box, function(b,kk){
31271 b.el.position('absolute');
31273 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31274 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31276 if(b.size == 'md-left' || b.size == 'md-right'){
31277 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31278 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31281 b.el.setWidth(width);
31282 b.el.setHeight(height);
31284 b.el.select('iframe',true).setSize(width,height);
31288 for (var i = 0; i < this.cols; i++){
31290 if(maxY[i] < maxY[col]){
31295 col = Math.min(col, i);
31299 x = pos.x + col * (this.colWidth + this.padWidth);
31303 var positions = [];
31305 switch (box.length){
31307 positions = this.getVerticalOneBoxColPositions(x, y, box);
31310 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31313 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31316 positions = this.getVerticalFourBoxColPositions(x, y, box);
31322 Roo.each(box, function(b,kk){
31324 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31326 var sz = b.el.getSize();
31328 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31336 for (var i = 0; i < this.cols; i++){
31337 mY = Math.max(mY, maxY[i]);
31340 this.el.setHeight(mY - pos.y);
31344 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31346 // var pos = this.el.getBox(true);
31349 // var maxX = pos.right;
31351 // var maxHeight = 0;
31353 // Roo.each(items, function(item, k){
31357 // item.el.position('absolute');
31359 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31361 // item.el.setWidth(width);
31363 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31365 // item.el.setHeight(height);
31368 // item.el.setXY([x, y], isInstant ? false : true);
31370 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31373 // y = y + height + this.alternativePadWidth;
31375 // maxHeight = maxHeight + height + this.alternativePadWidth;
31379 // this.el.setHeight(maxHeight);
31383 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31385 var pos = this.el.getBox(true);
31390 var maxX = pos.right;
31392 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31394 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31396 Roo.each(queue, function(box, k){
31398 Roo.each(box, function(b, kk){
31400 b.el.position('absolute');
31402 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31403 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31405 if(b.size == 'md-left' || b.size == 'md-right'){
31406 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31407 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31410 b.el.setWidth(width);
31411 b.el.setHeight(height);
31419 var positions = [];
31421 switch (box.length){
31423 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31426 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31429 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31432 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31438 Roo.each(box, function(b,kk){
31440 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31442 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31450 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31452 Roo.each(eItems, function(b,k){
31454 b.size = (k == 0) ? 'sm' : 'xs';
31455 b.x = (k == 0) ? 2 : 1;
31456 b.y = (k == 0) ? 2 : 1;
31458 b.el.position('absolute');
31460 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31462 b.el.setWidth(width);
31464 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31466 b.el.setHeight(height);
31470 var positions = [];
31473 x : maxX - this.unitWidth * 2 - this.gutter,
31478 x : maxX - this.unitWidth,
31479 y : minY + (this.unitWidth + this.gutter) * 2
31483 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31487 Roo.each(eItems, function(b,k){
31489 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31495 getVerticalOneBoxColPositions : function(x, y, box)
31499 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31501 if(box[0].size == 'md-left'){
31505 if(box[0].size == 'md-right'){
31510 x : x + (this.unitWidth + this.gutter) * rand,
31517 getVerticalTwoBoxColPositions : function(x, y, box)
31521 if(box[0].size == 'xs'){
31525 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31529 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31543 x : x + (this.unitWidth + this.gutter) * 2,
31544 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31551 getVerticalThreeBoxColPositions : function(x, y, box)
31555 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31563 x : x + (this.unitWidth + this.gutter) * 1,
31568 x : x + (this.unitWidth + this.gutter) * 2,
31576 if(box[0].size == 'xs' && box[1].size == 'xs'){
31585 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31589 x : x + (this.unitWidth + this.gutter) * 1,
31603 x : x + (this.unitWidth + this.gutter) * 2,
31608 x : x + (this.unitWidth + this.gutter) * 2,
31609 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31616 getVerticalFourBoxColPositions : function(x, y, box)
31620 if(box[0].size == 'xs'){
31629 y : y + (this.unitHeight + this.gutter) * 1
31634 y : y + (this.unitHeight + this.gutter) * 2
31638 x : x + (this.unitWidth + this.gutter) * 1,
31652 x : x + (this.unitWidth + this.gutter) * 2,
31657 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31658 y : y + (this.unitHeight + this.gutter) * 1
31662 x : x + (this.unitWidth + this.gutter) * 2,
31663 y : y + (this.unitWidth + this.gutter) * 2
31670 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31674 if(box[0].size == 'md-left'){
31676 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31683 if(box[0].size == 'md-right'){
31685 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31686 y : minY + (this.unitWidth + this.gutter) * 1
31692 var rand = Math.floor(Math.random() * (4 - box[0].y));
31695 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31696 y : minY + (this.unitWidth + this.gutter) * rand
31703 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31707 if(box[0].size == 'xs'){
31710 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31715 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31716 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31724 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31729 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31730 y : minY + (this.unitWidth + this.gutter) * 2
31737 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31741 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31744 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31749 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31750 y : minY + (this.unitWidth + this.gutter) * 1
31754 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31755 y : minY + (this.unitWidth + this.gutter) * 2
31762 if(box[0].size == 'xs' && box[1].size == 'xs'){
31765 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31770 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31775 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31776 y : minY + (this.unitWidth + this.gutter) * 1
31784 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31789 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31790 y : minY + (this.unitWidth + this.gutter) * 2
31794 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31795 y : minY + (this.unitWidth + this.gutter) * 2
31802 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31806 if(box[0].size == 'xs'){
31809 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31814 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31819 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),
31824 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31825 y : minY + (this.unitWidth + this.gutter) * 1
31833 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31838 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31839 y : minY + (this.unitWidth + this.gutter) * 2
31843 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31844 y : minY + (this.unitWidth + this.gutter) * 2
31848 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),
31849 y : minY + (this.unitWidth + this.gutter) * 2
31857 * remove a Masonry Brick
31858 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31860 removeBrick : function(brick_id)
31866 for (var i = 0; i<this.bricks.length; i++) {
31867 if (this.bricks[i].id == brick_id) {
31868 this.bricks.splice(i,1);
31869 this.el.dom.removeChild(Roo.get(brick_id).dom);
31876 * adds a Masonry Brick
31877 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31879 addBrick : function(cfg)
31881 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31882 //this.register(cn);
31883 cn.parentId = this.id;
31884 cn.onRender(this.el, null);
31889 * register a Masonry Brick
31890 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31893 register : function(brick)
31895 this.bricks.push(brick);
31896 brick.masonryId = this.id;
31900 * clear all the Masonry Brick
31902 clearAll : function()
31905 //this.getChildContainer().dom.innerHTML = "";
31906 this.el.dom.innerHTML = '';
31909 getSelected : function()
31911 if (!this.selectedBrick) {
31915 return this.selectedBrick;
31919 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31923 * register a Masonry Layout
31924 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31927 register : function(layout)
31929 this.groups[layout.id] = layout;
31932 * fetch a Masonry Layout based on the masonry layout ID
31933 * @param {string} the masonry layout to add
31934 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31937 get: function(layout_id) {
31938 if (typeof(this.groups[layout_id]) == 'undefined') {
31941 return this.groups[layout_id] ;
31953 * http://masonry.desandro.com
31955 * The idea is to render all the bricks based on vertical width...
31957 * The original code extends 'outlayer' - we might need to use that....
31963 * @class Roo.bootstrap.LayoutMasonryAuto
31964 * @extends Roo.bootstrap.Component
31965 * Bootstrap Layout Masonry class
31968 * Create a new Element
31969 * @param {Object} config The config object
31972 Roo.bootstrap.LayoutMasonryAuto = function(config){
31973 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31976 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31979 * @cfg {Boolean} isFitWidth - resize the width..
31981 isFitWidth : false, // options..
31983 * @cfg {Boolean} isOriginLeft = left align?
31985 isOriginLeft : true,
31987 * @cfg {Boolean} isOriginTop = top align?
31989 isOriginTop : false,
31991 * @cfg {Boolean} isLayoutInstant = no animation?
31993 isLayoutInstant : false, // needed?
31995 * @cfg {Boolean} isResizingContainer = not sure if this is used..
31997 isResizingContainer : true,
31999 * @cfg {Number} columnWidth width of the columns
32005 * @cfg {Number} maxCols maximum number of columns
32010 * @cfg {Number} padHeight padding below box..
32016 * @cfg {Boolean} isAutoInitial defalut true
32019 isAutoInitial : true,
32025 initialColumnWidth : 0,
32026 currentSize : null,
32028 colYs : null, // array.
32035 bricks: null, //CompositeElement
32036 cols : 0, // array?
32037 // element : null, // wrapped now this.el
32038 _isLayoutInited : null,
32041 getAutoCreate : function(){
32045 cls: 'blog-masonary-wrapper ' + this.cls,
32047 cls : 'mas-boxes masonary'
32054 getChildContainer: function( )
32056 if (this.boxesEl) {
32057 return this.boxesEl;
32060 this.boxesEl = this.el.select('.mas-boxes').first();
32062 return this.boxesEl;
32066 initEvents : function()
32070 if(this.isAutoInitial){
32071 Roo.log('hook children rendered');
32072 this.on('childrenrendered', function() {
32073 Roo.log('children rendered');
32080 initial : function()
32082 this.reloadItems();
32084 this.currentSize = this.el.getBox(true);
32086 /// was window resize... - let's see if this works..
32087 Roo.EventManager.onWindowResize(this.resize, this);
32089 if(!this.isAutoInitial){
32094 this.layout.defer(500,this);
32097 reloadItems: function()
32099 this.bricks = this.el.select('.masonry-brick', true);
32101 this.bricks.each(function(b) {
32102 //Roo.log(b.getSize());
32103 if (!b.attr('originalwidth')) {
32104 b.attr('originalwidth', b.getSize().width);
32109 Roo.log(this.bricks.elements.length);
32112 resize : function()
32115 var cs = this.el.getBox(true);
32117 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32118 Roo.log("no change in with or X");
32121 this.currentSize = cs;
32125 layout : function()
32128 this._resetLayout();
32129 //this._manageStamps();
32131 // don't animate first layout
32132 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32133 this.layoutItems( isInstant );
32135 // flag for initalized
32136 this._isLayoutInited = true;
32139 layoutItems : function( isInstant )
32141 //var items = this._getItemsForLayout( this.items );
32142 // original code supports filtering layout items.. we just ignore it..
32144 this._layoutItems( this.bricks , isInstant );
32146 this._postLayout();
32148 _layoutItems : function ( items , isInstant)
32150 //this.fireEvent( 'layout', this, items );
32153 if ( !items || !items.elements.length ) {
32154 // no items, emit event with empty array
32159 items.each(function(item) {
32160 Roo.log("layout item");
32162 // get x/y object from method
32163 var position = this._getItemLayoutPosition( item );
32165 position.item = item;
32166 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32167 queue.push( position );
32170 this._processLayoutQueue( queue );
32172 /** Sets position of item in DOM
32173 * @param {Element} item
32174 * @param {Number} x - horizontal position
32175 * @param {Number} y - vertical position
32176 * @param {Boolean} isInstant - disables transitions
32178 _processLayoutQueue : function( queue )
32180 for ( var i=0, len = queue.length; i < len; i++ ) {
32181 var obj = queue[i];
32182 obj.item.position('absolute');
32183 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32189 * Any logic you want to do after each layout,
32190 * i.e. size the container
32192 _postLayout : function()
32194 this.resizeContainer();
32197 resizeContainer : function()
32199 if ( !this.isResizingContainer ) {
32202 var size = this._getContainerSize();
32204 this.el.setSize(size.width,size.height);
32205 this.boxesEl.setSize(size.width,size.height);
32211 _resetLayout : function()
32213 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32214 this.colWidth = this.el.getWidth();
32215 //this.gutter = this.el.getWidth();
32217 this.measureColumns();
32223 this.colYs.push( 0 );
32229 measureColumns : function()
32231 this.getContainerWidth();
32232 // if columnWidth is 0, default to outerWidth of first item
32233 if ( !this.columnWidth ) {
32234 var firstItem = this.bricks.first();
32235 Roo.log(firstItem);
32236 this.columnWidth = this.containerWidth;
32237 if (firstItem && firstItem.attr('originalwidth') ) {
32238 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32240 // columnWidth fall back to item of first element
32241 Roo.log("set column width?");
32242 this.initialColumnWidth = this.columnWidth ;
32244 // if first elem has no width, default to size of container
32249 if (this.initialColumnWidth) {
32250 this.columnWidth = this.initialColumnWidth;
32255 // column width is fixed at the top - however if container width get's smaller we should
32258 // this bit calcs how man columns..
32260 var columnWidth = this.columnWidth += this.gutter;
32262 // calculate columns
32263 var containerWidth = this.containerWidth + this.gutter;
32265 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32266 // fix rounding errors, typically with gutters
32267 var excess = columnWidth - containerWidth % columnWidth;
32270 // if overshoot is less than a pixel, round up, otherwise floor it
32271 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32272 cols = Math[ mathMethod ]( cols );
32273 this.cols = Math.max( cols, 1 );
32274 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32276 // padding positioning..
32277 var totalColWidth = this.cols * this.columnWidth;
32278 var padavail = this.containerWidth - totalColWidth;
32279 // so for 2 columns - we need 3 'pads'
32281 var padNeeded = (1+this.cols) * this.padWidth;
32283 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32285 this.columnWidth += padExtra
32286 //this.padWidth = Math.floor(padavail / ( this.cols));
32288 // adjust colum width so that padding is fixed??
32290 // we have 3 columns ... total = width * 3
32291 // we have X left over... that should be used by
32293 //if (this.expandC) {
32301 getContainerWidth : function()
32303 /* // container is parent if fit width
32304 var container = this.isFitWidth ? this.element.parentNode : this.element;
32305 // check that this.size and size are there
32306 // IE8 triggers resize on body size change, so they might not be
32308 var size = getSize( container ); //FIXME
32309 this.containerWidth = size && size.innerWidth; //FIXME
32312 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32316 _getItemLayoutPosition : function( item ) // what is item?
32318 // we resize the item to our columnWidth..
32320 item.setWidth(this.columnWidth);
32321 item.autoBoxAdjust = false;
32323 var sz = item.getSize();
32325 // how many columns does this brick span
32326 var remainder = this.containerWidth % this.columnWidth;
32328 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32329 // round if off by 1 pixel, otherwise use ceil
32330 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32331 colSpan = Math.min( colSpan, this.cols );
32333 // normally this should be '1' as we dont' currently allow multi width columns..
32335 var colGroup = this._getColGroup( colSpan );
32336 // get the minimum Y value from the columns
32337 var minimumY = Math.min.apply( Math, colGroup );
32338 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32340 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32342 // position the brick
32344 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32345 y: this.currentSize.y + minimumY + this.padHeight
32349 // apply setHeight to necessary columns
32350 var setHeight = minimumY + sz.height + this.padHeight;
32351 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32353 var setSpan = this.cols + 1 - colGroup.length;
32354 for ( var i = 0; i < setSpan; i++ ) {
32355 this.colYs[ shortColIndex + i ] = setHeight ;
32362 * @param {Number} colSpan - number of columns the element spans
32363 * @returns {Array} colGroup
32365 _getColGroup : function( colSpan )
32367 if ( colSpan < 2 ) {
32368 // if brick spans only one column, use all the column Ys
32373 // how many different places could this brick fit horizontally
32374 var groupCount = this.cols + 1 - colSpan;
32375 // for each group potential horizontal position
32376 for ( var i = 0; i < groupCount; i++ ) {
32377 // make an array of colY values for that one group
32378 var groupColYs = this.colYs.slice( i, i + colSpan );
32379 // and get the max value of the array
32380 colGroup[i] = Math.max.apply( Math, groupColYs );
32385 _manageStamp : function( stamp )
32387 var stampSize = stamp.getSize();
32388 var offset = stamp.getBox();
32389 // get the columns that this stamp affects
32390 var firstX = this.isOriginLeft ? offset.x : offset.right;
32391 var lastX = firstX + stampSize.width;
32392 var firstCol = Math.floor( firstX / this.columnWidth );
32393 firstCol = Math.max( 0, firstCol );
32395 var lastCol = Math.floor( lastX / this.columnWidth );
32396 // lastCol should not go over if multiple of columnWidth #425
32397 lastCol -= lastX % this.columnWidth ? 0 : 1;
32398 lastCol = Math.min( this.cols - 1, lastCol );
32400 // set colYs to bottom of the stamp
32401 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32404 for ( var i = firstCol; i <= lastCol; i++ ) {
32405 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32410 _getContainerSize : function()
32412 this.maxY = Math.max.apply( Math, this.colYs );
32417 if ( this.isFitWidth ) {
32418 size.width = this._getContainerFitWidth();
32424 _getContainerFitWidth : function()
32426 var unusedCols = 0;
32427 // count unused columns
32430 if ( this.colYs[i] !== 0 ) {
32435 // fit container to columns that have been used
32436 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32439 needsResizeLayout : function()
32441 var previousWidth = this.containerWidth;
32442 this.getContainerWidth();
32443 return previousWidth !== this.containerWidth;
32458 * @class Roo.bootstrap.MasonryBrick
32459 * @extends Roo.bootstrap.Component
32460 * Bootstrap MasonryBrick class
32463 * Create a new MasonryBrick
32464 * @param {Object} config The config object
32467 Roo.bootstrap.MasonryBrick = function(config){
32469 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32471 Roo.bootstrap.MasonryBrick.register(this);
32477 * When a MasonryBrick is clcik
32478 * @param {Roo.bootstrap.MasonryBrick} this
32479 * @param {Roo.EventObject} e
32485 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32488 * @cfg {String} title
32492 * @cfg {String} html
32496 * @cfg {String} bgimage
32500 * @cfg {String} videourl
32504 * @cfg {String} cls
32508 * @cfg {String} href
32512 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32517 * @cfg {String} placetitle (center|bottom)
32522 * @cfg {Boolean} isFitContainer defalut true
32524 isFitContainer : true,
32527 * @cfg {Boolean} preventDefault defalut false
32529 preventDefault : false,
32532 * @cfg {Boolean} inverse defalut false
32534 maskInverse : false,
32536 getAutoCreate : function()
32538 if(!this.isFitContainer){
32539 return this.getSplitAutoCreate();
32542 var cls = 'masonry-brick masonry-brick-full';
32544 if(this.href.length){
32545 cls += ' masonry-brick-link';
32548 if(this.bgimage.length){
32549 cls += ' masonry-brick-image';
32552 if(this.maskInverse){
32553 cls += ' mask-inverse';
32556 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32557 cls += ' enable-mask';
32561 cls += ' masonry-' + this.size + '-brick';
32564 if(this.placetitle.length){
32566 switch (this.placetitle) {
32568 cls += ' masonry-center-title';
32571 cls += ' masonry-bottom-title';
32578 if(!this.html.length && !this.bgimage.length){
32579 cls += ' masonry-center-title';
32582 if(!this.html.length && this.bgimage.length){
32583 cls += ' masonry-bottom-title';
32588 cls += ' ' + this.cls;
32592 tag: (this.href.length) ? 'a' : 'div',
32597 cls: 'masonry-brick-mask'
32601 cls: 'masonry-brick-paragraph',
32607 if(this.href.length){
32608 cfg.href = this.href;
32611 var cn = cfg.cn[1].cn;
32613 if(this.title.length){
32616 cls: 'masonry-brick-title',
32621 if(this.html.length){
32624 cls: 'masonry-brick-text',
32629 if (!this.title.length && !this.html.length) {
32630 cfg.cn[1].cls += ' hide';
32633 if(this.bgimage.length){
32636 cls: 'masonry-brick-image-view',
32641 if(this.videourl.length){
32642 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32643 // youtube support only?
32646 cls: 'masonry-brick-image-view',
32649 allowfullscreen : true
32657 getSplitAutoCreate : function()
32659 var cls = 'masonry-brick masonry-brick-split';
32661 if(this.href.length){
32662 cls += ' masonry-brick-link';
32665 if(this.bgimage.length){
32666 cls += ' masonry-brick-image';
32670 cls += ' masonry-' + this.size + '-brick';
32673 switch (this.placetitle) {
32675 cls += ' masonry-center-title';
32678 cls += ' masonry-bottom-title';
32681 if(!this.bgimage.length){
32682 cls += ' masonry-center-title';
32685 if(this.bgimage.length){
32686 cls += ' masonry-bottom-title';
32692 cls += ' ' + this.cls;
32696 tag: (this.href.length) ? 'a' : 'div',
32701 cls: 'masonry-brick-split-head',
32705 cls: 'masonry-brick-paragraph',
32712 cls: 'masonry-brick-split-body',
32718 if(this.href.length){
32719 cfg.href = this.href;
32722 if(this.title.length){
32723 cfg.cn[0].cn[0].cn.push({
32725 cls: 'masonry-brick-title',
32730 if(this.html.length){
32731 cfg.cn[1].cn.push({
32733 cls: 'masonry-brick-text',
32738 if(this.bgimage.length){
32739 cfg.cn[0].cn.push({
32741 cls: 'masonry-brick-image-view',
32746 if(this.videourl.length){
32747 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32748 // youtube support only?
32749 cfg.cn[0].cn.cn.push({
32751 cls: 'masonry-brick-image-view',
32754 allowfullscreen : true
32761 initEvents: function()
32763 switch (this.size) {
32796 this.el.on('touchstart', this.onTouchStart, this);
32797 this.el.on('touchmove', this.onTouchMove, this);
32798 this.el.on('touchend', this.onTouchEnd, this);
32799 this.el.on('contextmenu', this.onContextMenu, this);
32801 this.el.on('mouseenter' ,this.enter, this);
32802 this.el.on('mouseleave', this.leave, this);
32803 this.el.on('click', this.onClick, this);
32806 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32807 this.parent().bricks.push(this);
32812 onClick: function(e, el)
32814 var time = this.endTimer - this.startTimer;
32815 // Roo.log(e.preventDefault());
32818 e.preventDefault();
32823 if(!this.preventDefault){
32827 e.preventDefault();
32829 if (this.activeClass != '') {
32830 this.selectBrick();
32833 this.fireEvent('click', this, e);
32836 enter: function(e, el)
32838 e.preventDefault();
32840 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32844 if(this.bgimage.length && this.html.length){
32845 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32849 leave: function(e, el)
32851 e.preventDefault();
32853 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32857 if(this.bgimage.length && this.html.length){
32858 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32862 onTouchStart: function(e, el)
32864 // e.preventDefault();
32866 this.touchmoved = false;
32868 if(!this.isFitContainer){
32872 if(!this.bgimage.length || !this.html.length){
32876 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32878 this.timer = new Date().getTime();
32882 onTouchMove: function(e, el)
32884 this.touchmoved = true;
32887 onContextMenu : function(e,el)
32889 e.preventDefault();
32890 e.stopPropagation();
32894 onTouchEnd: function(e, el)
32896 // e.preventDefault();
32898 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32905 if(!this.bgimage.length || !this.html.length){
32907 if(this.href.length){
32908 window.location.href = this.href;
32914 if(!this.isFitContainer){
32918 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32920 window.location.href = this.href;
32923 //selection on single brick only
32924 selectBrick : function() {
32926 if (!this.parentId) {
32930 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32931 var index = m.selectedBrick.indexOf(this.id);
32934 m.selectedBrick.splice(index,1);
32935 this.el.removeClass(this.activeClass);
32939 for(var i = 0; i < m.selectedBrick.length; i++) {
32940 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32941 b.el.removeClass(b.activeClass);
32944 m.selectedBrick = [];
32946 m.selectedBrick.push(this.id);
32947 this.el.addClass(this.activeClass);
32951 isSelected : function(){
32952 return this.el.hasClass(this.activeClass);
32957 Roo.apply(Roo.bootstrap.MasonryBrick, {
32960 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32962 * register a Masonry Brick
32963 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32966 register : function(brick)
32968 //this.groups[brick.id] = brick;
32969 this.groups.add(brick.id, brick);
32972 * fetch a masonry brick based on the masonry brick ID
32973 * @param {string} the masonry brick to add
32974 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32977 get: function(brick_id)
32979 // if (typeof(this.groups[brick_id]) == 'undefined') {
32982 // return this.groups[brick_id] ;
32984 if(this.groups.key(brick_id)) {
32985 return this.groups.key(brick_id);
33003 * @class Roo.bootstrap.Brick
33004 * @extends Roo.bootstrap.Component
33005 * Bootstrap Brick class
33008 * Create a new Brick
33009 * @param {Object} config The config object
33012 Roo.bootstrap.Brick = function(config){
33013 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33019 * When a Brick is click
33020 * @param {Roo.bootstrap.Brick} this
33021 * @param {Roo.EventObject} e
33027 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33030 * @cfg {String} title
33034 * @cfg {String} html
33038 * @cfg {String} bgimage
33042 * @cfg {String} cls
33046 * @cfg {String} href
33050 * @cfg {String} video
33054 * @cfg {Boolean} square
33058 getAutoCreate : function()
33060 var cls = 'roo-brick';
33062 if(this.href.length){
33063 cls += ' roo-brick-link';
33066 if(this.bgimage.length){
33067 cls += ' roo-brick-image';
33070 if(!this.html.length && !this.bgimage.length){
33071 cls += ' roo-brick-center-title';
33074 if(!this.html.length && this.bgimage.length){
33075 cls += ' roo-brick-bottom-title';
33079 cls += ' ' + this.cls;
33083 tag: (this.href.length) ? 'a' : 'div',
33088 cls: 'roo-brick-paragraph',
33094 if(this.href.length){
33095 cfg.href = this.href;
33098 var cn = cfg.cn[0].cn;
33100 if(this.title.length){
33103 cls: 'roo-brick-title',
33108 if(this.html.length){
33111 cls: 'roo-brick-text',
33118 if(this.bgimage.length){
33121 cls: 'roo-brick-image-view',
33129 initEvents: function()
33131 if(this.title.length || this.html.length){
33132 this.el.on('mouseenter' ,this.enter, this);
33133 this.el.on('mouseleave', this.leave, this);
33136 Roo.EventManager.onWindowResize(this.resize, this);
33138 if(this.bgimage.length){
33139 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33140 this.imageEl.on('load', this.onImageLoad, this);
33147 onImageLoad : function()
33152 resize : function()
33154 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33156 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33158 if(this.bgimage.length){
33159 var image = this.el.select('.roo-brick-image-view', true).first();
33161 image.setWidth(paragraph.getWidth());
33164 image.setHeight(paragraph.getWidth());
33167 this.el.setHeight(image.getHeight());
33168 paragraph.setHeight(image.getHeight());
33174 enter: function(e, el)
33176 e.preventDefault();
33178 if(this.bgimage.length){
33179 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33180 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33184 leave: function(e, el)
33186 e.preventDefault();
33188 if(this.bgimage.length){
33189 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33190 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33205 * @class Roo.bootstrap.NumberField
33206 * @extends Roo.bootstrap.Input
33207 * Bootstrap NumberField class
33213 * Create a new NumberField
33214 * @param {Object} config The config object
33217 Roo.bootstrap.NumberField = function(config){
33218 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33221 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33224 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33226 allowDecimals : true,
33228 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33230 decimalSeparator : ".",
33232 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33234 decimalPrecision : 2,
33236 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33238 allowNegative : true,
33241 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33245 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33247 minValue : Number.NEGATIVE_INFINITY,
33249 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33251 maxValue : Number.MAX_VALUE,
33253 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33255 minText : "The minimum value for this field is {0}",
33257 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33259 maxText : "The maximum value for this field is {0}",
33261 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33262 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33264 nanText : "{0} is not a valid number",
33266 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33268 thousandsDelimiter : false,
33270 * @cfg {String} valueAlign alignment of value
33272 valueAlign : "left",
33274 getAutoCreate : function()
33276 var hiddenInput = {
33280 cls: 'hidden-number-input'
33284 hiddenInput.name = this.name;
33289 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33291 this.name = hiddenInput.name;
33293 if(cfg.cn.length > 0) {
33294 cfg.cn.push(hiddenInput);
33301 initEvents : function()
33303 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33305 var allowed = "0123456789";
33307 if(this.allowDecimals){
33308 allowed += this.decimalSeparator;
33311 if(this.allowNegative){
33315 if(this.thousandsDelimiter) {
33319 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33321 var keyPress = function(e){
33323 var k = e.getKey();
33325 var c = e.getCharCode();
33328 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33329 allowed.indexOf(String.fromCharCode(c)) === -1
33335 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33339 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33344 this.el.on("keypress", keyPress, this);
33347 validateValue : function(value)
33350 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33354 var num = this.parseValue(value);
33357 this.markInvalid(String.format(this.nanText, value));
33361 if(num < this.minValue){
33362 this.markInvalid(String.format(this.minText, this.minValue));
33366 if(num > this.maxValue){
33367 this.markInvalid(String.format(this.maxText, this.maxValue));
33374 getValue : function()
33376 var v = this.hiddenEl().getValue();
33378 return this.fixPrecision(this.parseValue(v));
33381 parseValue : function(value)
33383 if(this.thousandsDelimiter) {
33385 r = new RegExp(",", "g");
33386 value = value.replace(r, "");
33389 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33390 return isNaN(value) ? '' : value;
33393 fixPrecision : function(value)
33395 if(this.thousandsDelimiter) {
33397 r = new RegExp(",", "g");
33398 value = value.replace(r, "");
33401 var nan = isNaN(value);
33403 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33404 return nan ? '' : value;
33406 return parseFloat(value).toFixed(this.decimalPrecision);
33409 setValue : function(v)
33411 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33417 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33419 this.inputEl().dom.value = (v == '') ? '' :
33420 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33422 if(!this.allowZero && v === '0') {
33423 this.hiddenEl().dom.value = '';
33424 this.inputEl().dom.value = '';
33431 decimalPrecisionFcn : function(v)
33433 return Math.floor(v);
33436 beforeBlur : function()
33438 var v = this.parseValue(this.getRawValue());
33440 if(v || v === 0 || v === ''){
33445 hiddenEl : function()
33447 return this.el.select('input.hidden-number-input',true).first();
33459 * @class Roo.bootstrap.DocumentSlider
33460 * @extends Roo.bootstrap.Component
33461 * Bootstrap DocumentSlider class
33464 * Create a new DocumentViewer
33465 * @param {Object} config The config object
33468 Roo.bootstrap.DocumentSlider = function(config){
33469 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33476 * Fire after initEvent
33477 * @param {Roo.bootstrap.DocumentSlider} this
33482 * Fire after update
33483 * @param {Roo.bootstrap.DocumentSlider} this
33489 * @param {Roo.bootstrap.DocumentSlider} this
33495 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33501 getAutoCreate : function()
33505 cls : 'roo-document-slider',
33509 cls : 'roo-document-slider-header',
33513 cls : 'roo-document-slider-header-title'
33519 cls : 'roo-document-slider-body',
33523 cls : 'roo-document-slider-prev',
33527 cls : 'fa fa-chevron-left'
33533 cls : 'roo-document-slider-thumb',
33537 cls : 'roo-document-slider-image'
33543 cls : 'roo-document-slider-next',
33547 cls : 'fa fa-chevron-right'
33559 initEvents : function()
33561 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33562 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33564 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33565 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33567 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33568 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33570 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33571 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33573 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33574 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33576 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33577 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33579 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33580 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33582 this.thumbEl.on('click', this.onClick, this);
33584 this.prevIndicator.on('click', this.prev, this);
33586 this.nextIndicator.on('click', this.next, this);
33590 initial : function()
33592 if(this.files.length){
33593 this.indicator = 1;
33597 this.fireEvent('initial', this);
33600 update : function()
33602 this.imageEl.attr('src', this.files[this.indicator - 1]);
33604 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33606 this.prevIndicator.show();
33608 if(this.indicator == 1){
33609 this.prevIndicator.hide();
33612 this.nextIndicator.show();
33614 if(this.indicator == this.files.length){
33615 this.nextIndicator.hide();
33618 this.thumbEl.scrollTo('top');
33620 this.fireEvent('update', this);
33623 onClick : function(e)
33625 e.preventDefault();
33627 this.fireEvent('click', this);
33632 e.preventDefault();
33634 this.indicator = Math.max(1, this.indicator - 1);
33641 e.preventDefault();
33643 this.indicator = Math.min(this.files.length, this.indicator + 1);
33657 * @class Roo.bootstrap.RadioSet
33658 * @extends Roo.bootstrap.Input
33659 * Bootstrap RadioSet class
33660 * @cfg {String} indicatorpos (left|right) default left
33661 * @cfg {Boolean} inline (true|false) inline the element (default true)
33662 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33664 * Create a new RadioSet
33665 * @param {Object} config The config object
33668 Roo.bootstrap.RadioSet = function(config){
33670 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33674 Roo.bootstrap.RadioSet.register(this);
33679 * Fires when the element is checked or unchecked.
33680 * @param {Roo.bootstrap.RadioSet} this This radio
33681 * @param {Roo.bootstrap.Radio} item The checked item
33686 * Fires when the element is click.
33687 * @param {Roo.bootstrap.RadioSet} this This radio set
33688 * @param {Roo.bootstrap.Radio} item The checked item
33689 * @param {Roo.EventObject} e The event object
33696 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33704 indicatorpos : 'left',
33706 getAutoCreate : function()
33710 cls : 'roo-radio-set-label',
33714 html : this.fieldLabel
33719 if(this.indicatorpos == 'left'){
33722 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33723 tooltip : 'This field is required'
33728 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33729 tooltip : 'This field is required'
33735 cls : 'roo-radio-set-items'
33738 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33740 if (align === 'left' && this.fieldLabel.length) {
33743 cls : "roo-radio-set-right",
33749 if(this.labelWidth > 12){
33750 label.style = "width: " + this.labelWidth + 'px';
33753 if(this.labelWidth < 13 && this.labelmd == 0){
33754 this.labelmd = this.labelWidth;
33757 if(this.labellg > 0){
33758 label.cls += ' col-lg-' + this.labellg;
33759 items.cls += ' col-lg-' + (12 - this.labellg);
33762 if(this.labelmd > 0){
33763 label.cls += ' col-md-' + this.labelmd;
33764 items.cls += ' col-md-' + (12 - this.labelmd);
33767 if(this.labelsm > 0){
33768 label.cls += ' col-sm-' + this.labelsm;
33769 items.cls += ' col-sm-' + (12 - this.labelsm);
33772 if(this.labelxs > 0){
33773 label.cls += ' col-xs-' + this.labelxs;
33774 items.cls += ' col-xs-' + (12 - this.labelxs);
33780 cls : 'roo-radio-set',
33784 cls : 'roo-radio-set-input',
33787 value : this.value ? this.value : ''
33794 if(this.weight.length){
33795 cfg.cls += ' roo-radio-' + this.weight;
33799 cfg.cls += ' roo-radio-set-inline';
33803 ['xs','sm','md','lg'].map(function(size){
33804 if (settings[size]) {
33805 cfg.cls += ' col-' + size + '-' + settings[size];
33813 initEvents : function()
33815 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33816 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33818 if(!this.fieldLabel.length){
33819 this.labelEl.hide();
33822 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33823 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33825 this.indicator = this.indicatorEl();
33827 if(this.indicator){
33828 this.indicator.addClass('invisible');
33831 this.originalValue = this.getValue();
33835 inputEl: function ()
33837 return this.el.select('.roo-radio-set-input', true).first();
33840 getChildContainer : function()
33842 return this.itemsEl;
33845 register : function(item)
33847 this.radioes.push(item);
33851 validate : function()
33853 if(this.getVisibilityEl().hasClass('hidden')){
33859 Roo.each(this.radioes, function(i){
33868 if(this.allowBlank) {
33872 if(this.disabled || valid){
33877 this.markInvalid();
33882 markValid : function()
33884 if(this.labelEl.isVisible(true)){
33885 this.indicatorEl().removeClass('visible');
33886 this.indicatorEl().addClass('invisible');
33889 this.el.removeClass([this.invalidClass, this.validClass]);
33890 this.el.addClass(this.validClass);
33892 this.fireEvent('valid', this);
33895 markInvalid : function(msg)
33897 if(this.allowBlank || this.disabled){
33901 if(this.labelEl.isVisible(true)){
33902 this.indicatorEl().removeClass('invisible');
33903 this.indicatorEl().addClass('visible');
33906 this.el.removeClass([this.invalidClass, this.validClass]);
33907 this.el.addClass(this.invalidClass);
33909 this.fireEvent('invalid', this, msg);
33913 setValue : function(v, suppressEvent)
33915 if(this.value === v){
33922 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33925 Roo.each(this.radioes, function(i){
33927 i.el.removeClass('checked');
33930 Roo.each(this.radioes, function(i){
33932 if(i.value === v || i.value.toString() === v.toString()){
33934 i.el.addClass('checked');
33936 if(suppressEvent !== true){
33937 this.fireEvent('check', this, i);
33948 clearInvalid : function(){
33950 if(!this.el || this.preventMark){
33954 this.el.removeClass([this.invalidClass]);
33956 this.fireEvent('valid', this);
33961 Roo.apply(Roo.bootstrap.RadioSet, {
33965 register : function(set)
33967 this.groups[set.name] = set;
33970 get: function(name)
33972 if (typeof(this.groups[name]) == 'undefined') {
33976 return this.groups[name] ;
33982 * Ext JS Library 1.1.1
33983 * Copyright(c) 2006-2007, Ext JS, LLC.
33985 * Originally Released Under LGPL - original licence link has changed is not relivant.
33988 * <script type="text/javascript">
33993 * @class Roo.bootstrap.SplitBar
33994 * @extends Roo.util.Observable
33995 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33999 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34000 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34001 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34002 split.minSize = 100;
34003 split.maxSize = 600;
34004 split.animate = true;
34005 split.on('moved', splitterMoved);
34008 * Create a new SplitBar
34009 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34010 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34011 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34012 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34013 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34014 position of the SplitBar).
34016 Roo.bootstrap.SplitBar = function(cfg){
34021 // dragElement : elm
34022 // resizingElement: el,
34024 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34025 // placement : Roo.bootstrap.SplitBar.LEFT ,
34026 // existingProxy ???
34029 this.el = Roo.get(cfg.dragElement, true);
34030 this.el.dom.unselectable = "on";
34032 this.resizingEl = Roo.get(cfg.resizingElement, true);
34036 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34037 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34040 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34043 * The minimum size of the resizing element. (Defaults to 0)
34049 * The maximum size of the resizing element. (Defaults to 2000)
34052 this.maxSize = 2000;
34055 * Whether to animate the transition to the new size
34058 this.animate = false;
34061 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34064 this.useShim = false;
34069 if(!cfg.existingProxy){
34071 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34073 this.proxy = Roo.get(cfg.existingProxy).dom;
34076 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34079 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34082 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34085 this.dragSpecs = {};
34088 * @private The adapter to use to positon and resize elements
34090 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34091 this.adapter.init(this);
34093 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34095 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34096 this.el.addClass("roo-splitbar-h");
34099 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34100 this.el.addClass("roo-splitbar-v");
34106 * Fires when the splitter is moved (alias for {@link #event-moved})
34107 * @param {Roo.bootstrap.SplitBar} this
34108 * @param {Number} newSize the new width or height
34113 * Fires when the splitter is moved
34114 * @param {Roo.bootstrap.SplitBar} this
34115 * @param {Number} newSize the new width or height
34119 * @event beforeresize
34120 * Fires before the splitter is dragged
34121 * @param {Roo.bootstrap.SplitBar} this
34123 "beforeresize" : true,
34125 "beforeapply" : true
34128 Roo.util.Observable.call(this);
34131 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34132 onStartProxyDrag : function(x, y){
34133 this.fireEvent("beforeresize", this);
34135 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34137 o.enableDisplayMode("block");
34138 // all splitbars share the same overlay
34139 Roo.bootstrap.SplitBar.prototype.overlay = o;
34141 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34142 this.overlay.show();
34143 Roo.get(this.proxy).setDisplayed("block");
34144 var size = this.adapter.getElementSize(this);
34145 this.activeMinSize = this.getMinimumSize();;
34146 this.activeMaxSize = this.getMaximumSize();;
34147 var c1 = size - this.activeMinSize;
34148 var c2 = Math.max(this.activeMaxSize - size, 0);
34149 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34150 this.dd.resetConstraints();
34151 this.dd.setXConstraint(
34152 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34153 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34155 this.dd.setYConstraint(0, 0);
34157 this.dd.resetConstraints();
34158 this.dd.setXConstraint(0, 0);
34159 this.dd.setYConstraint(
34160 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34161 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34164 this.dragSpecs.startSize = size;
34165 this.dragSpecs.startPoint = [x, y];
34166 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34170 * @private Called after the drag operation by the DDProxy
34172 onEndProxyDrag : function(e){
34173 Roo.get(this.proxy).setDisplayed(false);
34174 var endPoint = Roo.lib.Event.getXY(e);
34176 this.overlay.hide();
34179 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34180 newSize = this.dragSpecs.startSize +
34181 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34182 endPoint[0] - this.dragSpecs.startPoint[0] :
34183 this.dragSpecs.startPoint[0] - endPoint[0]
34186 newSize = this.dragSpecs.startSize +
34187 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34188 endPoint[1] - this.dragSpecs.startPoint[1] :
34189 this.dragSpecs.startPoint[1] - endPoint[1]
34192 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34193 if(newSize != this.dragSpecs.startSize){
34194 if(this.fireEvent('beforeapply', this, newSize) !== false){
34195 this.adapter.setElementSize(this, newSize);
34196 this.fireEvent("moved", this, newSize);
34197 this.fireEvent("resize", this, newSize);
34203 * Get the adapter this SplitBar uses
34204 * @return The adapter object
34206 getAdapter : function(){
34207 return this.adapter;
34211 * Set the adapter this SplitBar uses
34212 * @param {Object} adapter A SplitBar adapter object
34214 setAdapter : function(adapter){
34215 this.adapter = adapter;
34216 this.adapter.init(this);
34220 * Gets the minimum size for the resizing element
34221 * @return {Number} The minimum size
34223 getMinimumSize : function(){
34224 return this.minSize;
34228 * Sets the minimum size for the resizing element
34229 * @param {Number} minSize The minimum size
34231 setMinimumSize : function(minSize){
34232 this.minSize = minSize;
34236 * Gets the maximum size for the resizing element
34237 * @return {Number} The maximum size
34239 getMaximumSize : function(){
34240 return this.maxSize;
34244 * Sets the maximum size for the resizing element
34245 * @param {Number} maxSize The maximum size
34247 setMaximumSize : function(maxSize){
34248 this.maxSize = maxSize;
34252 * Sets the initialize size for the resizing element
34253 * @param {Number} size The initial size
34255 setCurrentSize : function(size){
34256 var oldAnimate = this.animate;
34257 this.animate = false;
34258 this.adapter.setElementSize(this, size);
34259 this.animate = oldAnimate;
34263 * Destroy this splitbar.
34264 * @param {Boolean} removeEl True to remove the element
34266 destroy : function(removeEl){
34268 this.shim.remove();
34271 this.proxy.parentNode.removeChild(this.proxy);
34279 * @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.
34281 Roo.bootstrap.SplitBar.createProxy = function(dir){
34282 var proxy = new Roo.Element(document.createElement("div"));
34283 proxy.unselectable();
34284 var cls = 'roo-splitbar-proxy';
34285 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34286 document.body.appendChild(proxy.dom);
34291 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34292 * Default Adapter. It assumes the splitter and resizing element are not positioned
34293 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34295 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34298 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34299 // do nothing for now
34300 init : function(s){
34304 * Called before drag operations to get the current size of the resizing element.
34305 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34307 getElementSize : function(s){
34308 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34309 return s.resizingEl.getWidth();
34311 return s.resizingEl.getHeight();
34316 * Called after drag operations to set the size of the resizing element.
34317 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34318 * @param {Number} newSize The new size to set
34319 * @param {Function} onComplete A function to be invoked when resizing is complete
34321 setElementSize : function(s, newSize, onComplete){
34322 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34324 s.resizingEl.setWidth(newSize);
34326 onComplete(s, newSize);
34329 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34334 s.resizingEl.setHeight(newSize);
34336 onComplete(s, newSize);
34339 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34346 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34347 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34348 * Adapter that moves the splitter element to align with the resized sizing element.
34349 * Used with an absolute positioned SplitBar.
34350 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34351 * document.body, make sure you assign an id to the body element.
34353 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34354 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34355 this.container = Roo.get(container);
34358 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34359 init : function(s){
34360 this.basic.init(s);
34363 getElementSize : function(s){
34364 return this.basic.getElementSize(s);
34367 setElementSize : function(s, newSize, onComplete){
34368 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34371 moveSplitter : function(s){
34372 var yes = Roo.bootstrap.SplitBar;
34373 switch(s.placement){
34375 s.el.setX(s.resizingEl.getRight());
34378 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34381 s.el.setY(s.resizingEl.getBottom());
34384 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34391 * Orientation constant - Create a vertical SplitBar
34395 Roo.bootstrap.SplitBar.VERTICAL = 1;
34398 * Orientation constant - Create a horizontal SplitBar
34402 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34405 * Placement constant - The resizing element is to the left of the splitter element
34409 Roo.bootstrap.SplitBar.LEFT = 1;
34412 * Placement constant - The resizing element is to the right of the splitter element
34416 Roo.bootstrap.SplitBar.RIGHT = 2;
34419 * Placement constant - The resizing element is positioned above the splitter element
34423 Roo.bootstrap.SplitBar.TOP = 3;
34426 * Placement constant - The resizing element is positioned under splitter element
34430 Roo.bootstrap.SplitBar.BOTTOM = 4;
34431 Roo.namespace("Roo.bootstrap.layout");/*
34433 * Ext JS Library 1.1.1
34434 * Copyright(c) 2006-2007, Ext JS, LLC.
34436 * Originally Released Under LGPL - original licence link has changed is not relivant.
34439 * <script type="text/javascript">
34443 * @class Roo.bootstrap.layout.Manager
34444 * @extends Roo.bootstrap.Component
34445 * Base class for layout managers.
34447 Roo.bootstrap.layout.Manager = function(config)
34449 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34455 /** false to disable window resize monitoring @type Boolean */
34456 this.monitorWindowResize = true;
34461 * Fires when a layout is performed.
34462 * @param {Roo.LayoutManager} this
34466 * @event regionresized
34467 * Fires when the user resizes a region.
34468 * @param {Roo.LayoutRegion} region The resized region
34469 * @param {Number} newSize The new size (width for east/west, height for north/south)
34471 "regionresized" : true,
34473 * @event regioncollapsed
34474 * Fires when a region is collapsed.
34475 * @param {Roo.LayoutRegion} region The collapsed region
34477 "regioncollapsed" : true,
34479 * @event regionexpanded
34480 * Fires when a region is expanded.
34481 * @param {Roo.LayoutRegion} region The expanded region
34483 "regionexpanded" : true
34485 this.updating = false;
34488 this.el = Roo.get(config.el);
34494 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34499 monitorWindowResize : true,
34505 onRender : function(ct, position)
34508 this.el = Roo.get(ct);
34511 //this.fireEvent('render',this);
34515 initEvents: function()
34519 // ie scrollbar fix
34520 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34521 document.body.scroll = "no";
34522 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34523 this.el.position('relative');
34525 this.id = this.el.id;
34526 this.el.addClass("roo-layout-container");
34527 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34528 if(this.el.dom != document.body ) {
34529 this.el.on('resize', this.layout,this);
34530 this.el.on('show', this.layout,this);
34536 * Returns true if this layout is currently being updated
34537 * @return {Boolean}
34539 isUpdating : function(){
34540 return this.updating;
34544 * Suspend the LayoutManager from doing auto-layouts while
34545 * making multiple add or remove calls
34547 beginUpdate : function(){
34548 this.updating = true;
34552 * Restore auto-layouts and optionally disable the manager from performing a layout
34553 * @param {Boolean} noLayout true to disable a layout update
34555 endUpdate : function(noLayout){
34556 this.updating = false;
34562 layout: function(){
34566 onRegionResized : function(region, newSize){
34567 this.fireEvent("regionresized", region, newSize);
34571 onRegionCollapsed : function(region){
34572 this.fireEvent("regioncollapsed", region);
34575 onRegionExpanded : function(region){
34576 this.fireEvent("regionexpanded", region);
34580 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34581 * performs box-model adjustments.
34582 * @return {Object} The size as an object {width: (the width), height: (the height)}
34584 getViewSize : function()
34587 if(this.el.dom != document.body){
34588 size = this.el.getSize();
34590 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34592 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34593 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34598 * Returns the Element this layout is bound to.
34599 * @return {Roo.Element}
34601 getEl : function(){
34606 * Returns the specified region.
34607 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34608 * @return {Roo.LayoutRegion}
34610 getRegion : function(target){
34611 return this.regions[target.toLowerCase()];
34614 onWindowResize : function(){
34615 if(this.monitorWindowResize){
34622 * Ext JS Library 1.1.1
34623 * Copyright(c) 2006-2007, Ext JS, LLC.
34625 * Originally Released Under LGPL - original licence link has changed is not relivant.
34628 * <script type="text/javascript">
34631 * @class Roo.bootstrap.layout.Border
34632 * @extends Roo.bootstrap.layout.Manager
34633 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34634 * please see: examples/bootstrap/nested.html<br><br>
34636 <b>The container the layout is rendered into can be either the body element or any other element.
34637 If it is not the body element, the container needs to either be an absolute positioned element,
34638 or you will need to add "position:relative" to the css of the container. You will also need to specify
34639 the container size if it is not the body element.</b>
34642 * Create a new Border
34643 * @param {Object} config Configuration options
34645 Roo.bootstrap.layout.Border = function(config){
34646 config = config || {};
34647 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34651 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34652 if(config[region]){
34653 config[region].region = region;
34654 this.addRegion(config[region]);
34660 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34662 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34664 * Creates and adds a new region if it doesn't already exist.
34665 * @param {String} target The target region key (north, south, east, west or center).
34666 * @param {Object} config The regions config object
34667 * @return {BorderLayoutRegion} The new region
34669 addRegion : function(config)
34671 if(!this.regions[config.region]){
34672 var r = this.factory(config);
34673 this.bindRegion(r);
34675 return this.regions[config.region];
34679 bindRegion : function(r){
34680 this.regions[r.config.region] = r;
34682 r.on("visibilitychange", this.layout, this);
34683 r.on("paneladded", this.layout, this);
34684 r.on("panelremoved", this.layout, this);
34685 r.on("invalidated", this.layout, this);
34686 r.on("resized", this.onRegionResized, this);
34687 r.on("collapsed", this.onRegionCollapsed, this);
34688 r.on("expanded", this.onRegionExpanded, this);
34692 * Performs a layout update.
34694 layout : function()
34696 if(this.updating) {
34700 // render all the rebions if they have not been done alreayd?
34701 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34702 if(this.regions[region] && !this.regions[region].bodyEl){
34703 this.regions[region].onRender(this.el)
34707 var size = this.getViewSize();
34708 var w = size.width;
34709 var h = size.height;
34714 //var x = 0, y = 0;
34716 var rs = this.regions;
34717 var north = rs["north"];
34718 var south = rs["south"];
34719 var west = rs["west"];
34720 var east = rs["east"];
34721 var center = rs["center"];
34722 //if(this.hideOnLayout){ // not supported anymore
34723 //c.el.setStyle("display", "none");
34725 if(north && north.isVisible()){
34726 var b = north.getBox();
34727 var m = north.getMargins();
34728 b.width = w - (m.left+m.right);
34731 centerY = b.height + b.y + m.bottom;
34732 centerH -= centerY;
34733 north.updateBox(this.safeBox(b));
34735 if(south && south.isVisible()){
34736 var b = south.getBox();
34737 var m = south.getMargins();
34738 b.width = w - (m.left+m.right);
34740 var totalHeight = (b.height + m.top + m.bottom);
34741 b.y = h - totalHeight + m.top;
34742 centerH -= totalHeight;
34743 south.updateBox(this.safeBox(b));
34745 if(west && west.isVisible()){
34746 var b = west.getBox();
34747 var m = west.getMargins();
34748 b.height = centerH - (m.top+m.bottom);
34750 b.y = centerY + m.top;
34751 var totalWidth = (b.width + m.left + m.right);
34752 centerX += totalWidth;
34753 centerW -= totalWidth;
34754 west.updateBox(this.safeBox(b));
34756 if(east && east.isVisible()){
34757 var b = east.getBox();
34758 var m = east.getMargins();
34759 b.height = centerH - (m.top+m.bottom);
34760 var totalWidth = (b.width + m.left + m.right);
34761 b.x = w - totalWidth + m.left;
34762 b.y = centerY + m.top;
34763 centerW -= totalWidth;
34764 east.updateBox(this.safeBox(b));
34767 var m = center.getMargins();
34769 x: centerX + m.left,
34770 y: centerY + m.top,
34771 width: centerW - (m.left+m.right),
34772 height: centerH - (m.top+m.bottom)
34774 //if(this.hideOnLayout){
34775 //center.el.setStyle("display", "block");
34777 center.updateBox(this.safeBox(centerBox));
34780 this.fireEvent("layout", this);
34784 safeBox : function(box){
34785 box.width = Math.max(0, box.width);
34786 box.height = Math.max(0, box.height);
34791 * Adds a ContentPanel (or subclass) to this layout.
34792 * @param {String} target The target region key (north, south, east, west or center).
34793 * @param {Roo.ContentPanel} panel The panel to add
34794 * @return {Roo.ContentPanel} The added panel
34796 add : function(target, panel){
34798 target = target.toLowerCase();
34799 return this.regions[target].add(panel);
34803 * Remove a ContentPanel (or subclass) to this layout.
34804 * @param {String} target The target region key (north, south, east, west or center).
34805 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34806 * @return {Roo.ContentPanel} The removed panel
34808 remove : function(target, panel){
34809 target = target.toLowerCase();
34810 return this.regions[target].remove(panel);
34814 * Searches all regions for a panel with the specified id
34815 * @param {String} panelId
34816 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34818 findPanel : function(panelId){
34819 var rs = this.regions;
34820 for(var target in rs){
34821 if(typeof rs[target] != "function"){
34822 var p = rs[target].getPanel(panelId);
34832 * Searches all regions for a panel with the specified id and activates (shows) it.
34833 * @param {String/ContentPanel} panelId The panels id or the panel itself
34834 * @return {Roo.ContentPanel} The shown panel or null
34836 showPanel : function(panelId) {
34837 var rs = this.regions;
34838 for(var target in rs){
34839 var r = rs[target];
34840 if(typeof r != "function"){
34841 if(r.hasPanel(panelId)){
34842 return r.showPanel(panelId);
34850 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34851 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34854 restoreState : function(provider){
34856 provider = Roo.state.Manager;
34858 var sm = new Roo.LayoutStateManager();
34859 sm.init(this, provider);
34865 * Adds a xtype elements to the layout.
34869 xtype : 'ContentPanel',
34876 xtype : 'NestedLayoutPanel',
34882 items : [ ... list of content panels or nested layout panels.. ]
34886 * @param {Object} cfg Xtype definition of item to add.
34888 addxtype : function(cfg)
34890 // basically accepts a pannel...
34891 // can accept a layout region..!?!?
34892 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34895 // theory? children can only be panels??
34897 //if (!cfg.xtype.match(/Panel$/)) {
34902 if (typeof(cfg.region) == 'undefined') {
34903 Roo.log("Failed to add Panel, region was not set");
34907 var region = cfg.region;
34913 xitems = cfg.items;
34920 case 'Content': // ContentPanel (el, cfg)
34921 case 'Scroll': // ContentPanel (el, cfg)
34923 cfg.autoCreate = true;
34924 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34926 // var el = this.el.createChild();
34927 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34930 this.add(region, ret);
34934 case 'TreePanel': // our new panel!
34935 cfg.el = this.el.createChild();
34936 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34937 this.add(region, ret);
34942 // create a new Layout (which is a Border Layout...
34944 var clayout = cfg.layout;
34945 clayout.el = this.el.createChild();
34946 clayout.items = clayout.items || [];
34950 // replace this exitems with the clayout ones..
34951 xitems = clayout.items;
34953 // force background off if it's in center...
34954 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34955 cfg.background = false;
34957 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34960 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34961 //console.log('adding nested layout panel ' + cfg.toSource());
34962 this.add(region, ret);
34963 nb = {}; /// find first...
34968 // needs grid and region
34970 //var el = this.getRegion(region).el.createChild();
34972 *var el = this.el.createChild();
34973 // create the grid first...
34974 cfg.grid.container = el;
34975 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34978 if (region == 'center' && this.active ) {
34979 cfg.background = false;
34982 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34984 this.add(region, ret);
34986 if (cfg.background) {
34987 // render grid on panel activation (if panel background)
34988 ret.on('activate', function(gp) {
34989 if (!gp.grid.rendered) {
34990 // gp.grid.render(el);
34994 // cfg.grid.render(el);
35000 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35001 // it was the old xcomponent building that caused this before.
35002 // espeically if border is the top element in the tree.
35012 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35014 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35015 this.add(region, ret);
35019 throw "Can not add '" + cfg.xtype + "' to Border";
35025 this.beginUpdate();
35029 Roo.each(xitems, function(i) {
35030 region = nb && i.region ? i.region : false;
35032 var add = ret.addxtype(i);
35035 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35036 if (!i.background) {
35037 abn[region] = nb[region] ;
35044 // make the last non-background panel active..
35045 //if (nb) { Roo.log(abn); }
35048 for(var r in abn) {
35049 region = this.getRegion(r);
35051 // tried using nb[r], but it does not work..
35053 region.showPanel(abn[r]);
35064 factory : function(cfg)
35067 var validRegions = Roo.bootstrap.layout.Border.regions;
35069 var target = cfg.region;
35072 var r = Roo.bootstrap.layout;
35076 return new r.North(cfg);
35078 return new r.South(cfg);
35080 return new r.East(cfg);
35082 return new r.West(cfg);
35084 return new r.Center(cfg);
35086 throw 'Layout region "'+target+'" not supported.';
35093 * Ext JS Library 1.1.1
35094 * Copyright(c) 2006-2007, Ext JS, LLC.
35096 * Originally Released Under LGPL - original licence link has changed is not relivant.
35099 * <script type="text/javascript">
35103 * @class Roo.bootstrap.layout.Basic
35104 * @extends Roo.util.Observable
35105 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35106 * and does not have a titlebar, tabs or any other features. All it does is size and position
35107 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35108 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35109 * @cfg {string} region the region that it inhabits..
35110 * @cfg {bool} skipConfig skip config?
35114 Roo.bootstrap.layout.Basic = function(config){
35116 this.mgr = config.mgr;
35118 this.position = config.region;
35120 var skipConfig = config.skipConfig;
35124 * @scope Roo.BasicLayoutRegion
35128 * @event beforeremove
35129 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35130 * @param {Roo.LayoutRegion} this
35131 * @param {Roo.ContentPanel} panel The panel
35132 * @param {Object} e The cancel event object
35134 "beforeremove" : true,
35136 * @event invalidated
35137 * Fires when the layout for this region is changed.
35138 * @param {Roo.LayoutRegion} this
35140 "invalidated" : true,
35142 * @event visibilitychange
35143 * Fires when this region is shown or hidden
35144 * @param {Roo.LayoutRegion} this
35145 * @param {Boolean} visibility true or false
35147 "visibilitychange" : true,
35149 * @event paneladded
35150 * Fires when a panel is added.
35151 * @param {Roo.LayoutRegion} this
35152 * @param {Roo.ContentPanel} panel The panel
35154 "paneladded" : true,
35156 * @event panelremoved
35157 * Fires when a panel is removed.
35158 * @param {Roo.LayoutRegion} this
35159 * @param {Roo.ContentPanel} panel The panel
35161 "panelremoved" : true,
35163 * @event beforecollapse
35164 * Fires when this region before collapse.
35165 * @param {Roo.LayoutRegion} this
35167 "beforecollapse" : true,
35170 * Fires when this region is collapsed.
35171 * @param {Roo.LayoutRegion} this
35173 "collapsed" : true,
35176 * Fires when this region is expanded.
35177 * @param {Roo.LayoutRegion} this
35182 * Fires when this region is slid into view.
35183 * @param {Roo.LayoutRegion} this
35185 "slideshow" : true,
35188 * Fires when this region slides out of view.
35189 * @param {Roo.LayoutRegion} this
35191 "slidehide" : true,
35193 * @event panelactivated
35194 * Fires when a panel is activated.
35195 * @param {Roo.LayoutRegion} this
35196 * @param {Roo.ContentPanel} panel The activated panel
35198 "panelactivated" : true,
35201 * Fires when the user resizes this region.
35202 * @param {Roo.LayoutRegion} this
35203 * @param {Number} newSize The new size (width for east/west, height for north/south)
35207 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35208 this.panels = new Roo.util.MixedCollection();
35209 this.panels.getKey = this.getPanelId.createDelegate(this);
35211 this.activePanel = null;
35212 // ensure listeners are added...
35214 if (config.listeners || config.events) {
35215 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35216 listeners : config.listeners || {},
35217 events : config.events || {}
35221 if(skipConfig !== true){
35222 this.applyConfig(config);
35226 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35228 getPanelId : function(p){
35232 applyConfig : function(config){
35233 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35234 this.config = config;
35239 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35240 * the width, for horizontal (north, south) the height.
35241 * @param {Number} newSize The new width or height
35243 resizeTo : function(newSize){
35244 var el = this.el ? this.el :
35245 (this.activePanel ? this.activePanel.getEl() : null);
35247 switch(this.position){
35250 el.setWidth(newSize);
35251 this.fireEvent("resized", this, newSize);
35255 el.setHeight(newSize);
35256 this.fireEvent("resized", this, newSize);
35262 getBox : function(){
35263 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35266 getMargins : function(){
35267 return this.margins;
35270 updateBox : function(box){
35272 var el = this.activePanel.getEl();
35273 el.dom.style.left = box.x + "px";
35274 el.dom.style.top = box.y + "px";
35275 this.activePanel.setSize(box.width, box.height);
35279 * Returns the container element for this region.
35280 * @return {Roo.Element}
35282 getEl : function(){
35283 return this.activePanel;
35287 * Returns true if this region is currently visible.
35288 * @return {Boolean}
35290 isVisible : function(){
35291 return this.activePanel ? true : false;
35294 setActivePanel : function(panel){
35295 panel = this.getPanel(panel);
35296 if(this.activePanel && this.activePanel != panel){
35297 this.activePanel.setActiveState(false);
35298 this.activePanel.getEl().setLeftTop(-10000,-10000);
35300 this.activePanel = panel;
35301 panel.setActiveState(true);
35303 panel.setSize(this.box.width, this.box.height);
35305 this.fireEvent("panelactivated", this, panel);
35306 this.fireEvent("invalidated");
35310 * Show the specified panel.
35311 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35312 * @return {Roo.ContentPanel} The shown panel or null
35314 showPanel : function(panel){
35315 panel = this.getPanel(panel);
35317 this.setActivePanel(panel);
35323 * Get the active panel for this region.
35324 * @return {Roo.ContentPanel} The active panel or null
35326 getActivePanel : function(){
35327 return this.activePanel;
35331 * Add the passed ContentPanel(s)
35332 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35333 * @return {Roo.ContentPanel} The panel added (if only one was added)
35335 add : function(panel){
35336 if(arguments.length > 1){
35337 for(var i = 0, len = arguments.length; i < len; i++) {
35338 this.add(arguments[i]);
35342 if(this.hasPanel(panel)){
35343 this.showPanel(panel);
35346 var el = panel.getEl();
35347 if(el.dom.parentNode != this.mgr.el.dom){
35348 this.mgr.el.dom.appendChild(el.dom);
35350 if(panel.setRegion){
35351 panel.setRegion(this);
35353 this.panels.add(panel);
35354 el.setStyle("position", "absolute");
35355 if(!panel.background){
35356 this.setActivePanel(panel);
35357 if(this.config.initialSize && this.panels.getCount()==1){
35358 this.resizeTo(this.config.initialSize);
35361 this.fireEvent("paneladded", this, panel);
35366 * Returns true if the panel is in this region.
35367 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35368 * @return {Boolean}
35370 hasPanel : function(panel){
35371 if(typeof panel == "object"){ // must be panel obj
35372 panel = panel.getId();
35374 return this.getPanel(panel) ? true : false;
35378 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35379 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35380 * @param {Boolean} preservePanel Overrides the config preservePanel option
35381 * @return {Roo.ContentPanel} The panel that was removed
35383 remove : function(panel, preservePanel){
35384 panel = this.getPanel(panel);
35389 this.fireEvent("beforeremove", this, panel, e);
35390 if(e.cancel === true){
35393 var panelId = panel.getId();
35394 this.panels.removeKey(panelId);
35399 * Returns the panel specified or null if it's not in this region.
35400 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35401 * @return {Roo.ContentPanel}
35403 getPanel : function(id){
35404 if(typeof id == "object"){ // must be panel obj
35407 return this.panels.get(id);
35411 * Returns this regions position (north/south/east/west/center).
35414 getPosition: function(){
35415 return this.position;
35419 * Ext JS Library 1.1.1
35420 * Copyright(c) 2006-2007, Ext JS, LLC.
35422 * Originally Released Under LGPL - original licence link has changed is not relivant.
35425 * <script type="text/javascript">
35429 * @class Roo.bootstrap.layout.Region
35430 * @extends Roo.bootstrap.layout.Basic
35431 * This class represents a region in a layout manager.
35433 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35434 * @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})
35435 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35436 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35437 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35438 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35439 * @cfg {String} title The title for the region (overrides panel titles)
35440 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35441 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35442 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35443 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35444 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35445 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35446 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35447 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35448 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35449 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35451 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35452 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35453 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35454 * @cfg {Number} width For East/West panels
35455 * @cfg {Number} height For North/South panels
35456 * @cfg {Boolean} split To show the splitter
35457 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35459 * @cfg {string} cls Extra CSS classes to add to region
35461 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35462 * @cfg {string} region the region that it inhabits..
35465 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35466 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35468 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35469 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35470 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35472 Roo.bootstrap.layout.Region = function(config)
35474 this.applyConfig(config);
35476 var mgr = config.mgr;
35477 var pos = config.region;
35478 config.skipConfig = true;
35479 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35482 this.onRender(mgr.el);
35485 this.visible = true;
35486 this.collapsed = false;
35487 this.unrendered_panels = [];
35490 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35492 position: '', // set by wrapper (eg. north/south etc..)
35493 unrendered_panels : null, // unrendered panels.
35494 createBody : function(){
35495 /** This region's body element
35496 * @type Roo.Element */
35497 this.bodyEl = this.el.createChild({
35499 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35503 onRender: function(ctr, pos)
35505 var dh = Roo.DomHelper;
35506 /** This region's container element
35507 * @type Roo.Element */
35508 this.el = dh.append(ctr.dom, {
35510 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35512 /** This region's title element
35513 * @type Roo.Element */
35515 this.titleEl = dh.append(this.el.dom,
35518 unselectable: "on",
35519 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35521 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35522 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35525 this.titleEl.enableDisplayMode();
35526 /** This region's title text element
35527 * @type HTMLElement */
35528 this.titleTextEl = this.titleEl.dom.firstChild;
35529 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35531 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35532 this.closeBtn.enableDisplayMode();
35533 this.closeBtn.on("click", this.closeClicked, this);
35534 this.closeBtn.hide();
35536 this.createBody(this.config);
35537 if(this.config.hideWhenEmpty){
35539 this.on("paneladded", this.validateVisibility, this);
35540 this.on("panelremoved", this.validateVisibility, this);
35542 if(this.autoScroll){
35543 this.bodyEl.setStyle("overflow", "auto");
35545 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35547 //if(c.titlebar !== false){
35548 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35549 this.titleEl.hide();
35551 this.titleEl.show();
35552 if(this.config.title){
35553 this.titleTextEl.innerHTML = this.config.title;
35557 if(this.config.collapsed){
35558 this.collapse(true);
35560 if(this.config.hidden){
35564 if (this.unrendered_panels && this.unrendered_panels.length) {
35565 for (var i =0;i< this.unrendered_panels.length; i++) {
35566 this.add(this.unrendered_panels[i]);
35568 this.unrendered_panels = null;
35574 applyConfig : function(c)
35577 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35578 var dh = Roo.DomHelper;
35579 if(c.titlebar !== false){
35580 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35581 this.collapseBtn.on("click", this.collapse, this);
35582 this.collapseBtn.enableDisplayMode();
35584 if(c.showPin === true || this.showPin){
35585 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35586 this.stickBtn.enableDisplayMode();
35587 this.stickBtn.on("click", this.expand, this);
35588 this.stickBtn.hide();
35593 /** This region's collapsed element
35594 * @type Roo.Element */
35597 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35598 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35601 if(c.floatable !== false){
35602 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35603 this.collapsedEl.on("click", this.collapseClick, this);
35606 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35607 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35608 id: "message", unselectable: "on", style:{"float":"left"}});
35609 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35611 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35612 this.expandBtn.on("click", this.expand, this);
35616 if(this.collapseBtn){
35617 this.collapseBtn.setVisible(c.collapsible == true);
35620 this.cmargins = c.cmargins || this.cmargins ||
35621 (this.position == "west" || this.position == "east" ?
35622 {top: 0, left: 2, right:2, bottom: 0} :
35623 {top: 2, left: 0, right:0, bottom: 2});
35625 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35628 this.bottomTabs = c.tabPosition != "top";
35630 this.autoScroll = c.autoScroll || false;
35635 this.duration = c.duration || .30;
35636 this.slideDuration = c.slideDuration || .45;
35641 * Returns true if this region is currently visible.
35642 * @return {Boolean}
35644 isVisible : function(){
35645 return this.visible;
35649 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35650 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35652 //setCollapsedTitle : function(title){
35653 // title = title || " ";
35654 // if(this.collapsedTitleTextEl){
35655 // this.collapsedTitleTextEl.innerHTML = title;
35659 getBox : function(){
35661 // if(!this.collapsed){
35662 b = this.el.getBox(false, true);
35664 // b = this.collapsedEl.getBox(false, true);
35669 getMargins : function(){
35670 return this.margins;
35671 //return this.collapsed ? this.cmargins : this.margins;
35674 highlight : function(){
35675 this.el.addClass("x-layout-panel-dragover");
35678 unhighlight : function(){
35679 this.el.removeClass("x-layout-panel-dragover");
35682 updateBox : function(box)
35684 if (!this.bodyEl) {
35685 return; // not rendered yet..
35689 if(!this.collapsed){
35690 this.el.dom.style.left = box.x + "px";
35691 this.el.dom.style.top = box.y + "px";
35692 this.updateBody(box.width, box.height);
35694 this.collapsedEl.dom.style.left = box.x + "px";
35695 this.collapsedEl.dom.style.top = box.y + "px";
35696 this.collapsedEl.setSize(box.width, box.height);
35699 this.tabs.autoSizeTabs();
35703 updateBody : function(w, h)
35706 this.el.setWidth(w);
35707 w -= this.el.getBorderWidth("rl");
35708 if(this.config.adjustments){
35709 w += this.config.adjustments[0];
35712 if(h !== null && h > 0){
35713 this.el.setHeight(h);
35714 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35715 h -= this.el.getBorderWidth("tb");
35716 if(this.config.adjustments){
35717 h += this.config.adjustments[1];
35719 this.bodyEl.setHeight(h);
35721 h = this.tabs.syncHeight(h);
35724 if(this.panelSize){
35725 w = w !== null ? w : this.panelSize.width;
35726 h = h !== null ? h : this.panelSize.height;
35728 if(this.activePanel){
35729 var el = this.activePanel.getEl();
35730 w = w !== null ? w : el.getWidth();
35731 h = h !== null ? h : el.getHeight();
35732 this.panelSize = {width: w, height: h};
35733 this.activePanel.setSize(w, h);
35735 if(Roo.isIE && this.tabs){
35736 this.tabs.el.repaint();
35741 * Returns the container element for this region.
35742 * @return {Roo.Element}
35744 getEl : function(){
35749 * Hides this region.
35752 //if(!this.collapsed){
35753 this.el.dom.style.left = "-2000px";
35756 // this.collapsedEl.dom.style.left = "-2000px";
35757 // this.collapsedEl.hide();
35759 this.visible = false;
35760 this.fireEvent("visibilitychange", this, false);
35764 * Shows this region if it was previously hidden.
35767 //if(!this.collapsed){
35770 // this.collapsedEl.show();
35772 this.visible = true;
35773 this.fireEvent("visibilitychange", this, true);
35776 closeClicked : function(){
35777 if(this.activePanel){
35778 this.remove(this.activePanel);
35782 collapseClick : function(e){
35784 e.stopPropagation();
35787 e.stopPropagation();
35793 * Collapses this region.
35794 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35797 collapse : function(skipAnim, skipCheck = false){
35798 if(this.collapsed) {
35802 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35804 this.collapsed = true;
35806 this.split.el.hide();
35808 if(this.config.animate && skipAnim !== true){
35809 this.fireEvent("invalidated", this);
35810 this.animateCollapse();
35812 this.el.setLocation(-20000,-20000);
35814 this.collapsedEl.show();
35815 this.fireEvent("collapsed", this);
35816 this.fireEvent("invalidated", this);
35822 animateCollapse : function(){
35827 * Expands this region if it was previously collapsed.
35828 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35829 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35832 expand : function(e, skipAnim){
35834 e.stopPropagation();
35836 if(!this.collapsed || this.el.hasActiveFx()) {
35840 this.afterSlideIn();
35843 this.collapsed = false;
35844 if(this.config.animate && skipAnim !== true){
35845 this.animateExpand();
35849 this.split.el.show();
35851 this.collapsedEl.setLocation(-2000,-2000);
35852 this.collapsedEl.hide();
35853 this.fireEvent("invalidated", this);
35854 this.fireEvent("expanded", this);
35858 animateExpand : function(){
35862 initTabs : function()
35864 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35866 var ts = new Roo.bootstrap.panel.Tabs({
35867 el: this.bodyEl.dom,
35868 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35869 disableTooltips: this.config.disableTabTips,
35870 toolbar : this.config.toolbar
35873 if(this.config.hideTabs){
35874 ts.stripWrap.setDisplayed(false);
35877 ts.resizeTabs = this.config.resizeTabs === true;
35878 ts.minTabWidth = this.config.minTabWidth || 40;
35879 ts.maxTabWidth = this.config.maxTabWidth || 250;
35880 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35881 ts.monitorResize = false;
35882 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35883 ts.bodyEl.addClass('roo-layout-tabs-body');
35884 this.panels.each(this.initPanelAsTab, this);
35887 initPanelAsTab : function(panel){
35888 var ti = this.tabs.addTab(
35892 this.config.closeOnTab && panel.isClosable(),
35895 if(panel.tabTip !== undefined){
35896 ti.setTooltip(panel.tabTip);
35898 ti.on("activate", function(){
35899 this.setActivePanel(panel);
35902 if(this.config.closeOnTab){
35903 ti.on("beforeclose", function(t, e){
35905 this.remove(panel);
35909 panel.tabItem = ti;
35914 updatePanelTitle : function(panel, title)
35916 if(this.activePanel == panel){
35917 this.updateTitle(title);
35920 var ti = this.tabs.getTab(panel.getEl().id);
35922 if(panel.tabTip !== undefined){
35923 ti.setTooltip(panel.tabTip);
35928 updateTitle : function(title){
35929 if(this.titleTextEl && !this.config.title){
35930 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35934 setActivePanel : function(panel)
35936 panel = this.getPanel(panel);
35937 if(this.activePanel && this.activePanel != panel){
35938 if(this.activePanel.setActiveState(false) === false){
35942 this.activePanel = panel;
35943 panel.setActiveState(true);
35944 if(this.panelSize){
35945 panel.setSize(this.panelSize.width, this.panelSize.height);
35948 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35950 this.updateTitle(panel.getTitle());
35952 this.fireEvent("invalidated", this);
35954 this.fireEvent("panelactivated", this, panel);
35958 * Shows the specified panel.
35959 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35960 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35962 showPanel : function(panel)
35964 panel = this.getPanel(panel);
35967 var tab = this.tabs.getTab(panel.getEl().id);
35968 if(tab.isHidden()){
35969 this.tabs.unhideTab(tab.id);
35973 this.setActivePanel(panel);
35980 * Get the active panel for this region.
35981 * @return {Roo.ContentPanel} The active panel or null
35983 getActivePanel : function(){
35984 return this.activePanel;
35987 validateVisibility : function(){
35988 if(this.panels.getCount() < 1){
35989 this.updateTitle(" ");
35990 this.closeBtn.hide();
35993 if(!this.isVisible()){
36000 * Adds the passed ContentPanel(s) to this region.
36001 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36002 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36004 add : function(panel)
36006 if(arguments.length > 1){
36007 for(var i = 0, len = arguments.length; i < len; i++) {
36008 this.add(arguments[i]);
36013 // if we have not been rendered yet, then we can not really do much of this..
36014 if (!this.bodyEl) {
36015 this.unrendered_panels.push(panel);
36022 if(this.hasPanel(panel)){
36023 this.showPanel(panel);
36026 panel.setRegion(this);
36027 this.panels.add(panel);
36028 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36029 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36030 // and hide them... ???
36031 this.bodyEl.dom.appendChild(panel.getEl().dom);
36032 if(panel.background !== true){
36033 this.setActivePanel(panel);
36035 this.fireEvent("paneladded", this, panel);
36042 this.initPanelAsTab(panel);
36046 if(panel.background !== true){
36047 this.tabs.activate(panel.getEl().id);
36049 this.fireEvent("paneladded", this, panel);
36054 * Hides the tab for the specified panel.
36055 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36057 hidePanel : function(panel){
36058 if(this.tabs && (panel = this.getPanel(panel))){
36059 this.tabs.hideTab(panel.getEl().id);
36064 * Unhides the tab for a previously hidden panel.
36065 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36067 unhidePanel : function(panel){
36068 if(this.tabs && (panel = this.getPanel(panel))){
36069 this.tabs.unhideTab(panel.getEl().id);
36073 clearPanels : function(){
36074 while(this.panels.getCount() > 0){
36075 this.remove(this.panels.first());
36080 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36081 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36082 * @param {Boolean} preservePanel Overrides the config preservePanel option
36083 * @return {Roo.ContentPanel} The panel that was removed
36085 remove : function(panel, preservePanel)
36087 panel = this.getPanel(panel);
36092 this.fireEvent("beforeremove", this, panel, e);
36093 if(e.cancel === true){
36096 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36097 var panelId = panel.getId();
36098 this.panels.removeKey(panelId);
36100 document.body.appendChild(panel.getEl().dom);
36103 this.tabs.removeTab(panel.getEl().id);
36104 }else if (!preservePanel){
36105 this.bodyEl.dom.removeChild(panel.getEl().dom);
36107 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36108 var p = this.panels.first();
36109 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36110 tempEl.appendChild(p.getEl().dom);
36111 this.bodyEl.update("");
36112 this.bodyEl.dom.appendChild(p.getEl().dom);
36114 this.updateTitle(p.getTitle());
36116 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36117 this.setActivePanel(p);
36119 panel.setRegion(null);
36120 if(this.activePanel == panel){
36121 this.activePanel = null;
36123 if(this.config.autoDestroy !== false && preservePanel !== true){
36124 try{panel.destroy();}catch(e){}
36126 this.fireEvent("panelremoved", this, panel);
36131 * Returns the TabPanel component used by this region
36132 * @return {Roo.TabPanel}
36134 getTabs : function(){
36138 createTool : function(parentEl, className){
36139 var btn = Roo.DomHelper.append(parentEl, {
36141 cls: "x-layout-tools-button",
36144 cls: "roo-layout-tools-button-inner " + className,
36148 btn.addClassOnOver("roo-layout-tools-button-over");
36153 * Ext JS Library 1.1.1
36154 * Copyright(c) 2006-2007, Ext JS, LLC.
36156 * Originally Released Under LGPL - original licence link has changed is not relivant.
36159 * <script type="text/javascript">
36165 * @class Roo.SplitLayoutRegion
36166 * @extends Roo.LayoutRegion
36167 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36169 Roo.bootstrap.layout.Split = function(config){
36170 this.cursor = config.cursor;
36171 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36174 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36176 splitTip : "Drag to resize.",
36177 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36178 useSplitTips : false,
36180 applyConfig : function(config){
36181 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36184 onRender : function(ctr,pos) {
36186 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36187 if(!this.config.split){
36192 var splitEl = Roo.DomHelper.append(ctr.dom, {
36194 id: this.el.id + "-split",
36195 cls: "roo-layout-split roo-layout-split-"+this.position,
36198 /** The SplitBar for this region
36199 * @type Roo.SplitBar */
36200 // does not exist yet...
36201 Roo.log([this.position, this.orientation]);
36203 this.split = new Roo.bootstrap.SplitBar({
36204 dragElement : splitEl,
36205 resizingElement: this.el,
36206 orientation : this.orientation
36209 this.split.on("moved", this.onSplitMove, this);
36210 this.split.useShim = this.config.useShim === true;
36211 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36212 if(this.useSplitTips){
36213 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36215 //if(config.collapsible){
36216 // this.split.el.on("dblclick", this.collapse, this);
36219 if(typeof this.config.minSize != "undefined"){
36220 this.split.minSize = this.config.minSize;
36222 if(typeof this.config.maxSize != "undefined"){
36223 this.split.maxSize = this.config.maxSize;
36225 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36226 this.hideSplitter();
36231 getHMaxSize : function(){
36232 var cmax = this.config.maxSize || 10000;
36233 var center = this.mgr.getRegion("center");
36234 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36237 getVMaxSize : function(){
36238 var cmax = this.config.maxSize || 10000;
36239 var center = this.mgr.getRegion("center");
36240 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36243 onSplitMove : function(split, newSize){
36244 this.fireEvent("resized", this, newSize);
36248 * Returns the {@link Roo.SplitBar} for this region.
36249 * @return {Roo.SplitBar}
36251 getSplitBar : function(){
36256 this.hideSplitter();
36257 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36260 hideSplitter : function(){
36262 this.split.el.setLocation(-2000,-2000);
36263 this.split.el.hide();
36269 this.split.el.show();
36271 Roo.bootstrap.layout.Split.superclass.show.call(this);
36274 beforeSlide: function(){
36275 if(Roo.isGecko){// firefox overflow auto bug workaround
36276 this.bodyEl.clip();
36278 this.tabs.bodyEl.clip();
36280 if(this.activePanel){
36281 this.activePanel.getEl().clip();
36283 if(this.activePanel.beforeSlide){
36284 this.activePanel.beforeSlide();
36290 afterSlide : function(){
36291 if(Roo.isGecko){// firefox overflow auto bug workaround
36292 this.bodyEl.unclip();
36294 this.tabs.bodyEl.unclip();
36296 if(this.activePanel){
36297 this.activePanel.getEl().unclip();
36298 if(this.activePanel.afterSlide){
36299 this.activePanel.afterSlide();
36305 initAutoHide : function(){
36306 if(this.autoHide !== false){
36307 if(!this.autoHideHd){
36308 var st = new Roo.util.DelayedTask(this.slideIn, this);
36309 this.autoHideHd = {
36310 "mouseout": function(e){
36311 if(!e.within(this.el, true)){
36315 "mouseover" : function(e){
36321 this.el.on(this.autoHideHd);
36325 clearAutoHide : function(){
36326 if(this.autoHide !== false){
36327 this.el.un("mouseout", this.autoHideHd.mouseout);
36328 this.el.un("mouseover", this.autoHideHd.mouseover);
36332 clearMonitor : function(){
36333 Roo.get(document).un("click", this.slideInIf, this);
36336 // these names are backwards but not changed for compat
36337 slideOut : function(){
36338 if(this.isSlid || this.el.hasActiveFx()){
36341 this.isSlid = true;
36342 if(this.collapseBtn){
36343 this.collapseBtn.hide();
36345 this.closeBtnState = this.closeBtn.getStyle('display');
36346 this.closeBtn.hide();
36348 this.stickBtn.show();
36351 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36352 this.beforeSlide();
36353 this.el.setStyle("z-index", 10001);
36354 this.el.slideIn(this.getSlideAnchor(), {
36355 callback: function(){
36357 this.initAutoHide();
36358 Roo.get(document).on("click", this.slideInIf, this);
36359 this.fireEvent("slideshow", this);
36366 afterSlideIn : function(){
36367 this.clearAutoHide();
36368 this.isSlid = false;
36369 this.clearMonitor();
36370 this.el.setStyle("z-index", "");
36371 if(this.collapseBtn){
36372 this.collapseBtn.show();
36374 this.closeBtn.setStyle('display', this.closeBtnState);
36376 this.stickBtn.hide();
36378 this.fireEvent("slidehide", this);
36381 slideIn : function(cb){
36382 if(!this.isSlid || this.el.hasActiveFx()){
36386 this.isSlid = false;
36387 this.beforeSlide();
36388 this.el.slideOut(this.getSlideAnchor(), {
36389 callback: function(){
36390 this.el.setLeftTop(-10000, -10000);
36392 this.afterSlideIn();
36400 slideInIf : function(e){
36401 if(!e.within(this.el)){
36406 animateCollapse : function(){
36407 this.beforeSlide();
36408 this.el.setStyle("z-index", 20000);
36409 var anchor = this.getSlideAnchor();
36410 this.el.slideOut(anchor, {
36411 callback : function(){
36412 this.el.setStyle("z-index", "");
36413 this.collapsedEl.slideIn(anchor, {duration:.3});
36415 this.el.setLocation(-10000,-10000);
36417 this.fireEvent("collapsed", this);
36424 animateExpand : function(){
36425 this.beforeSlide();
36426 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36427 this.el.setStyle("z-index", 20000);
36428 this.collapsedEl.hide({
36431 this.el.slideIn(this.getSlideAnchor(), {
36432 callback : function(){
36433 this.el.setStyle("z-index", "");
36436 this.split.el.show();
36438 this.fireEvent("invalidated", this);
36439 this.fireEvent("expanded", this);
36467 getAnchor : function(){
36468 return this.anchors[this.position];
36471 getCollapseAnchor : function(){
36472 return this.canchors[this.position];
36475 getSlideAnchor : function(){
36476 return this.sanchors[this.position];
36479 getAlignAdj : function(){
36480 var cm = this.cmargins;
36481 switch(this.position){
36497 getExpandAdj : function(){
36498 var c = this.collapsedEl, cm = this.cmargins;
36499 switch(this.position){
36501 return [-(cm.right+c.getWidth()+cm.left), 0];
36504 return [cm.right+c.getWidth()+cm.left, 0];
36507 return [0, -(cm.top+cm.bottom+c.getHeight())];
36510 return [0, cm.top+cm.bottom+c.getHeight()];
36516 * Ext JS Library 1.1.1
36517 * Copyright(c) 2006-2007, Ext JS, LLC.
36519 * Originally Released Under LGPL - original licence link has changed is not relivant.
36522 * <script type="text/javascript">
36525 * These classes are private internal classes
36527 Roo.bootstrap.layout.Center = function(config){
36528 config.region = "center";
36529 Roo.bootstrap.layout.Region.call(this, config);
36530 this.visible = true;
36531 this.minWidth = config.minWidth || 20;
36532 this.minHeight = config.minHeight || 20;
36535 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36537 // center panel can't be hidden
36541 // center panel can't be hidden
36544 getMinWidth: function(){
36545 return this.minWidth;
36548 getMinHeight: function(){
36549 return this.minHeight;
36562 Roo.bootstrap.layout.North = function(config)
36564 config.region = 'north';
36565 config.cursor = 'n-resize';
36567 Roo.bootstrap.layout.Split.call(this, config);
36571 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36572 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36573 this.split.el.addClass("roo-layout-split-v");
36575 var size = config.initialSize || config.height;
36576 if(typeof size != "undefined"){
36577 this.el.setHeight(size);
36580 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36582 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36586 getBox : function(){
36587 if(this.collapsed){
36588 return this.collapsedEl.getBox();
36590 var box = this.el.getBox();
36592 box.height += this.split.el.getHeight();
36597 updateBox : function(box){
36598 if(this.split && !this.collapsed){
36599 box.height -= this.split.el.getHeight();
36600 this.split.el.setLeft(box.x);
36601 this.split.el.setTop(box.y+box.height);
36602 this.split.el.setWidth(box.width);
36604 if(this.collapsed){
36605 this.updateBody(box.width, null);
36607 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36615 Roo.bootstrap.layout.South = function(config){
36616 config.region = 'south';
36617 config.cursor = 's-resize';
36618 Roo.bootstrap.layout.Split.call(this, config);
36620 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36621 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36622 this.split.el.addClass("roo-layout-split-v");
36624 var size = config.initialSize || config.height;
36625 if(typeof size != "undefined"){
36626 this.el.setHeight(size);
36630 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36631 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36632 getBox : function(){
36633 if(this.collapsed){
36634 return this.collapsedEl.getBox();
36636 var box = this.el.getBox();
36638 var sh = this.split.el.getHeight();
36645 updateBox : function(box){
36646 if(this.split && !this.collapsed){
36647 var sh = this.split.el.getHeight();
36650 this.split.el.setLeft(box.x);
36651 this.split.el.setTop(box.y-sh);
36652 this.split.el.setWidth(box.width);
36654 if(this.collapsed){
36655 this.updateBody(box.width, null);
36657 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36661 Roo.bootstrap.layout.East = function(config){
36662 config.region = "east";
36663 config.cursor = "e-resize";
36664 Roo.bootstrap.layout.Split.call(this, config);
36666 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36667 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36668 this.split.el.addClass("roo-layout-split-h");
36670 var size = config.initialSize || config.width;
36671 if(typeof size != "undefined"){
36672 this.el.setWidth(size);
36675 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36676 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36677 getBox : function(){
36678 if(this.collapsed){
36679 return this.collapsedEl.getBox();
36681 var box = this.el.getBox();
36683 var sw = this.split.el.getWidth();
36690 updateBox : function(box){
36691 if(this.split && !this.collapsed){
36692 var sw = this.split.el.getWidth();
36694 this.split.el.setLeft(box.x);
36695 this.split.el.setTop(box.y);
36696 this.split.el.setHeight(box.height);
36699 if(this.collapsed){
36700 this.updateBody(null, box.height);
36702 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36706 Roo.bootstrap.layout.West = function(config){
36707 config.region = "west";
36708 config.cursor = "w-resize";
36710 Roo.bootstrap.layout.Split.call(this, config);
36712 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36713 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36714 this.split.el.addClass("roo-layout-split-h");
36718 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36719 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36721 onRender: function(ctr, pos)
36723 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36724 var size = this.config.initialSize || this.config.width;
36725 if(typeof size != "undefined"){
36726 this.el.setWidth(size);
36730 getBox : function(){
36731 if(this.collapsed){
36732 return this.collapsedEl.getBox();
36734 var box = this.el.getBox();
36736 box.width += this.split.el.getWidth();
36741 updateBox : function(box){
36742 if(this.split && !this.collapsed){
36743 var sw = this.split.el.getWidth();
36745 this.split.el.setLeft(box.x+box.width);
36746 this.split.el.setTop(box.y);
36747 this.split.el.setHeight(box.height);
36749 if(this.collapsed){
36750 this.updateBody(null, box.height);
36752 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36755 Roo.namespace("Roo.bootstrap.panel");/*
36757 * Ext JS Library 1.1.1
36758 * Copyright(c) 2006-2007, Ext JS, LLC.
36760 * Originally Released Under LGPL - original licence link has changed is not relivant.
36763 * <script type="text/javascript">
36766 * @class Roo.ContentPanel
36767 * @extends Roo.util.Observable
36768 * A basic ContentPanel element.
36769 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36770 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36771 * @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
36772 * @cfg {Boolean} closable True if the panel can be closed/removed
36773 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36774 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36775 * @cfg {Toolbar} toolbar A toolbar for this panel
36776 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36777 * @cfg {String} title The title for this panel
36778 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36779 * @cfg {String} url Calls {@link #setUrl} with this value
36780 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36781 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36782 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36783 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36784 * @cfg {Boolean} badges render the badges
36787 * Create a new ContentPanel.
36788 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36789 * @param {String/Object} config A string to set only the title or a config object
36790 * @param {String} content (optional) Set the HTML content for this panel
36791 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36793 Roo.bootstrap.panel.Content = function( config){
36795 this.tpl = config.tpl || false;
36797 var el = config.el;
36798 var content = config.content;
36800 if(config.autoCreate){ // xtype is available if this is called from factory
36803 this.el = Roo.get(el);
36804 if(!this.el && config && config.autoCreate){
36805 if(typeof config.autoCreate == "object"){
36806 if(!config.autoCreate.id){
36807 config.autoCreate.id = config.id||el;
36809 this.el = Roo.DomHelper.append(document.body,
36810 config.autoCreate, true);
36812 var elcfg = { tag: "div",
36813 cls: "roo-layout-inactive-content",
36817 elcfg.html = config.html;
36821 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36824 this.closable = false;
36825 this.loaded = false;
36826 this.active = false;
36829 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36831 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36833 this.wrapEl = this.el; //this.el.wrap();
36835 if (config.toolbar.items) {
36836 ti = config.toolbar.items ;
36837 delete config.toolbar.items ;
36841 this.toolbar.render(this.wrapEl, 'before');
36842 for(var i =0;i < ti.length;i++) {
36843 // Roo.log(['add child', items[i]]);
36844 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36846 this.toolbar.items = nitems;
36847 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36848 delete config.toolbar;
36852 // xtype created footer. - not sure if will work as we normally have to render first..
36853 if (this.footer && !this.footer.el && this.footer.xtype) {
36854 if (!this.wrapEl) {
36855 this.wrapEl = this.el.wrap();
36858 this.footer.container = this.wrapEl.createChild();
36860 this.footer = Roo.factory(this.footer, Roo);
36865 if(typeof config == "string"){
36866 this.title = config;
36868 Roo.apply(this, config);
36872 this.resizeEl = Roo.get(this.resizeEl, true);
36874 this.resizeEl = this.el;
36876 // handle view.xtype
36884 * Fires when this panel is activated.
36885 * @param {Roo.ContentPanel} this
36889 * @event deactivate
36890 * Fires when this panel is activated.
36891 * @param {Roo.ContentPanel} this
36893 "deactivate" : true,
36897 * Fires when this panel is resized if fitToFrame is true.
36898 * @param {Roo.ContentPanel} this
36899 * @param {Number} width The width after any component adjustments
36900 * @param {Number} height The height after any component adjustments
36906 * Fires when this tab is created
36907 * @param {Roo.ContentPanel} this
36918 if(this.autoScroll){
36919 this.resizeEl.setStyle("overflow", "auto");
36921 // fix randome scrolling
36922 //this.el.on('scroll', function() {
36923 // Roo.log('fix random scolling');
36924 // this.scrollTo('top',0);
36927 content = content || this.content;
36929 this.setContent(content);
36931 if(config && config.url){
36932 this.setUrl(this.url, this.params, this.loadOnce);
36937 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36939 if (this.view && typeof(this.view.xtype) != 'undefined') {
36940 this.view.el = this.el.appendChild(document.createElement("div"));
36941 this.view = Roo.factory(this.view);
36942 this.view.render && this.view.render(false, '');
36946 this.fireEvent('render', this);
36949 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36953 setRegion : function(region){
36954 this.region = region;
36955 this.setActiveClass(region && !this.background);
36959 setActiveClass: function(state)
36962 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36963 this.el.setStyle('position','relative');
36965 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36966 this.el.setStyle('position', 'absolute');
36971 * Returns the toolbar for this Panel if one was configured.
36972 * @return {Roo.Toolbar}
36974 getToolbar : function(){
36975 return this.toolbar;
36978 setActiveState : function(active)
36980 this.active = active;
36981 this.setActiveClass(active);
36983 if(this.fireEvent("deactivate", this) === false){
36988 this.fireEvent("activate", this);
36992 * Updates this panel's element
36993 * @param {String} content The new content
36994 * @param {Boolean} loadScripts (optional) true to look for and process scripts
36996 setContent : function(content, loadScripts){
36997 this.el.update(content, loadScripts);
37000 ignoreResize : function(w, h){
37001 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37004 this.lastSize = {width: w, height: h};
37009 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37010 * @return {Roo.UpdateManager} The UpdateManager
37012 getUpdateManager : function(){
37013 return this.el.getUpdateManager();
37016 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37017 * @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:
37020 url: "your-url.php",
37021 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37022 callback: yourFunction,
37023 scope: yourObject, //(optional scope)
37026 text: "Loading...",
37031 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37032 * 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.
37033 * @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}
37034 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37035 * @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.
37036 * @return {Roo.ContentPanel} this
37039 var um = this.el.getUpdateManager();
37040 um.update.apply(um, arguments);
37046 * 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.
37047 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37048 * @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)
37049 * @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)
37050 * @return {Roo.UpdateManager} The UpdateManager
37052 setUrl : function(url, params, loadOnce){
37053 if(this.refreshDelegate){
37054 this.removeListener("activate", this.refreshDelegate);
37056 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37057 this.on("activate", this.refreshDelegate);
37058 return this.el.getUpdateManager();
37061 _handleRefresh : function(url, params, loadOnce){
37062 if(!loadOnce || !this.loaded){
37063 var updater = this.el.getUpdateManager();
37064 updater.update(url, params, this._setLoaded.createDelegate(this));
37068 _setLoaded : function(){
37069 this.loaded = true;
37073 * Returns this panel's id
37076 getId : function(){
37081 * Returns this panel's element - used by regiosn to add.
37082 * @return {Roo.Element}
37084 getEl : function(){
37085 return this.wrapEl || this.el;
37090 adjustForComponents : function(width, height)
37092 //Roo.log('adjustForComponents ');
37093 if(this.resizeEl != this.el){
37094 width -= this.el.getFrameWidth('lr');
37095 height -= this.el.getFrameWidth('tb');
37098 var te = this.toolbar.getEl();
37099 te.setWidth(width);
37100 height -= te.getHeight();
37103 var te = this.footer.getEl();
37104 te.setWidth(width);
37105 height -= te.getHeight();
37109 if(this.adjustments){
37110 width += this.adjustments[0];
37111 height += this.adjustments[1];
37113 return {"width": width, "height": height};
37116 setSize : function(width, height){
37117 if(this.fitToFrame && !this.ignoreResize(width, height)){
37118 if(this.fitContainer && this.resizeEl != this.el){
37119 this.el.setSize(width, height);
37121 var size = this.adjustForComponents(width, height);
37122 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37123 this.fireEvent('resize', this, size.width, size.height);
37128 * Returns this panel's title
37131 getTitle : function(){
37133 if (typeof(this.title) != 'object') {
37138 for (var k in this.title) {
37139 if (!this.title.hasOwnProperty(k)) {
37143 if (k.indexOf('-') >= 0) {
37144 var s = k.split('-');
37145 for (var i = 0; i<s.length; i++) {
37146 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37149 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37156 * Set this panel's title
37157 * @param {String} title
37159 setTitle : function(title){
37160 this.title = title;
37162 this.region.updatePanelTitle(this, title);
37167 * Returns true is this panel was configured to be closable
37168 * @return {Boolean}
37170 isClosable : function(){
37171 return this.closable;
37174 beforeSlide : function(){
37176 this.resizeEl.clip();
37179 afterSlide : function(){
37181 this.resizeEl.unclip();
37185 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37186 * Will fail silently if the {@link #setUrl} method has not been called.
37187 * This does not activate the panel, just updates its content.
37189 refresh : function(){
37190 if(this.refreshDelegate){
37191 this.loaded = false;
37192 this.refreshDelegate();
37197 * Destroys this panel
37199 destroy : function(){
37200 this.el.removeAllListeners();
37201 var tempEl = document.createElement("span");
37202 tempEl.appendChild(this.el.dom);
37203 tempEl.innerHTML = "";
37209 * form - if the content panel contains a form - this is a reference to it.
37210 * @type {Roo.form.Form}
37214 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37215 * This contains a reference to it.
37221 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37231 * @param {Object} cfg Xtype definition of item to add.
37235 getChildContainer: function () {
37236 return this.getEl();
37241 var ret = new Roo.factory(cfg);
37246 if (cfg.xtype.match(/^Form$/)) {
37249 //if (this.footer) {
37250 // el = this.footer.container.insertSibling(false, 'before');
37252 el = this.el.createChild();
37255 this.form = new Roo.form.Form(cfg);
37258 if ( this.form.allItems.length) {
37259 this.form.render(el.dom);
37263 // should only have one of theses..
37264 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37265 // views.. should not be just added - used named prop 'view''
37267 cfg.el = this.el.appendChild(document.createElement("div"));
37270 var ret = new Roo.factory(cfg);
37272 ret.render && ret.render(false, ''); // render blank..
37282 * @class Roo.bootstrap.panel.Grid
37283 * @extends Roo.bootstrap.panel.Content
37285 * Create a new GridPanel.
37286 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37287 * @param {Object} config A the config object
37293 Roo.bootstrap.panel.Grid = function(config)
37297 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37298 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37300 config.el = this.wrapper;
37301 //this.el = this.wrapper;
37303 if (config.container) {
37304 // ctor'ed from a Border/panel.grid
37307 this.wrapper.setStyle("overflow", "hidden");
37308 this.wrapper.addClass('roo-grid-container');
37313 if(config.toolbar){
37314 var tool_el = this.wrapper.createChild();
37315 this.toolbar = Roo.factory(config.toolbar);
37317 if (config.toolbar.items) {
37318 ti = config.toolbar.items ;
37319 delete config.toolbar.items ;
37323 this.toolbar.render(tool_el);
37324 for(var i =0;i < ti.length;i++) {
37325 // Roo.log(['add child', items[i]]);
37326 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37328 this.toolbar.items = nitems;
37330 delete config.toolbar;
37333 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37334 config.grid.scrollBody = true;;
37335 config.grid.monitorWindowResize = false; // turn off autosizing
37336 config.grid.autoHeight = false;
37337 config.grid.autoWidth = false;
37339 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37341 if (config.background) {
37342 // render grid on panel activation (if panel background)
37343 this.on('activate', function(gp) {
37344 if (!gp.grid.rendered) {
37345 gp.grid.render(this.wrapper);
37346 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37351 this.grid.render(this.wrapper);
37352 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37355 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37356 // ??? needed ??? config.el = this.wrapper;
37361 // xtype created footer. - not sure if will work as we normally have to render first..
37362 if (this.footer && !this.footer.el && this.footer.xtype) {
37364 var ctr = this.grid.getView().getFooterPanel(true);
37365 this.footer.dataSource = this.grid.dataSource;
37366 this.footer = Roo.factory(this.footer, Roo);
37367 this.footer.render(ctr);
37377 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37378 getId : function(){
37379 return this.grid.id;
37383 * Returns the grid for this panel
37384 * @return {Roo.bootstrap.Table}
37386 getGrid : function(){
37390 setSize : function(width, height){
37391 if(!this.ignoreResize(width, height)){
37392 var grid = this.grid;
37393 var size = this.adjustForComponents(width, height);
37394 var gridel = grid.getGridEl();
37395 gridel.setSize(size.width, size.height);
37397 var thd = grid.getGridEl().select('thead',true).first();
37398 var tbd = grid.getGridEl().select('tbody', true).first();
37400 tbd.setSize(width, height - thd.getHeight());
37409 beforeSlide : function(){
37410 this.grid.getView().scroller.clip();
37413 afterSlide : function(){
37414 this.grid.getView().scroller.unclip();
37417 destroy : function(){
37418 this.grid.destroy();
37420 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37425 * @class Roo.bootstrap.panel.Nest
37426 * @extends Roo.bootstrap.panel.Content
37428 * Create a new Panel, that can contain a layout.Border.
37431 * @param {Roo.BorderLayout} layout The layout for this panel
37432 * @param {String/Object} config A string to set only the title or a config object
37434 Roo.bootstrap.panel.Nest = function(config)
37436 // construct with only one argument..
37437 /* FIXME - implement nicer consturctors
37438 if (layout.layout) {
37440 layout = config.layout;
37441 delete config.layout;
37443 if (layout.xtype && !layout.getEl) {
37444 // then layout needs constructing..
37445 layout = Roo.factory(layout, Roo);
37449 config.el = config.layout.getEl();
37451 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37453 config.layout.monitorWindowResize = false; // turn off autosizing
37454 this.layout = config.layout;
37455 this.layout.getEl().addClass("roo-layout-nested-layout");
37462 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37464 setSize : function(width, height){
37465 if(!this.ignoreResize(width, height)){
37466 var size = this.adjustForComponents(width, height);
37467 var el = this.layout.getEl();
37468 if (size.height < 1) {
37469 el.setWidth(size.width);
37471 el.setSize(size.width, size.height);
37473 var touch = el.dom.offsetWidth;
37474 this.layout.layout();
37475 // ie requires a double layout on the first pass
37476 if(Roo.isIE && !this.initialized){
37477 this.initialized = true;
37478 this.layout.layout();
37483 // activate all subpanels if not currently active..
37485 setActiveState : function(active){
37486 this.active = active;
37487 this.setActiveClass(active);
37490 this.fireEvent("deactivate", this);
37494 this.fireEvent("activate", this);
37495 // not sure if this should happen before or after..
37496 if (!this.layout) {
37497 return; // should not happen..
37500 for (var r in this.layout.regions) {
37501 reg = this.layout.getRegion(r);
37502 if (reg.getActivePanel()) {
37503 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37504 reg.setActivePanel(reg.getActivePanel());
37507 if (!reg.panels.length) {
37510 reg.showPanel(reg.getPanel(0));
37519 * Returns the nested BorderLayout for this panel
37520 * @return {Roo.BorderLayout}
37522 getLayout : function(){
37523 return this.layout;
37527 * Adds a xtype elements to the layout of the nested panel
37531 xtype : 'ContentPanel',
37538 xtype : 'NestedLayoutPanel',
37544 items : [ ... list of content panels or nested layout panels.. ]
37548 * @param {Object} cfg Xtype definition of item to add.
37550 addxtype : function(cfg) {
37551 return this.layout.addxtype(cfg);
37556 * Ext JS Library 1.1.1
37557 * Copyright(c) 2006-2007, Ext JS, LLC.
37559 * Originally Released Under LGPL - original licence link has changed is not relivant.
37562 * <script type="text/javascript">
37565 * @class Roo.TabPanel
37566 * @extends Roo.util.Observable
37567 * A lightweight tab container.
37571 // basic tabs 1, built from existing content
37572 var tabs = new Roo.TabPanel("tabs1");
37573 tabs.addTab("script", "View Script");
37574 tabs.addTab("markup", "View Markup");
37575 tabs.activate("script");
37577 // more advanced tabs, built from javascript
37578 var jtabs = new Roo.TabPanel("jtabs");
37579 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37581 // set up the UpdateManager
37582 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37583 var updater = tab2.getUpdateManager();
37584 updater.setDefaultUrl("ajax1.htm");
37585 tab2.on('activate', updater.refresh, updater, true);
37587 // Use setUrl for Ajax loading
37588 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37589 tab3.setUrl("ajax2.htm", null, true);
37592 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37595 jtabs.activate("jtabs-1");
37598 * Create a new TabPanel.
37599 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37600 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37602 Roo.bootstrap.panel.Tabs = function(config){
37604 * The container element for this TabPanel.
37605 * @type Roo.Element
37607 this.el = Roo.get(config.el);
37610 if(typeof config == "boolean"){
37611 this.tabPosition = config ? "bottom" : "top";
37613 Roo.apply(this, config);
37617 if(this.tabPosition == "bottom"){
37618 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37619 this.el.addClass("roo-tabs-bottom");
37621 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37622 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37623 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37625 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37627 if(this.tabPosition != "bottom"){
37628 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37629 * @type Roo.Element
37631 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37632 this.el.addClass("roo-tabs-top");
37636 this.bodyEl.setStyle("position", "relative");
37638 this.active = null;
37639 this.activateDelegate = this.activate.createDelegate(this);
37644 * Fires when the active tab changes
37645 * @param {Roo.TabPanel} this
37646 * @param {Roo.TabPanelItem} activePanel The new active tab
37650 * @event beforetabchange
37651 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37652 * @param {Roo.TabPanel} this
37653 * @param {Object} e Set cancel to true on this object to cancel the tab change
37654 * @param {Roo.TabPanelItem} tab The tab being changed to
37656 "beforetabchange" : true
37659 Roo.EventManager.onWindowResize(this.onResize, this);
37660 this.cpad = this.el.getPadding("lr");
37661 this.hiddenCount = 0;
37664 // toolbar on the tabbar support...
37665 if (this.toolbar) {
37666 alert("no toolbar support yet");
37667 this.toolbar = false;
37669 var tcfg = this.toolbar;
37670 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37671 this.toolbar = new Roo.Toolbar(tcfg);
37672 if (Roo.isSafari) {
37673 var tbl = tcfg.container.child('table', true);
37674 tbl.setAttribute('width', '100%');
37682 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37685 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37687 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37689 tabPosition : "top",
37691 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37693 currentTabWidth : 0,
37695 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37699 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37703 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37705 preferredTabWidth : 175,
37707 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37709 resizeTabs : false,
37711 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37713 monitorResize : true,
37715 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37720 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37721 * @param {String} id The id of the div to use <b>or create</b>
37722 * @param {String} text The text for the tab
37723 * @param {String} content (optional) Content to put in the TabPanelItem body
37724 * @param {Boolean} closable (optional) True to create a close icon on the tab
37725 * @return {Roo.TabPanelItem} The created TabPanelItem
37727 addTab : function(id, text, content, closable, tpl)
37729 var item = new Roo.bootstrap.panel.TabItem({
37733 closable : closable,
37736 this.addTabItem(item);
37738 item.setContent(content);
37744 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37745 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37746 * @return {Roo.TabPanelItem}
37748 getTab : function(id){
37749 return this.items[id];
37753 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37754 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37756 hideTab : function(id){
37757 var t = this.items[id];
37760 this.hiddenCount++;
37761 this.autoSizeTabs();
37766 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37767 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37769 unhideTab : function(id){
37770 var t = this.items[id];
37772 t.setHidden(false);
37773 this.hiddenCount--;
37774 this.autoSizeTabs();
37779 * Adds an existing {@link Roo.TabPanelItem}.
37780 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37782 addTabItem : function(item){
37783 this.items[item.id] = item;
37784 this.items.push(item);
37785 // if(this.resizeTabs){
37786 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37787 // this.autoSizeTabs();
37789 // item.autoSize();
37794 * Removes a {@link Roo.TabPanelItem}.
37795 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37797 removeTab : function(id){
37798 var items = this.items;
37799 var tab = items[id];
37800 if(!tab) { return; }
37801 var index = items.indexOf(tab);
37802 if(this.active == tab && items.length > 1){
37803 var newTab = this.getNextAvailable(index);
37808 this.stripEl.dom.removeChild(tab.pnode.dom);
37809 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37810 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37812 items.splice(index, 1);
37813 delete this.items[tab.id];
37814 tab.fireEvent("close", tab);
37815 tab.purgeListeners();
37816 this.autoSizeTabs();
37819 getNextAvailable : function(start){
37820 var items = this.items;
37822 // look for a next tab that will slide over to
37823 // replace the one being removed
37824 while(index < items.length){
37825 var item = items[++index];
37826 if(item && !item.isHidden()){
37830 // if one isn't found select the previous tab (on the left)
37833 var item = items[--index];
37834 if(item && !item.isHidden()){
37842 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37843 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37845 disableTab : function(id){
37846 var tab = this.items[id];
37847 if(tab && this.active != tab){
37853 * Enables a {@link Roo.TabPanelItem} that is disabled.
37854 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37856 enableTab : function(id){
37857 var tab = this.items[id];
37862 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37863 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37864 * @return {Roo.TabPanelItem} The TabPanelItem.
37866 activate : function(id){
37867 var tab = this.items[id];
37871 if(tab == this.active || tab.disabled){
37875 this.fireEvent("beforetabchange", this, e, tab);
37876 if(e.cancel !== true && !tab.disabled){
37878 this.active.hide();
37880 this.active = this.items[id];
37881 this.active.show();
37882 this.fireEvent("tabchange", this, this.active);
37888 * Gets the active {@link Roo.TabPanelItem}.
37889 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37891 getActiveTab : function(){
37892 return this.active;
37896 * Updates the tab body element to fit the height of the container element
37897 * for overflow scrolling
37898 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37900 syncHeight : function(targetHeight){
37901 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37902 var bm = this.bodyEl.getMargins();
37903 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37904 this.bodyEl.setHeight(newHeight);
37908 onResize : function(){
37909 if(this.monitorResize){
37910 this.autoSizeTabs();
37915 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37917 beginUpdate : function(){
37918 this.updating = true;
37922 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37924 endUpdate : function(){
37925 this.updating = false;
37926 this.autoSizeTabs();
37930 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37932 autoSizeTabs : function(){
37933 var count = this.items.length;
37934 var vcount = count - this.hiddenCount;
37935 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37938 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37939 var availWidth = Math.floor(w / vcount);
37940 var b = this.stripBody;
37941 if(b.getWidth() > w){
37942 var tabs = this.items;
37943 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37944 if(availWidth < this.minTabWidth){
37945 /*if(!this.sleft){ // incomplete scrolling code
37946 this.createScrollButtons();
37949 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37952 if(this.currentTabWidth < this.preferredTabWidth){
37953 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37959 * Returns the number of tabs in this TabPanel.
37962 getCount : function(){
37963 return this.items.length;
37967 * Resizes all the tabs to the passed width
37968 * @param {Number} The new width
37970 setTabWidth : function(width){
37971 this.currentTabWidth = width;
37972 for(var i = 0, len = this.items.length; i < len; i++) {
37973 if(!this.items[i].isHidden()) {
37974 this.items[i].setWidth(width);
37980 * Destroys this TabPanel
37981 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37983 destroy : function(removeEl){
37984 Roo.EventManager.removeResizeListener(this.onResize, this);
37985 for(var i = 0, len = this.items.length; i < len; i++){
37986 this.items[i].purgeListeners();
37988 if(removeEl === true){
37989 this.el.update("");
37994 createStrip : function(container)
37996 var strip = document.createElement("nav");
37997 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37998 container.appendChild(strip);
38002 createStripList : function(strip)
38004 // div wrapper for retard IE
38005 // returns the "tr" element.
38006 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38007 //'<div class="x-tabs-strip-wrap">'+
38008 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38009 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38010 return strip.firstChild; //.firstChild.firstChild.firstChild;
38012 createBody : function(container)
38014 var body = document.createElement("div");
38015 Roo.id(body, "tab-body");
38016 //Roo.fly(body).addClass("x-tabs-body");
38017 Roo.fly(body).addClass("tab-content");
38018 container.appendChild(body);
38021 createItemBody :function(bodyEl, id){
38022 var body = Roo.getDom(id);
38024 body = document.createElement("div");
38027 //Roo.fly(body).addClass("x-tabs-item-body");
38028 Roo.fly(body).addClass("tab-pane");
38029 bodyEl.insertBefore(body, bodyEl.firstChild);
38033 createStripElements : function(stripEl, text, closable, tpl)
38035 var td = document.createElement("li"); // was td..
38038 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38041 stripEl.appendChild(td);
38043 td.className = "x-tabs-closable";
38044 if(!this.closeTpl){
38045 this.closeTpl = new Roo.Template(
38046 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38047 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38048 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38051 var el = this.closeTpl.overwrite(td, {"text": text});
38052 var close = el.getElementsByTagName("div")[0];
38053 var inner = el.getElementsByTagName("em")[0];
38054 return {"el": el, "close": close, "inner": inner};
38057 // not sure what this is..
38058 // if(!this.tabTpl){
38059 //this.tabTpl = new Roo.Template(
38060 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38061 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38063 // this.tabTpl = new Roo.Template(
38064 // '<a href="#">' +
38065 // '<span unselectable="on"' +
38066 // (this.disableTooltips ? '' : ' title="{text}"') +
38067 // ' >{text}</span></a>'
38073 var template = tpl || this.tabTpl || false;
38077 template = new Roo.Template(
38079 '<span unselectable="on"' +
38080 (this.disableTooltips ? '' : ' title="{text}"') +
38081 ' >{text}</span></a>'
38085 switch (typeof(template)) {
38089 template = new Roo.Template(template);
38095 var el = template.overwrite(td, {"text": text});
38097 var inner = el.getElementsByTagName("span")[0];
38099 return {"el": el, "inner": inner};
38107 * @class Roo.TabPanelItem
38108 * @extends Roo.util.Observable
38109 * Represents an individual item (tab plus body) in a TabPanel.
38110 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38111 * @param {String} id The id of this TabPanelItem
38112 * @param {String} text The text for the tab of this TabPanelItem
38113 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38115 Roo.bootstrap.panel.TabItem = function(config){
38117 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38118 * @type Roo.TabPanel
38120 this.tabPanel = config.panel;
38122 * The id for this TabPanelItem
38125 this.id = config.id;
38127 this.disabled = false;
38129 this.text = config.text;
38131 this.loaded = false;
38132 this.closable = config.closable;
38135 * The body element for this TabPanelItem.
38136 * @type Roo.Element
38138 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38139 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38140 this.bodyEl.setStyle("display", "block");
38141 this.bodyEl.setStyle("zoom", "1");
38142 //this.hideAction();
38144 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38146 this.el = Roo.get(els.el);
38147 this.inner = Roo.get(els.inner, true);
38148 this.textEl = Roo.get(this.el.dom.firstChild, true);
38149 this.pnode = Roo.get(els.el.parentNode, true);
38150 // this.el.on("mousedown", this.onTabMouseDown, this);
38151 this.el.on("click", this.onTabClick, this);
38153 if(config.closable){
38154 var c = Roo.get(els.close, true);
38155 c.dom.title = this.closeText;
38156 c.addClassOnOver("close-over");
38157 c.on("click", this.closeClick, this);
38163 * Fires when this tab becomes the active tab.
38164 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38165 * @param {Roo.TabPanelItem} this
38169 * @event beforeclose
38170 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38171 * @param {Roo.TabPanelItem} this
38172 * @param {Object} e Set cancel to true on this object to cancel the close.
38174 "beforeclose": true,
38177 * Fires when this tab is closed.
38178 * @param {Roo.TabPanelItem} this
38182 * @event deactivate
38183 * Fires when this tab is no longer the active tab.
38184 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38185 * @param {Roo.TabPanelItem} this
38187 "deactivate" : true
38189 this.hidden = false;
38191 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38194 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38196 purgeListeners : function(){
38197 Roo.util.Observable.prototype.purgeListeners.call(this);
38198 this.el.removeAllListeners();
38201 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38204 this.pnode.addClass("active");
38207 this.tabPanel.stripWrap.repaint();
38209 this.fireEvent("activate", this.tabPanel, this);
38213 * Returns true if this tab is the active tab.
38214 * @return {Boolean}
38216 isActive : function(){
38217 return this.tabPanel.getActiveTab() == this;
38221 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38224 this.pnode.removeClass("active");
38226 this.fireEvent("deactivate", this.tabPanel, this);
38229 hideAction : function(){
38230 this.bodyEl.hide();
38231 this.bodyEl.setStyle("position", "absolute");
38232 this.bodyEl.setLeft("-20000px");
38233 this.bodyEl.setTop("-20000px");
38236 showAction : function(){
38237 this.bodyEl.setStyle("position", "relative");
38238 this.bodyEl.setTop("");
38239 this.bodyEl.setLeft("");
38240 this.bodyEl.show();
38244 * Set the tooltip for the tab.
38245 * @param {String} tooltip The tab's tooltip
38247 setTooltip : function(text){
38248 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38249 this.textEl.dom.qtip = text;
38250 this.textEl.dom.removeAttribute('title');
38252 this.textEl.dom.title = text;
38256 onTabClick : function(e){
38257 e.preventDefault();
38258 this.tabPanel.activate(this.id);
38261 onTabMouseDown : function(e){
38262 e.preventDefault();
38263 this.tabPanel.activate(this.id);
38266 getWidth : function(){
38267 return this.inner.getWidth();
38270 setWidth : function(width){
38271 var iwidth = width - this.pnode.getPadding("lr");
38272 this.inner.setWidth(iwidth);
38273 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38274 this.pnode.setWidth(width);
38278 * Show or hide the tab
38279 * @param {Boolean} hidden True to hide or false to show.
38281 setHidden : function(hidden){
38282 this.hidden = hidden;
38283 this.pnode.setStyle("display", hidden ? "none" : "");
38287 * Returns true if this tab is "hidden"
38288 * @return {Boolean}
38290 isHidden : function(){
38291 return this.hidden;
38295 * Returns the text for this tab
38298 getText : function(){
38302 autoSize : function(){
38303 //this.el.beginMeasure();
38304 this.textEl.setWidth(1);
38306 * #2804 [new] Tabs in Roojs
38307 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38309 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38310 //this.el.endMeasure();
38314 * Sets the text for the tab (Note: this also sets the tooltip text)
38315 * @param {String} text The tab's text and tooltip
38317 setText : function(text){
38319 this.textEl.update(text);
38320 this.setTooltip(text);
38321 //if(!this.tabPanel.resizeTabs){
38322 // this.autoSize();
38326 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38328 activate : function(){
38329 this.tabPanel.activate(this.id);
38333 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38335 disable : function(){
38336 if(this.tabPanel.active != this){
38337 this.disabled = true;
38338 this.pnode.addClass("disabled");
38343 * Enables this TabPanelItem if it was previously disabled.
38345 enable : function(){
38346 this.disabled = false;
38347 this.pnode.removeClass("disabled");
38351 * Sets the content for this TabPanelItem.
38352 * @param {String} content The content
38353 * @param {Boolean} loadScripts true to look for and load scripts
38355 setContent : function(content, loadScripts){
38356 this.bodyEl.update(content, loadScripts);
38360 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38361 * @return {Roo.UpdateManager} The UpdateManager
38363 getUpdateManager : function(){
38364 return this.bodyEl.getUpdateManager();
38368 * Set a URL to be used to load the content for this TabPanelItem.
38369 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38370 * @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)
38371 * @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)
38372 * @return {Roo.UpdateManager} The UpdateManager
38374 setUrl : function(url, params, loadOnce){
38375 if(this.refreshDelegate){
38376 this.un('activate', this.refreshDelegate);
38378 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38379 this.on("activate", this.refreshDelegate);
38380 return this.bodyEl.getUpdateManager();
38384 _handleRefresh : function(url, params, loadOnce){
38385 if(!loadOnce || !this.loaded){
38386 var updater = this.bodyEl.getUpdateManager();
38387 updater.update(url, params, this._setLoaded.createDelegate(this));
38392 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38393 * Will fail silently if the setUrl method has not been called.
38394 * This does not activate the panel, just updates its content.
38396 refresh : function(){
38397 if(this.refreshDelegate){
38398 this.loaded = false;
38399 this.refreshDelegate();
38404 _setLoaded : function(){
38405 this.loaded = true;
38409 closeClick : function(e){
38412 this.fireEvent("beforeclose", this, o);
38413 if(o.cancel !== true){
38414 this.tabPanel.removeTab(this.id);
38418 * The text displayed in the tooltip for the close icon.
38421 closeText : "Close this tab"
38424 * This script refer to:
38425 * Title: International Telephone Input
38426 * Author: Jack O'Connor
38427 * Code version: v12.1.12
38428 * Availability: https://github.com/jackocnr/intl-tel-input.git
38431 Roo.bootstrap.PhoneInputData = function() {
38434 "Afghanistan (افغانستان)",
38439 "Albania (Shqipëri)",
38444 "Algeria (الجزائر)",
38469 "Antigua and Barbuda",
38479 "Armenia (Հայաստան)",
38495 "Austria (Österreich)",
38500 "Azerbaijan (Azərbaycan)",
38510 "Bahrain (البحرين)",
38515 "Bangladesh (বাংলাদেশ)",
38525 "Belarus (Беларусь)",
38530 "Belgium (België)",
38560 "Bosnia and Herzegovina (Босна и Херцеговина)",
38575 "British Indian Ocean Territory",
38580 "British Virgin Islands",
38590 "Bulgaria (България)",
38600 "Burundi (Uburundi)",
38605 "Cambodia (កម្ពុជា)",
38610 "Cameroon (Cameroun)",
38619 ["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"]
38622 "Cape Verde (Kabu Verdi)",
38627 "Caribbean Netherlands",
38638 "Central African Republic (République centrafricaine)",
38658 "Christmas Island",
38664 "Cocos (Keeling) Islands",
38675 "Comoros (جزر القمر)",
38680 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38685 "Congo (Republic) (Congo-Brazzaville)",
38705 "Croatia (Hrvatska)",
38726 "Czech Republic (Česká republika)",
38731 "Denmark (Danmark)",
38746 "Dominican Republic (República Dominicana)",
38750 ["809", "829", "849"]
38768 "Equatorial Guinea (Guinea Ecuatorial)",
38788 "Falkland Islands (Islas Malvinas)",
38793 "Faroe Islands (Føroyar)",
38814 "French Guiana (Guyane française)",
38819 "French Polynesia (Polynésie française)",
38834 "Georgia (საქართველო)",
38839 "Germany (Deutschland)",
38859 "Greenland (Kalaallit Nunaat)",
38896 "Guinea-Bissau (Guiné Bissau)",
38921 "Hungary (Magyarország)",
38926 "Iceland (Ísland)",
38946 "Iraq (العراق)",
38962 "Israel (ישראל)",
38989 "Jordan (الأردن)",
38994 "Kazakhstan (Казахстан)",
39015 "Kuwait (الكويت)",
39020 "Kyrgyzstan (Кыргызстан)",
39030 "Latvia (Latvija)",
39035 "Lebanon (لبنان)",
39050 "Libya (ليبيا)",
39060 "Lithuania (Lietuva)",
39075 "Macedonia (FYROM) (Македонија)",
39080 "Madagascar (Madagasikara)",
39110 "Marshall Islands",
39120 "Mauritania (موريتانيا)",
39125 "Mauritius (Moris)",
39146 "Moldova (Republica Moldova)",
39156 "Mongolia (Монгол)",
39161 "Montenegro (Crna Gora)",
39171 "Morocco (المغرب)",
39177 "Mozambique (Moçambique)",
39182 "Myanmar (Burma) (မြန်မာ)",
39187 "Namibia (Namibië)",
39202 "Netherlands (Nederland)",
39207 "New Caledonia (Nouvelle-Calédonie)",
39242 "North Korea (조선 민주주의 인민 공화국)",
39247 "Northern Mariana Islands",
39263 "Pakistan (پاکستان)",
39273 "Palestine (فلسطين)",
39283 "Papua New Guinea",
39325 "Réunion (La Réunion)",
39331 "Romania (România)",
39347 "Saint Barthélemy",
39358 "Saint Kitts and Nevis",
39368 "Saint Martin (Saint-Martin (partie française))",
39374 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39379 "Saint Vincent and the Grenadines",
39394 "São Tomé and Príncipe (São Tomé e Príncipe)",
39399 "Saudi Arabia (المملكة العربية السعودية)",
39404 "Senegal (Sénégal)",
39434 "Slovakia (Slovensko)",
39439 "Slovenia (Slovenija)",
39449 "Somalia (Soomaaliya)",
39459 "South Korea (대한민국)",
39464 "South Sudan (جنوب السودان)",
39474 "Sri Lanka (ශ්රී ලංකාව)",
39479 "Sudan (السودان)",
39489 "Svalbard and Jan Mayen",
39500 "Sweden (Sverige)",
39505 "Switzerland (Schweiz)",
39510 "Syria (سوريا)",
39555 "Trinidad and Tobago",
39560 "Tunisia (تونس)",
39565 "Turkey (Türkiye)",
39575 "Turks and Caicos Islands",
39585 "U.S. Virgin Islands",
39595 "Ukraine (Україна)",
39600 "United Arab Emirates (الإمارات العربية المتحدة)",
39622 "Uzbekistan (Oʻzbekiston)",
39632 "Vatican City (Città del Vaticano)",
39643 "Vietnam (Việt Nam)",
39648 "Wallis and Futuna (Wallis-et-Futuna)",
39653 "Western Sahara (الصحراء الغربية)",
39659 "Yemen (اليمن)",
39683 * This script refer to:
39684 * Title: International Telephone Input
39685 * Author: Jack O'Connor
39686 * Code version: v12.1.12
39687 * Availability: https://github.com/jackocnr/intl-tel-input.git
39691 * @class Roo.bootstrap.PhoneInput
39692 * @extends Roo.bootstrap.TriggerField
39693 * An input with International dial-code selection
39695 * @cfg {String} defaultDialCode default '+852'
39696 * @cfg {Array} preferedCountries default []
39699 * Create a new PhoneInput.
39700 * @param {Object} config Configuration options
39703 Roo.bootstrap.PhoneInput = function(config) {
39704 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39707 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39709 listWidth: undefined,
39711 selectedClass: 'active',
39713 invalidClass : "has-warning",
39715 validClass: 'has-success',
39717 allowed: '0123456789',
39720 * @cfg {String} defaultDialCode The default dial code when initializing the input
39722 defaultDialCode: '+852',
39725 * @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
39727 preferedCountries: false,
39729 getAutoCreate : function()
39731 var data = Roo.bootstrap.PhoneInputData();
39732 var align = this.labelAlign || this.parentLabelAlign();
39735 this.allCountries = [];
39736 this.dialCodeMapping = [];
39738 for (var i = 0; i < data.length; i++) {
39740 this.allCountries[i] = {
39744 priority: c[3] || 0,
39745 areaCodes: c[4] || null
39747 this.dialCodeMapping[c[2]] = {
39750 priority: c[3] || 0,
39751 areaCodes: c[4] || null
39763 cls : 'form-control tel-input',
39764 autocomplete: 'new-password'
39767 var hiddenInput = {
39770 cls: 'hidden-tel-input'
39774 hiddenInput.name = this.name;
39777 if (this.disabled) {
39778 input.disabled = true;
39781 var flag_container = {
39798 cls: this.hasFeedback ? 'has-feedback' : '',
39804 cls: 'dial-code-holder',
39811 cls: 'roo-select2-container input-group',
39818 if (this.fieldLabel.length) {
39821 tooltip: 'This field is required'
39827 cls: 'control-label',
39833 html: this.fieldLabel
39836 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39842 if(this.indicatorpos == 'right') {
39843 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39850 if(align == 'left') {
39858 if(this.labelWidth > 12){
39859 label.style = "width: " + this.labelWidth + 'px';
39861 if(this.labelWidth < 13 && this.labelmd == 0){
39862 this.labelmd = this.labelWidth;
39864 if(this.labellg > 0){
39865 label.cls += ' col-lg-' + this.labellg;
39866 input.cls += ' col-lg-' + (12 - this.labellg);
39868 if(this.labelmd > 0){
39869 label.cls += ' col-md-' + this.labelmd;
39870 container.cls += ' col-md-' + (12 - this.labelmd);
39872 if(this.labelsm > 0){
39873 label.cls += ' col-sm-' + this.labelsm;
39874 container.cls += ' col-sm-' + (12 - this.labelsm);
39876 if(this.labelxs > 0){
39877 label.cls += ' col-xs-' + this.labelxs;
39878 container.cls += ' col-xs-' + (12 - this.labelxs);
39888 var settings = this;
39890 ['xs','sm','md','lg'].map(function(size){
39891 if (settings[size]) {
39892 cfg.cls += ' col-' + size + '-' + settings[size];
39896 this.store = new Roo.data.Store({
39897 proxy : new Roo.data.MemoryProxy({}),
39898 reader : new Roo.data.JsonReader({
39909 'name' : 'dialCode',
39913 'name' : 'priority',
39917 'name' : 'areaCodes',
39924 if(!this.preferedCountries) {
39925 this.preferedCountries = [
39932 var p = this.preferedCountries.reverse();
39935 for (var i = 0; i < p.length; i++) {
39936 for (var j = 0; j < this.allCountries.length; j++) {
39937 if(this.allCountries[j].iso2 == p[i]) {
39938 var t = this.allCountries[j];
39939 this.allCountries.splice(j,1);
39940 this.allCountries.unshift(t);
39946 this.store.proxy.data = {
39948 data: this.allCountries
39954 initEvents : function()
39957 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39959 this.indicator = this.indicatorEl();
39960 this.flag = this.flagEl();
39961 this.dialCodeHolder = this.dialCodeHolderEl();
39963 this.trigger = this.el.select('div.flag-box',true).first();
39964 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39969 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39970 _this.list.setWidth(lw);
39973 this.list.on('mouseover', this.onViewOver, this);
39974 this.list.on('mousemove', this.onViewMove, this);
39975 this.inputEl().on("keyup", this.onKeyUp, this);
39977 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39979 this.view = new Roo.View(this.list, this.tpl, {
39980 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39983 this.view.on('click', this.onViewClick, this);
39984 this.setValue(this.defaultDialCode);
39987 onTriggerClick : function(e)
39989 Roo.log('trigger click');
39994 if(this.isExpanded()){
39996 this.hasFocus = false;
39998 this.store.load({});
39999 this.hasFocus = true;
40004 isExpanded : function()
40006 return this.list.isVisible();
40009 collapse : function()
40011 if(!this.isExpanded()){
40015 Roo.get(document).un('mousedown', this.collapseIf, this);
40016 Roo.get(document).un('mousewheel', this.collapseIf, this);
40017 this.fireEvent('collapse', this);
40021 expand : function()
40025 if(this.isExpanded() || !this.hasFocus){
40029 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40030 this.list.setWidth(lw);
40033 this.restrictHeight();
40035 Roo.get(document).on('mousedown', this.collapseIf, this);
40036 Roo.get(document).on('mousewheel', this.collapseIf, this);
40038 this.fireEvent('expand', this);
40041 restrictHeight : function()
40043 this.list.alignTo(this.inputEl(), this.listAlign);
40044 this.list.alignTo(this.inputEl(), this.listAlign);
40047 onViewOver : function(e, t)
40049 if(this.inKeyMode){
40052 var item = this.view.findItemFromChild(t);
40055 var index = this.view.indexOf(item);
40056 this.select(index, false);
40061 onViewClick : function(view, doFocus, el, e)
40063 var index = this.view.getSelectedIndexes()[0];
40065 var r = this.store.getAt(index);
40068 this.onSelect(r, index);
40070 if(doFocus !== false && !this.blockFocus){
40071 this.inputEl().focus();
40075 onViewMove : function(e, t)
40077 this.inKeyMode = false;
40080 select : function(index, scrollIntoView)
40082 this.selectedIndex = index;
40083 this.view.select(index);
40084 if(scrollIntoView !== false){
40085 var el = this.view.getNode(index);
40087 this.list.scrollChildIntoView(el, false);
40092 createList : function()
40094 this.list = Roo.get(document.body).createChild({
40096 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40097 style: 'display:none'
40100 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40103 collapseIf : function(e)
40105 var in_combo = e.within(this.el);
40106 var in_list = e.within(this.list);
40107 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40109 if (in_combo || in_list || is_list) {
40115 onSelect : function(record, index)
40117 if(this.fireEvent('beforeselect', this, record, index) !== false){
40119 this.setFlagClass(record.data.iso2);
40120 this.setDialCode(record.data.dialCode);
40121 this.hasFocus = false;
40123 this.fireEvent('select', this, record, index);
40127 flagEl : function()
40129 var flag = this.el.select('div.flag',true).first();
40136 dialCodeHolderEl : function()
40138 var d = this.el.select('input.dial-code-holder',true).first();
40145 setDialCode : function(v)
40147 this.dialCodeHolder.dom.value = '+'+v;
40150 setFlagClass : function(n)
40152 this.flag.dom.className = 'flag '+n;
40155 getValue : function()
40157 var v = this.inputEl().getValue();
40158 if(this.dialCodeHolder) {
40159 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40164 setValue : function(v)
40166 var d = this.getDialCode(v);
40168 //invalid dial code
40169 if(v.length == 0 || !d || d.length == 0) {
40171 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40172 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40178 this.setFlagClass(this.dialCodeMapping[d].iso2);
40179 this.setDialCode(d);
40180 this.inputEl().dom.value = v.replace('+'+d,'');
40181 this.hiddenEl().dom.value = this.getValue();
40186 getDialCode : function(v)
40190 if (v.length == 0) {
40191 return this.dialCodeHolder.dom.value;
40195 if (v.charAt(0) != "+") {
40198 var numericChars = "";
40199 for (var i = 1; i < v.length; i++) {
40200 var c = v.charAt(i);
40203 if (this.dialCodeMapping[numericChars]) {
40204 dialCode = v.substr(1, i);
40206 if (numericChars.length == 4) {
40216 this.setValue(this.defaultDialCode);
40220 hiddenEl : function()
40222 return this.el.select('input.hidden-tel-input',true).first();
40225 onKeyUp : function(e){
40227 var k = e.getKey();
40228 var c = e.getCharCode();
40231 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40232 this.allowed.indexOf(String.fromCharCode(c)) === -1
40237 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40240 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40244 this.setValue(this.getValue());
40249 * @class Roo.bootstrap.MoneyField
40250 * @extends Roo.bootstrap.ComboBox
40251 * Bootstrap MoneyField class
40254 * Create a new MoneyField.
40255 * @param {Object} config Configuration options
40258 Roo.bootstrap.MoneyField = function(config) {
40260 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40264 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40267 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40269 allowDecimals : true,
40271 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40273 decimalSeparator : ".",
40275 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40277 decimalPrecision : 0,
40279 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40281 allowNegative : true,
40283 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40287 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40289 minValue : Number.NEGATIVE_INFINITY,
40291 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40293 maxValue : Number.MAX_VALUE,
40295 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40297 minText : "The minimum value for this field is {0}",
40299 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40301 maxText : "The maximum value for this field is {0}",
40303 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40304 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40306 nanText : "{0} is not a valid number",
40308 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40312 * @cfg {String} defaults currency of the MoneyField
40313 * value should be in lkey
40315 defaultCurrency : false,
40317 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40319 thousandsDelimiter : false,
40329 getAutoCreate : function()
40331 var align = this.labelAlign || this.parentLabelAlign();
40343 cls : 'form-control roo-money-amount-input',
40344 autocomplete: 'new-password'
40347 var hiddenInput = {
40351 cls: 'hidden-number-input'
40355 hiddenInput.name = this.name;
40358 if (this.disabled) {
40359 input.disabled = true;
40362 var clg = 12 - this.inputlg;
40363 var cmd = 12 - this.inputmd;
40364 var csm = 12 - this.inputsm;
40365 var cxs = 12 - this.inputxs;
40369 cls : 'row roo-money-field',
40373 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40377 cls: 'roo-select2-container input-group',
40381 cls : 'form-control roo-money-currency-input',
40382 autocomplete: 'new-password',
40384 name : this.currencyName
40388 cls : 'input-group-addon',
40402 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40406 cls: this.hasFeedback ? 'has-feedback' : '',
40417 if (this.fieldLabel.length) {
40420 tooltip: 'This field is required'
40426 cls: 'control-label',
40432 html: this.fieldLabel
40435 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40441 if(this.indicatorpos == 'right') {
40442 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40449 if(align == 'left') {
40457 if(this.labelWidth > 12){
40458 label.style = "width: " + this.labelWidth + 'px';
40460 if(this.labelWidth < 13 && this.labelmd == 0){
40461 this.labelmd = this.labelWidth;
40463 if(this.labellg > 0){
40464 label.cls += ' col-lg-' + this.labellg;
40465 input.cls += ' col-lg-' + (12 - this.labellg);
40467 if(this.labelmd > 0){
40468 label.cls += ' col-md-' + this.labelmd;
40469 container.cls += ' col-md-' + (12 - this.labelmd);
40471 if(this.labelsm > 0){
40472 label.cls += ' col-sm-' + this.labelsm;
40473 container.cls += ' col-sm-' + (12 - this.labelsm);
40475 if(this.labelxs > 0){
40476 label.cls += ' col-xs-' + this.labelxs;
40477 container.cls += ' col-xs-' + (12 - this.labelxs);
40488 var settings = this;
40490 ['xs','sm','md','lg'].map(function(size){
40491 if (settings[size]) {
40492 cfg.cls += ' col-' + size + '-' + settings[size];
40499 initEvents : function()
40501 this.indicator = this.indicatorEl();
40503 this.initCurrencyEvent();
40505 this.initNumberEvent();
40508 initCurrencyEvent : function()
40511 throw "can not find store for combo";
40514 this.store = Roo.factory(this.store, Roo.data);
40515 this.store.parent = this;
40519 this.triggerEl = this.el.select('.input-group-addon', true).first();
40521 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40526 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40527 _this.list.setWidth(lw);
40530 this.list.on('mouseover', this.onViewOver, this);
40531 this.list.on('mousemove', this.onViewMove, this);
40532 this.list.on('scroll', this.onViewScroll, this);
40535 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40538 this.view = new Roo.View(this.list, this.tpl, {
40539 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40542 this.view.on('click', this.onViewClick, this);
40544 this.store.on('beforeload', this.onBeforeLoad, this);
40545 this.store.on('load', this.onLoad, this);
40546 this.store.on('loadexception', this.onLoadException, this);
40548 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40549 "up" : function(e){
40550 this.inKeyMode = true;
40554 "down" : function(e){
40555 if(!this.isExpanded()){
40556 this.onTriggerClick();
40558 this.inKeyMode = true;
40563 "enter" : function(e){
40566 if(this.fireEvent("specialkey", this, e)){
40567 this.onViewClick(false);
40573 "esc" : function(e){
40577 "tab" : function(e){
40580 if(this.fireEvent("specialkey", this, e)){
40581 this.onViewClick(false);
40589 doRelay : function(foo, bar, hname){
40590 if(hname == 'down' || this.scope.isExpanded()){
40591 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40599 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40603 initNumberEvent : function(e)
40605 this.inputEl().on("keydown" , this.fireKey, this);
40606 this.inputEl().on("focus", this.onFocus, this);
40607 this.inputEl().on("blur", this.onBlur, this);
40609 this.inputEl().relayEvent('keyup', this);
40611 if(this.indicator){
40612 this.indicator.addClass('invisible');
40615 this.originalValue = this.getValue();
40617 if(this.validationEvent == 'keyup'){
40618 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40619 this.inputEl().on('keyup', this.filterValidation, this);
40621 else if(this.validationEvent !== false){
40622 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40625 if(this.selectOnFocus){
40626 this.on("focus", this.preFocus, this);
40629 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40630 this.inputEl().on("keypress", this.filterKeys, this);
40632 this.inputEl().relayEvent('keypress', this);
40635 var allowed = "0123456789";
40637 if(this.allowDecimals){
40638 allowed += this.decimalSeparator;
40641 if(this.allowNegative){
40645 if(this.thousandsDelimiter) {
40649 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40651 var keyPress = function(e){
40653 var k = e.getKey();
40655 var c = e.getCharCode();
40658 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40659 allowed.indexOf(String.fromCharCode(c)) === -1
40665 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40669 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40674 this.inputEl().on("keypress", keyPress, this);
40678 onTriggerClick : function(e)
40685 this.loadNext = false;
40687 if(this.isExpanded()){
40692 this.hasFocus = true;
40694 if(this.triggerAction == 'all') {
40695 this.doQuery(this.allQuery, true);
40699 this.doQuery(this.getRawValue());
40702 getCurrency : function()
40704 var v = this.currencyEl().getValue();
40709 restrictHeight : function()
40711 this.list.alignTo(this.currencyEl(), this.listAlign);
40712 this.list.alignTo(this.currencyEl(), this.listAlign);
40715 onViewClick : function(view, doFocus, el, e)
40717 var index = this.view.getSelectedIndexes()[0];
40719 var r = this.store.getAt(index);
40722 this.onSelect(r, index);
40726 onSelect : function(record, index){
40728 if(this.fireEvent('beforeselect', this, record, index) !== false){
40730 this.setFromCurrencyData(index > -1 ? record.data : false);
40734 this.fireEvent('select', this, record, index);
40738 setFromCurrencyData : function(o)
40742 this.lastCurrency = o;
40744 if (this.currencyField) {
40745 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40747 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40750 this.lastSelectionText = currency;
40752 //setting default currency
40753 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40754 this.setCurrency(this.defaultCurrency);
40758 this.setCurrency(currency);
40761 setFromData : function(o)
40765 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40767 this.setFromCurrencyData(c);
40772 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40774 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40777 this.setValue(value);
40781 setCurrency : function(v)
40783 this.currencyValue = v;
40786 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40791 setValue : function(v)
40793 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40799 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40801 this.inputEl().dom.value = (v == '') ? '' :
40802 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40804 if(!this.allowZero && v === '0') {
40805 this.hiddenEl().dom.value = '';
40806 this.inputEl().dom.value = '';
40813 getRawValue : function()
40815 var v = this.inputEl().getValue();
40820 getValue : function()
40822 return this.fixPrecision(this.parseValue(this.getRawValue()));
40825 parseValue : function(value)
40827 if(this.thousandsDelimiter) {
40829 r = new RegExp(",", "g");
40830 value = value.replace(r, "");
40833 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40834 return isNaN(value) ? '' : value;
40838 fixPrecision : function(value)
40840 if(this.thousandsDelimiter) {
40842 r = new RegExp(",", "g");
40843 value = value.replace(r, "");
40846 var nan = isNaN(value);
40848 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40849 return nan ? '' : value;
40851 return parseFloat(value).toFixed(this.decimalPrecision);
40854 decimalPrecisionFcn : function(v)
40856 return Math.floor(v);
40859 validateValue : function(value)
40861 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40865 var num = this.parseValue(value);
40868 this.markInvalid(String.format(this.nanText, value));
40872 if(num < this.minValue){
40873 this.markInvalid(String.format(this.minText, this.minValue));
40877 if(num > this.maxValue){
40878 this.markInvalid(String.format(this.maxText, this.maxValue));
40885 validate : function()
40887 if(this.disabled || this.allowBlank){
40892 var currency = this.getCurrency();
40894 if(this.validateValue(this.getRawValue()) && currency.length){
40899 this.markInvalid();
40903 getName: function()
40908 beforeBlur : function()
40914 var v = this.parseValue(this.getRawValue());
40921 onBlur : function()
40925 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40926 //this.el.removeClass(this.focusClass);
40929 this.hasFocus = false;
40931 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40935 var v = this.getValue();
40937 if(String(v) !== String(this.startValue)){
40938 this.fireEvent('change', this, v, this.startValue);
40941 this.fireEvent("blur", this);
40944 inputEl : function()
40946 return this.el.select('.roo-money-amount-input', true).first();
40949 currencyEl : function()
40951 return this.el.select('.roo-money-currency-input', true).first();
40954 hiddenEl : function()
40956 return this.el.select('input.hidden-number-input',true).first();