4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2651 onRender : function(ct, position)
2653 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2656 var cfg = Roo.apply({}, this.getAutoCreate());
2659 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2661 //if (!cfg.name.length) {
2665 cfg.cls += ' ' + this.cls;
2668 cfg.style = this.style;
2670 this.el = Roo.get(document.body).createChild(cfg, position);
2672 //var type = this.el.dom.type;
2675 if(this.tabIndex !== undefined){
2676 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2679 this.dialogEl = this.el.select('.modal-dialog',true).first();
2680 this.bodyEl = this.el.select('.modal-body',true).first();
2681 this.closeEl = this.el.select('.modal-header .close', true).first();
2682 this.headerEl = this.el.select('.modal-header',true).first();
2683 this.titleEl = this.el.select('.modal-title',true).first();
2684 this.footerEl = this.el.select('.modal-footer',true).first();
2686 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2688 //this.el.addClass("x-dlg-modal");
2690 if (this.buttons.length) {
2691 Roo.each(this.buttons, function(bb) {
2692 var b = Roo.apply({}, bb);
2693 b.xns = b.xns || Roo.bootstrap;
2694 b.xtype = b.xtype || 'Button';
2695 if (typeof(b.listeners) == 'undefined') {
2696 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2699 var btn = Roo.factory(b);
2701 btn.render(this.el.select('.modal-footer div').first());
2705 // render the children.
2708 if(typeof(this.items) != 'undefined'){
2709 var items = this.items;
2712 for(var i =0;i < items.length;i++) {
2713 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2717 this.items = nitems;
2719 // where are these used - they used to be body/close/footer
2723 //this.el.addClass([this.fieldClass, this.cls]);
2727 getAutoCreate : function()
2731 html : this.html || ''
2736 cls : 'modal-title',
2740 if(this.specificTitle){
2746 if (this.allow_close) {
2758 if(this.size.length){
2759 size = 'modal-' + this.size;
2766 cls: "modal-dialog " + size,
2769 cls : "modal-content",
2772 cls : 'modal-header',
2777 cls : 'modal-footer',
2781 cls: 'btn-' + this.buttonPosition
2798 modal.cls += ' fade';
2804 getChildContainer : function() {
2809 getButtonContainer : function() {
2810 return this.el.select('.modal-footer div',true).first();
2813 initEvents : function()
2815 if (this.allow_close) {
2816 this.closeEl.on('click', this.hide, this);
2818 Roo.EventManager.onWindowResize(this.resize, this, true);
2825 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2827 if (this.fitwindow) {
2828 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2829 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2833 if(!this.fitwindow && this.max_width !== 0){
2835 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2839 this.setSize(w, this.height);
2843 if(!this.fit_content) {
2844 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2848 var body_childs = this.bodyEl.dom.childNodes;
2849 var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2850 for(var i = 0; i < body_childs.length; i++) {
2852 // if(body_childs[i].classList.indexOf('roo-layout-region') * 1 != -1) {
2853 // var layout_childs = body_childs[i].childNodes;
2854 // for(var j = 0; j < layout_childs.length; j++) {
2859 full_height += body_childs[i].offsetHeight;
2862 Roo.log(full_height);
2864 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2869 setSize : function(w,h)
2879 if (!this.rendered) {
2883 //this.el.setStyle('display', 'block');
2884 this.el.removeClass('hideing');
2885 this.el.addClass('show');
2887 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2890 this.el.addClass('in');
2893 this.el.addClass('in');
2896 // not sure how we can show data in here..
2898 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2901 Roo.get(document.body).addClass("x-body-masked");
2903 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2904 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2905 this.maskEl.addClass('show');
2909 this.fireEvent('show', this);
2911 // set zindex here - otherwise it appears to be ignored...
2912 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2915 this.items.forEach( function(e) {
2916 e.layout ? e.layout() : false;
2924 if(this.fireEvent("beforehide", this) !== false){
2925 this.maskEl.removeClass('show');
2926 Roo.get(document.body).removeClass("x-body-masked");
2927 this.el.removeClass('in');
2928 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2930 if(this.animate){ // why
2931 this.el.addClass('hideing');
2933 if (!this.el.hasClass('hideing')) {
2934 return; // it's been shown again...
2936 this.el.removeClass('show');
2937 this.el.removeClass('hideing');
2941 this.el.removeClass('show');
2943 this.fireEvent('hide', this);
2946 isVisible : function()
2949 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2953 addButton : function(str, cb)
2957 var b = Roo.apply({}, { html : str } );
2958 b.xns = b.xns || Roo.bootstrap;
2959 b.xtype = b.xtype || 'Button';
2960 if (typeof(b.listeners) == 'undefined') {
2961 b.listeners = { click : cb.createDelegate(this) };
2964 var btn = Roo.factory(b);
2966 btn.render(this.el.select('.modal-footer div').first());
2972 setDefaultButton : function(btn)
2974 //this.el.select('.modal-footer').()
2978 resizeTo: function(w,h)
2982 this.dialogEl.setWidth(w);
2983 if (this.diff === false) {
2984 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2987 this.bodyEl.setHeight(h-this.diff);
2989 this.fireEvent('resize', this);
2992 setContentSize : function(w, h)
2996 onButtonClick: function(btn,e)
2999 this.fireEvent('btnclick', btn.name, e);
3002 * Set the title of the Dialog
3003 * @param {String} str new Title
3005 setTitle: function(str) {
3006 this.titleEl.dom.innerHTML = str;
3009 * Set the body of the Dialog
3010 * @param {String} str new Title
3012 setBody: function(str) {
3013 this.bodyEl.dom.innerHTML = str;
3016 * Set the body of the Dialog using the template
3017 * @param {Obj} data - apply this data to the template and replace the body contents.
3019 applyBody: function(obj)
3022 Roo.log("Error - using apply Body without a template");
3025 this.tmpl.overwrite(this.bodyEl, obj);
3031 Roo.apply(Roo.bootstrap.Modal, {
3033 * Button config that displays a single OK button
3042 * Button config that displays Yes and No buttons
3058 * Button config that displays OK and Cancel buttons
3073 * Button config that displays Yes, No and Cancel buttons
3097 * messagebox - can be used as a replace
3101 * @class Roo.MessageBox
3102 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3106 Roo.Msg.alert('Status', 'Changes saved successfully.');
3108 // Prompt for user data:
3109 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3111 // process text value...
3115 // Show a dialog using config options:
3117 title:'Save Changes?',
3118 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3119 buttons: Roo.Msg.YESNOCANCEL,
3126 Roo.bootstrap.MessageBox = function(){
3127 var dlg, opt, mask, waitTimer;
3128 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3129 var buttons, activeTextEl, bwidth;
3133 var handleButton = function(button){
3135 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3139 var handleHide = function(){
3141 dlg.el.removeClass(opt.cls);
3144 // Roo.TaskMgr.stop(waitTimer);
3145 // waitTimer = null;
3150 var updateButtons = function(b){
3153 buttons["ok"].hide();
3154 buttons["cancel"].hide();
3155 buttons["yes"].hide();
3156 buttons["no"].hide();
3157 //dlg.footer.dom.style.display = 'none';
3160 dlg.footerEl.dom.style.display = '';
3161 for(var k in buttons){
3162 if(typeof buttons[k] != "function"){
3165 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3166 width += buttons[k].el.getWidth()+15;
3176 var handleEsc = function(d, k, e){
3177 if(opt && opt.closable !== false){
3187 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3188 * @return {Roo.BasicDialog} The BasicDialog element
3190 getDialog : function(){
3192 dlg = new Roo.bootstrap.Modal( {
3195 //constraintoviewport:false,
3197 //collapsible : false,
3202 //buttonAlign:"center",
3203 closeClick : function(){
3204 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3207 handleButton("cancel");
3212 dlg.on("hide", handleHide);
3214 //dlg.addKeyListener(27, handleEsc);
3216 this.buttons = buttons;
3217 var bt = this.buttonText;
3218 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3219 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3220 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3221 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3223 bodyEl = dlg.bodyEl.createChild({
3225 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3226 '<textarea class="roo-mb-textarea"></textarea>' +
3227 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3229 msgEl = bodyEl.dom.firstChild;
3230 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3231 textboxEl.enableDisplayMode();
3232 textboxEl.addKeyListener([10,13], function(){
3233 if(dlg.isVisible() && opt && opt.buttons){
3236 }else if(opt.buttons.yes){
3237 handleButton("yes");
3241 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3242 textareaEl.enableDisplayMode();
3243 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3244 progressEl.enableDisplayMode();
3246 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3247 var pf = progressEl.dom.firstChild;
3249 pp = Roo.get(pf.firstChild);
3250 pp.setHeight(pf.offsetHeight);
3258 * Updates the message box body text
3259 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3260 * the XHTML-compliant non-breaking space character '&#160;')
3261 * @return {Roo.MessageBox} This message box
3263 updateText : function(text)
3265 if(!dlg.isVisible() && !opt.width){
3266 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3267 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3269 msgEl.innerHTML = text || ' ';
3271 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3272 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3274 Math.min(opt.width || cw , this.maxWidth),
3275 Math.max(opt.minWidth || this.minWidth, bwidth)
3278 activeTextEl.setWidth(w);
3280 if(dlg.isVisible()){
3281 dlg.fixedcenter = false;
3283 // to big, make it scroll. = But as usual stupid IE does not support
3286 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3287 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3288 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3290 bodyEl.dom.style.height = '';
3291 bodyEl.dom.style.overflowY = '';
3294 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3296 bodyEl.dom.style.overflowX = '';
3299 dlg.setContentSize(w, bodyEl.getHeight());
3300 if(dlg.isVisible()){
3301 dlg.fixedcenter = true;
3307 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3308 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3309 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3310 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3311 * @return {Roo.MessageBox} This message box
3313 updateProgress : function(value, text){
3315 this.updateText(text);
3318 if (pp) { // weird bug on my firefox - for some reason this is not defined
3319 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3320 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3326 * Returns true if the message box is currently displayed
3327 * @return {Boolean} True if the message box is visible, else false
3329 isVisible : function(){
3330 return dlg && dlg.isVisible();
3334 * Hides the message box if it is displayed
3337 if(this.isVisible()){
3343 * Displays a new message box, or reinitializes an existing message box, based on the config options
3344 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3345 * The following config object properties are supported:
3347 Property Type Description
3348 ---------- --------------- ------------------------------------------------------------------------------------
3349 animEl String/Element An id or Element from which the message box should animate as it opens and
3350 closes (defaults to undefined)
3351 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3352 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3353 closable Boolean False to hide the top-right close button (defaults to true). Note that
3354 progress and wait dialogs will ignore this property and always hide the
3355 close button as they can only be closed programmatically.
3356 cls String A custom CSS class to apply to the message box element
3357 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3358 displayed (defaults to 75)
3359 fn Function A callback function to execute after closing the dialog. The arguments to the
3360 function will be btn (the name of the button that was clicked, if applicable,
3361 e.g. "ok"), and text (the value of the active text field, if applicable).
3362 Progress and wait dialogs will ignore this option since they do not respond to
3363 user actions and can only be closed programmatically, so any required function
3364 should be called by the same code after it closes the dialog.
3365 icon String A CSS class that provides a background image to be used as an icon for
3366 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3367 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3368 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3369 modal Boolean False to allow user interaction with the page while the message box is
3370 displayed (defaults to true)
3371 msg String A string that will replace the existing message box body text (defaults
3372 to the XHTML-compliant non-breaking space character ' ')
3373 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3374 progress Boolean True to display a progress bar (defaults to false)
3375 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3376 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3377 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3378 title String The title text
3379 value String The string value to set into the active textbox element if displayed
3380 wait Boolean True to display a progress bar (defaults to false)
3381 width Number The width of the dialog in pixels
3388 msg: 'Please enter your address:',
3390 buttons: Roo.MessageBox.OKCANCEL,
3393 animEl: 'addAddressBtn'
3396 * @param {Object} config Configuration options
3397 * @return {Roo.MessageBox} This message box
3399 show : function(options)
3402 // this causes nightmares if you show one dialog after another
3403 // especially on callbacks..
3405 if(this.isVisible()){
3408 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3409 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3410 Roo.log("New Dialog Message:" + options.msg )
3411 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3412 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3415 var d = this.getDialog();
3417 d.setTitle(opt.title || " ");
3418 d.closeEl.setDisplayed(opt.closable !== false);
3419 activeTextEl = textboxEl;
3420 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3425 textareaEl.setHeight(typeof opt.multiline == "number" ?
3426 opt.multiline : this.defaultTextHeight);
3427 activeTextEl = textareaEl;
3436 progressEl.setDisplayed(opt.progress === true);
3437 this.updateProgress(0);
3438 activeTextEl.dom.value = opt.value || "";
3440 dlg.setDefaultButton(activeTextEl);
3442 var bs = opt.buttons;
3446 }else if(bs && bs.yes){
3447 db = buttons["yes"];
3449 dlg.setDefaultButton(db);
3451 bwidth = updateButtons(opt.buttons);
3452 this.updateText(opt.msg);
3454 d.el.addClass(opt.cls);
3456 d.proxyDrag = opt.proxyDrag === true;
3457 d.modal = opt.modal !== false;
3458 d.mask = opt.modal !== false ? mask : false;
3460 // force it to the end of the z-index stack so it gets a cursor in FF
3461 document.body.appendChild(dlg.el.dom);
3462 d.animateTarget = null;
3463 d.show(options.animEl);
3469 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3470 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3471 * and closing the message box when the process is complete.
3472 * @param {String} title The title bar text
3473 * @param {String} msg The message box body text
3474 * @return {Roo.MessageBox} This message box
3476 progress : function(title, msg){
3483 minWidth: this.minProgressWidth,
3490 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3491 * If a callback function is passed it will be called after the user clicks the button, and the
3492 * id of the button that was clicked will be passed as the only parameter to the callback
3493 * (could also be the top-right close button).
3494 * @param {String} title The title bar text
3495 * @param {String} msg The message box body text
3496 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3497 * @param {Object} scope (optional) The scope of the callback function
3498 * @return {Roo.MessageBox} This message box
3500 alert : function(title, msg, fn, scope)
3515 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3516 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3517 * You are responsible for closing the message box when the process is complete.
3518 * @param {String} msg The message box body text
3519 * @param {String} title (optional) The title bar text
3520 * @return {Roo.MessageBox} This message box
3522 wait : function(msg, title){
3533 waitTimer = Roo.TaskMgr.start({
3535 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3543 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3544 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3545 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3546 * @param {String} title The title bar text
3547 * @param {String} msg The message box body text
3548 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3549 * @param {Object} scope (optional) The scope of the callback function
3550 * @return {Roo.MessageBox} This message box
3552 confirm : function(title, msg, fn, scope){
3556 buttons: this.YESNO,
3565 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3566 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3567 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3568 * (could also be the top-right close button) and the text that was entered will be passed as the two
3569 * parameters to the callback.
3570 * @param {String} title The title bar text
3571 * @param {String} msg The message box body text
3572 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3573 * @param {Object} scope (optional) The scope of the callback function
3574 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3575 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3576 * @return {Roo.MessageBox} This message box
3578 prompt : function(title, msg, fn, scope, multiline){
3582 buttons: this.OKCANCEL,
3587 multiline: multiline,
3594 * Button config that displays a single OK button
3599 * Button config that displays Yes and No buttons
3602 YESNO : {yes:true, no:true},
3604 * Button config that displays OK and Cancel buttons
3607 OKCANCEL : {ok:true, cancel:true},
3609 * Button config that displays Yes, No and Cancel buttons
3612 YESNOCANCEL : {yes:true, no:true, cancel:true},
3615 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3618 defaultTextHeight : 75,
3620 * The maximum width in pixels of the message box (defaults to 600)
3625 * The minimum width in pixels of the message box (defaults to 100)
3630 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3631 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3634 minProgressWidth : 250,
3636 * An object containing the default button text strings that can be overriden for localized language support.
3637 * Supported properties are: ok, cancel, yes and no.
3638 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3651 * Shorthand for {@link Roo.MessageBox}
3653 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3654 Roo.Msg = Roo.Msg || Roo.MessageBox;
3663 * @class Roo.bootstrap.Navbar
3664 * @extends Roo.bootstrap.Component
3665 * Bootstrap Navbar class
3668 * Create a new Navbar
3669 * @param {Object} config The config object
3673 Roo.bootstrap.Navbar = function(config){
3674 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3678 * @event beforetoggle
3679 * Fire before toggle the menu
3680 * @param {Roo.EventObject} e
3682 "beforetoggle" : true
3686 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3695 getAutoCreate : function(){
3698 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3702 initEvents :function ()
3704 //Roo.log(this.el.select('.navbar-toggle',true));
3705 this.el.select('.navbar-toggle',true).on('click', function() {
3706 if(this.fireEvent('beforetoggle', this) !== false){
3707 this.el.select('.navbar-collapse',true).toggleClass('in');
3717 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3719 var size = this.el.getSize();
3720 this.maskEl.setSize(size.width, size.height);
3721 this.maskEl.enableDisplayMode("block");
3730 getChildContainer : function()
3732 if (this.el.select('.collapse').getCount()) {
3733 return this.el.select('.collapse',true).first();
3766 * @class Roo.bootstrap.NavSimplebar
3767 * @extends Roo.bootstrap.Navbar
3768 * Bootstrap Sidebar class
3770 * @cfg {Boolean} inverse is inverted color
3772 * @cfg {String} type (nav | pills | tabs)
3773 * @cfg {Boolean} arrangement stacked | justified
3774 * @cfg {String} align (left | right) alignment
3776 * @cfg {Boolean} main (true|false) main nav bar? default false
3777 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3779 * @cfg {String} tag (header|footer|nav|div) default is nav
3785 * Create a new Sidebar
3786 * @param {Object} config The config object
3790 Roo.bootstrap.NavSimplebar = function(config){
3791 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3794 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3810 getAutoCreate : function(){
3814 tag : this.tag || 'div',
3827 this.type = this.type || 'nav';
3828 if (['tabs','pills'].indexOf(this.type)!==-1) {
3829 cfg.cn[0].cls += ' nav-' + this.type
3833 if (this.type!=='nav') {
3834 Roo.log('nav type must be nav/tabs/pills')
3836 cfg.cn[0].cls += ' navbar-nav'
3842 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3843 cfg.cn[0].cls += ' nav-' + this.arrangement;
3847 if (this.align === 'right') {
3848 cfg.cn[0].cls += ' navbar-right';
3852 cfg.cls += ' navbar-inverse';
3879 * @class Roo.bootstrap.NavHeaderbar
3880 * @extends Roo.bootstrap.NavSimplebar
3881 * Bootstrap Sidebar class
3883 * @cfg {String} brand what is brand
3884 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3885 * @cfg {String} brand_href href of the brand
3886 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3887 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3888 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3889 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3892 * Create a new Sidebar
3893 * @param {Object} config The config object
3897 Roo.bootstrap.NavHeaderbar = function(config){
3898 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3902 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3909 desktopCenter : false,
3912 getAutoCreate : function(){
3915 tag: this.nav || 'nav',
3922 if (this.desktopCenter) {
3923 cn.push({cls : 'container', cn : []});
3930 cls: 'navbar-header',
3935 cls: 'navbar-toggle',
3936 'data-toggle': 'collapse',
3941 html: 'Toggle navigation'
3963 cls: 'collapse navbar-collapse',
3967 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3969 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3970 cfg.cls += ' navbar-' + this.position;
3972 // tag can override this..
3974 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3977 if (this.brand !== '') {
3980 href: this.brand_href ? this.brand_href : '#',
3981 cls: 'navbar-brand',
3989 cfg.cls += ' main-nav';
3997 getHeaderChildContainer : function()
3999 if (this.srButton && this.el.select('.navbar-header').getCount()) {
4000 return this.el.select('.navbar-header',true).first();
4003 return this.getChildContainer();
4007 initEvents : function()
4009 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4011 if (this.autohide) {
4016 Roo.get(document).on('scroll',function(e) {
4017 var ns = Roo.get(document).getScroll().top;
4018 var os = prevScroll;
4022 ft.removeClass('slideDown');
4023 ft.addClass('slideUp');
4026 ft.removeClass('slideUp');
4027 ft.addClass('slideDown');
4048 * @class Roo.bootstrap.NavSidebar
4049 * @extends Roo.bootstrap.Navbar
4050 * Bootstrap Sidebar class
4053 * Create a new Sidebar
4054 * @param {Object} config The config object
4058 Roo.bootstrap.NavSidebar = function(config){
4059 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4062 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4064 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4066 getAutoCreate : function(){
4071 cls: 'sidebar sidebar-nav'
4093 * @class Roo.bootstrap.NavGroup
4094 * @extends Roo.bootstrap.Component
4095 * Bootstrap NavGroup class
4096 * @cfg {String} align (left|right)
4097 * @cfg {Boolean} inverse
4098 * @cfg {String} type (nav|pills|tab) default nav
4099 * @cfg {String} navId - reference Id for navbar.
4103 * Create a new nav group
4104 * @param {Object} config The config object
4107 Roo.bootstrap.NavGroup = function(config){
4108 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4111 Roo.bootstrap.NavGroup.register(this);
4115 * Fires when the active item changes
4116 * @param {Roo.bootstrap.NavGroup} this
4117 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4118 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4125 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4136 getAutoCreate : function()
4138 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4145 if (['tabs','pills'].indexOf(this.type)!==-1) {
4146 cfg.cls += ' nav-' + this.type
4148 if (this.type!=='nav') {
4149 Roo.log('nav type must be nav/tabs/pills')
4151 cfg.cls += ' navbar-nav'
4154 if (this.parent() && this.parent().sidebar) {
4157 cls: 'dashboard-menu sidebar-menu'
4163 if (this.form === true) {
4169 if (this.align === 'right') {
4170 cfg.cls += ' navbar-right';
4172 cfg.cls += ' navbar-left';
4176 if (this.align === 'right') {
4177 cfg.cls += ' navbar-right';
4181 cfg.cls += ' navbar-inverse';
4189 * sets the active Navigation item
4190 * @param {Roo.bootstrap.NavItem} the new current navitem
4192 setActiveItem : function(item)
4195 Roo.each(this.navItems, function(v){
4200 v.setActive(false, true);
4207 item.setActive(true, true);
4208 this.fireEvent('changed', this, item, prev);
4213 * gets the active Navigation item
4214 * @return {Roo.bootstrap.NavItem} the current navitem
4216 getActive : function()
4220 Roo.each(this.navItems, function(v){
4231 indexOfNav : function()
4235 Roo.each(this.navItems, function(v,i){
4246 * adds a Navigation item
4247 * @param {Roo.bootstrap.NavItem} the navitem to add
4249 addItem : function(cfg)
4251 var cn = new Roo.bootstrap.NavItem(cfg);
4253 cn.parentId = this.id;
4254 cn.onRender(this.el, null);
4258 * register a Navigation item
4259 * @param {Roo.bootstrap.NavItem} the navitem to add
4261 register : function(item)
4263 this.navItems.push( item);
4264 item.navId = this.navId;
4269 * clear all the Navigation item
4272 clearAll : function()
4275 this.el.dom.innerHTML = '';
4278 getNavItem: function(tabId)
4281 Roo.each(this.navItems, function(e) {
4282 if (e.tabId == tabId) {
4292 setActiveNext : function()
4294 var i = this.indexOfNav(this.getActive());
4295 if (i > this.navItems.length) {
4298 this.setActiveItem(this.navItems[i+1]);
4300 setActivePrev : function()
4302 var i = this.indexOfNav(this.getActive());
4306 this.setActiveItem(this.navItems[i-1]);
4308 clearWasActive : function(except) {
4309 Roo.each(this.navItems, function(e) {
4310 if (e.tabId != except.tabId && e.was_active) {
4311 e.was_active = false;
4318 getWasActive : function ()
4321 Roo.each(this.navItems, function(e) {
4336 Roo.apply(Roo.bootstrap.NavGroup, {
4340 * register a Navigation Group
4341 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4343 register : function(navgrp)
4345 this.groups[navgrp.navId] = navgrp;
4349 * fetch a Navigation Group based on the navigation ID
4350 * @param {string} the navgroup to add
4351 * @returns {Roo.bootstrap.NavGroup} the navgroup
4353 get: function(navId) {
4354 if (typeof(this.groups[navId]) == 'undefined') {
4356 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4358 return this.groups[navId] ;
4373 * @class Roo.bootstrap.NavItem
4374 * @extends Roo.bootstrap.Component
4375 * Bootstrap Navbar.NavItem class
4376 * @cfg {String} href link to
4377 * @cfg {String} html content of button
4378 * @cfg {String} badge text inside badge
4379 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4380 * @cfg {String} glyphicon name of glyphicon
4381 * @cfg {String} icon name of font awesome icon
4382 * @cfg {Boolean} active Is item active
4383 * @cfg {Boolean} disabled Is item disabled
4385 * @cfg {Boolean} preventDefault (true | false) default false
4386 * @cfg {String} tabId the tab that this item activates.
4387 * @cfg {String} tagtype (a|span) render as a href or span?
4388 * @cfg {Boolean} animateRef (true|false) link to element default false
4391 * Create a new Navbar Item
4392 * @param {Object} config The config object
4394 Roo.bootstrap.NavItem = function(config){
4395 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4400 * The raw click event for the entire grid.
4401 * @param {Roo.EventObject} e
4406 * Fires when the active item active state changes
4407 * @param {Roo.bootstrap.NavItem} this
4408 * @param {boolean} state the new state
4414 * Fires when scroll to element
4415 * @param {Roo.bootstrap.NavItem} this
4416 * @param {Object} options
4417 * @param {Roo.EventObject} e
4425 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4433 preventDefault : false,
4440 getAutoCreate : function(){
4449 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4451 if (this.disabled) {
4452 cfg.cls += ' disabled';
4455 if (this.href || this.html || this.glyphicon || this.icon) {
4459 href : this.href || "#",
4460 html: this.html || ''
4465 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4468 if(this.glyphicon) {
4469 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4474 cfg.cn[0].html += " <span class='caret'></span>";
4478 if (this.badge !== '') {
4480 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4488 initEvents: function()
4490 if (typeof (this.menu) != 'undefined') {
4491 this.menu.parentType = this.xtype;
4492 this.menu.triggerEl = this.el;
4493 this.menu = this.addxtype(Roo.apply({}, this.menu));
4496 this.el.select('a',true).on('click', this.onClick, this);
4498 if(this.tagtype == 'span'){
4499 this.el.select('span',true).on('click', this.onClick, this);
4502 // at this point parent should be available..
4503 this.parent().register(this);
4506 onClick : function(e)
4508 if (e.getTarget('.dropdown-menu-item')) {
4509 // did you click on a menu itemm.... - then don't trigger onclick..
4514 this.preventDefault ||
4517 Roo.log("NavItem - prevent Default?");
4521 if (this.disabled) {
4525 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4526 if (tg && tg.transition) {
4527 Roo.log("waiting for the transitionend");
4533 //Roo.log("fire event clicked");
4534 if(this.fireEvent('click', this, e) === false){
4538 if(this.tagtype == 'span'){
4542 //Roo.log(this.href);
4543 var ael = this.el.select('a',true).first();
4546 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4547 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4548 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4549 return; // ignore... - it's a 'hash' to another page.
4551 Roo.log("NavItem - prevent Default?");
4553 this.scrollToElement(e);
4557 var p = this.parent();
4559 if (['tabs','pills'].indexOf(p.type)!==-1) {
4560 if (typeof(p.setActiveItem) !== 'undefined') {
4561 p.setActiveItem(this);
4565 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4566 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4567 // remove the collapsed menu expand...
4568 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4572 isActive: function () {
4575 setActive : function(state, fire, is_was_active)
4577 if (this.active && !state && this.navId) {
4578 this.was_active = true;
4579 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4581 nv.clearWasActive(this);
4585 this.active = state;
4588 this.el.removeClass('active');
4589 } else if (!this.el.hasClass('active')) {
4590 this.el.addClass('active');
4593 this.fireEvent('changed', this, state);
4596 // show a panel if it's registered and related..
4598 if (!this.navId || !this.tabId || !state || is_was_active) {
4602 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4606 var pan = tg.getPanelByName(this.tabId);
4610 // if we can not flip to new panel - go back to old nav highlight..
4611 if (false == tg.showPanel(pan)) {
4612 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4614 var onav = nv.getWasActive();
4616 onav.setActive(true, false, true);
4625 // this should not be here...
4626 setDisabled : function(state)
4628 this.disabled = state;
4630 this.el.removeClass('disabled');
4631 } else if (!this.el.hasClass('disabled')) {
4632 this.el.addClass('disabled');
4638 * Fetch the element to display the tooltip on.
4639 * @return {Roo.Element} defaults to this.el
4641 tooltipEl : function()
4643 return this.el.select('' + this.tagtype + '', true).first();
4646 scrollToElement : function(e)
4648 var c = document.body;
4651 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4653 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4654 c = document.documentElement;
4657 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4663 var o = target.calcOffsetsTo(c);
4670 this.fireEvent('scrollto', this, options, e);
4672 Roo.get(c).scrollTo('top', options.value, true);
4685 * <span> icon </span>
4686 * <span> text </span>
4687 * <span>badge </span>
4691 * @class Roo.bootstrap.NavSidebarItem
4692 * @extends Roo.bootstrap.NavItem
4693 * Bootstrap Navbar.NavSidebarItem class
4694 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4695 * {Boolean} open is the menu open
4696 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4697 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4698 * {String} buttonSize (sm|md|lg)the extra classes for the button
4699 * {Boolean} showArrow show arrow next to the text (default true)
4701 * Create a new Navbar Button
4702 * @param {Object} config The config object
4704 Roo.bootstrap.NavSidebarItem = function(config){
4705 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4710 * The raw click event for the entire grid.
4711 * @param {Roo.EventObject} e
4716 * Fires when the active item active state changes
4717 * @param {Roo.bootstrap.NavSidebarItem} this
4718 * @param {boolean} state the new state
4726 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4728 badgeWeight : 'default',
4734 buttonWeight : 'default',
4740 getAutoCreate : function(){
4745 href : this.href || '#',
4751 if(this.buttonView){
4754 href : this.href || '#',
4755 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4768 cfg.cls += ' active';
4771 if (this.disabled) {
4772 cfg.cls += ' disabled';
4775 cfg.cls += ' open x-open';
4778 if (this.glyphicon || this.icon) {
4779 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4780 a.cn.push({ tag : 'i', cls : c }) ;
4783 if(!this.buttonView){
4786 html : this.html || ''
4793 if (this.badge !== '') {
4794 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4800 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4803 a.cls += ' dropdown-toggle treeview' ;
4809 initEvents : function()
4811 if (typeof (this.menu) != 'undefined') {
4812 this.menu.parentType = this.xtype;
4813 this.menu.triggerEl = this.el;
4814 this.menu = this.addxtype(Roo.apply({}, this.menu));
4817 this.el.on('click', this.onClick, this);
4819 if(this.badge !== ''){
4820 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4825 onClick : function(e)
4832 if(this.preventDefault){
4836 this.fireEvent('click', this);
4839 disable : function()
4841 this.setDisabled(true);
4846 this.setDisabled(false);
4849 setDisabled : function(state)
4851 if(this.disabled == state){
4855 this.disabled = state;
4858 this.el.addClass('disabled');
4862 this.el.removeClass('disabled');
4867 setActive : function(state)
4869 if(this.active == state){
4873 this.active = state;
4876 this.el.addClass('active');
4880 this.el.removeClass('active');
4885 isActive: function ()
4890 setBadge : function(str)
4896 this.badgeEl.dom.innerHTML = str;
4913 * @class Roo.bootstrap.Row
4914 * @extends Roo.bootstrap.Component
4915 * Bootstrap Row class (contains columns...)
4919 * @param {Object} config The config object
4922 Roo.bootstrap.Row = function(config){
4923 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4926 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4928 getAutoCreate : function(){
4947 * @class Roo.bootstrap.Element
4948 * @extends Roo.bootstrap.Component
4949 * Bootstrap Element class
4950 * @cfg {String} html contents of the element
4951 * @cfg {String} tag tag of the element
4952 * @cfg {String} cls class of the element
4953 * @cfg {Boolean} preventDefault (true|false) default false
4954 * @cfg {Boolean} clickable (true|false) default false
4957 * Create a new Element
4958 * @param {Object} config The config object
4961 Roo.bootstrap.Element = function(config){
4962 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4968 * When a element is chick
4969 * @param {Roo.bootstrap.Element} this
4970 * @param {Roo.EventObject} e
4976 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4981 preventDefault: false,
4984 getAutoCreate : function(){
4988 // cls: this.cls, double assign in parent class Component.js :: onRender
4995 initEvents: function()
4997 Roo.bootstrap.Element.superclass.initEvents.call(this);
5000 this.el.on('click', this.onClick, this);
5005 onClick : function(e)
5007 if(this.preventDefault){
5011 this.fireEvent('click', this, e);
5014 getValue : function()
5016 return this.el.dom.innerHTML;
5019 setValue : function(value)
5021 this.el.dom.innerHTML = value;
5036 * @class Roo.bootstrap.Pagination
5037 * @extends Roo.bootstrap.Component
5038 * Bootstrap Pagination class
5039 * @cfg {String} size xs | sm | md | lg
5040 * @cfg {Boolean} inverse false | true
5043 * Create a new Pagination
5044 * @param {Object} config The config object
5047 Roo.bootstrap.Pagination = function(config){
5048 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5051 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5057 getAutoCreate : function(){
5063 cfg.cls += ' inverse';
5069 cfg.cls += " " + this.cls;
5087 * @class Roo.bootstrap.PaginationItem
5088 * @extends Roo.bootstrap.Component
5089 * Bootstrap PaginationItem class
5090 * @cfg {String} html text
5091 * @cfg {String} href the link
5092 * @cfg {Boolean} preventDefault (true | false) default true
5093 * @cfg {Boolean} active (true | false) default false
5094 * @cfg {Boolean} disabled default false
5098 * Create a new PaginationItem
5099 * @param {Object} config The config object
5103 Roo.bootstrap.PaginationItem = function(config){
5104 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5109 * The raw click event for the entire grid.
5110 * @param {Roo.EventObject} e
5116 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5120 preventDefault: true,
5125 getAutoCreate : function(){
5131 href : this.href ? this.href : '#',
5132 html : this.html ? this.html : ''
5142 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5146 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5152 initEvents: function() {
5154 this.el.on('click', this.onClick, this);
5157 onClick : function(e)
5159 Roo.log('PaginationItem on click ');
5160 if(this.preventDefault){
5168 this.fireEvent('click', this, e);
5184 * @class Roo.bootstrap.Slider
5185 * @extends Roo.bootstrap.Component
5186 * Bootstrap Slider class
5189 * Create a new Slider
5190 * @param {Object} config The config object
5193 Roo.bootstrap.Slider = function(config){
5194 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5197 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5199 getAutoCreate : function(){
5203 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5207 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5219 * Ext JS Library 1.1.1
5220 * Copyright(c) 2006-2007, Ext JS, LLC.
5222 * Originally Released Under LGPL - original licence link has changed is not relivant.
5225 * <script type="text/javascript">
5230 * @class Roo.grid.ColumnModel
5231 * @extends Roo.util.Observable
5232 * This is the default implementation of a ColumnModel used by the Grid. It defines
5233 * the columns in the grid.
5236 var colModel = new Roo.grid.ColumnModel([
5237 {header: "Ticker", width: 60, sortable: true, locked: true},
5238 {header: "Company Name", width: 150, sortable: true},
5239 {header: "Market Cap.", width: 100, sortable: true},
5240 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5241 {header: "Employees", width: 100, sortable: true, resizable: false}
5246 * The config options listed for this class are options which may appear in each
5247 * individual column definition.
5248 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5250 * @param {Object} config An Array of column config objects. See this class's
5251 * config objects for details.
5253 Roo.grid.ColumnModel = function(config){
5255 * The config passed into the constructor
5257 this.config = config;
5260 // if no id, create one
5261 // if the column does not have a dataIndex mapping,
5262 // map it to the order it is in the config
5263 for(var i = 0, len = config.length; i < len; i++){
5265 if(typeof c.dataIndex == "undefined"){
5268 if(typeof c.renderer == "string"){
5269 c.renderer = Roo.util.Format[c.renderer];
5271 if(typeof c.id == "undefined"){
5274 if(c.editor && c.editor.xtype){
5275 c.editor = Roo.factory(c.editor, Roo.grid);
5277 if(c.editor && c.editor.isFormField){
5278 c.editor = new Roo.grid.GridEditor(c.editor);
5280 this.lookup[c.id] = c;
5284 * The width of columns which have no width specified (defaults to 100)
5287 this.defaultWidth = 100;
5290 * Default sortable of columns which have no sortable specified (defaults to false)
5293 this.defaultSortable = false;
5297 * @event widthchange
5298 * Fires when the width of a column changes.
5299 * @param {ColumnModel} this
5300 * @param {Number} columnIndex The column index
5301 * @param {Number} newWidth The new width
5303 "widthchange": true,
5305 * @event headerchange
5306 * Fires when the text of a header changes.
5307 * @param {ColumnModel} this
5308 * @param {Number} columnIndex The column index
5309 * @param {Number} newText The new header text
5311 "headerchange": true,
5313 * @event hiddenchange
5314 * Fires when a column is hidden or "unhidden".
5315 * @param {ColumnModel} this
5316 * @param {Number} columnIndex The column index
5317 * @param {Boolean} hidden true if hidden, false otherwise
5319 "hiddenchange": true,
5321 * @event columnmoved
5322 * Fires when a column is moved.
5323 * @param {ColumnModel} this
5324 * @param {Number} oldIndex
5325 * @param {Number} newIndex
5327 "columnmoved" : true,
5329 * @event columlockchange
5330 * Fires when a column's locked state is changed
5331 * @param {ColumnModel} this
5332 * @param {Number} colIndex
5333 * @param {Boolean} locked true if locked
5335 "columnlockchange" : true
5337 Roo.grid.ColumnModel.superclass.constructor.call(this);
5339 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5341 * @cfg {String} header The header text to display in the Grid view.
5344 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5345 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5346 * specified, the column's index is used as an index into the Record's data Array.
5349 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5350 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5353 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5354 * Defaults to the value of the {@link #defaultSortable} property.
5355 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5358 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5361 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5364 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5367 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5370 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5371 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5372 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5373 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5376 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5379 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5382 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5385 * @cfg {String} cursor (Optional)
5388 * @cfg {String} tooltip (Optional)
5391 * @cfg {Number} xs (Optional)
5394 * @cfg {Number} sm (Optional)
5397 * @cfg {Number} md (Optional)
5400 * @cfg {Number} lg (Optional)
5403 * Returns the id of the column at the specified index.
5404 * @param {Number} index The column index
5405 * @return {String} the id
5407 getColumnId : function(index){
5408 return this.config[index].id;
5412 * Returns the column for a specified id.
5413 * @param {String} id The column id
5414 * @return {Object} the column
5416 getColumnById : function(id){
5417 return this.lookup[id];
5422 * Returns the column for a specified dataIndex.
5423 * @param {String} dataIndex The column dataIndex
5424 * @return {Object|Boolean} the column or false if not found
5426 getColumnByDataIndex: function(dataIndex){
5427 var index = this.findColumnIndex(dataIndex);
5428 return index > -1 ? this.config[index] : false;
5432 * Returns the index for a specified column id.
5433 * @param {String} id The column id
5434 * @return {Number} the index, or -1 if not found
5436 getIndexById : function(id){
5437 for(var i = 0, len = this.config.length; i < len; i++){
5438 if(this.config[i].id == id){
5446 * Returns the index for a specified column dataIndex.
5447 * @param {String} dataIndex The column dataIndex
5448 * @return {Number} the index, or -1 if not found
5451 findColumnIndex : function(dataIndex){
5452 for(var i = 0, len = this.config.length; i < len; i++){
5453 if(this.config[i].dataIndex == dataIndex){
5461 moveColumn : function(oldIndex, newIndex){
5462 var c = this.config[oldIndex];
5463 this.config.splice(oldIndex, 1);
5464 this.config.splice(newIndex, 0, c);
5465 this.dataMap = null;
5466 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5469 isLocked : function(colIndex){
5470 return this.config[colIndex].locked === true;
5473 setLocked : function(colIndex, value, suppressEvent){
5474 if(this.isLocked(colIndex) == value){
5477 this.config[colIndex].locked = value;
5479 this.fireEvent("columnlockchange", this, colIndex, value);
5483 getTotalLockedWidth : function(){
5485 for(var i = 0; i < this.config.length; i++){
5486 if(this.isLocked(i) && !this.isHidden(i)){
5487 this.totalWidth += this.getColumnWidth(i);
5493 getLockedCount : function(){
5494 for(var i = 0, len = this.config.length; i < len; i++){
5495 if(!this.isLocked(i)){
5500 return this.config.length;
5504 * Returns the number of columns.
5507 getColumnCount : function(visibleOnly){
5508 if(visibleOnly === true){
5510 for(var i = 0, len = this.config.length; i < len; i++){
5511 if(!this.isHidden(i)){
5517 return this.config.length;
5521 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5522 * @param {Function} fn
5523 * @param {Object} scope (optional)
5524 * @return {Array} result
5526 getColumnsBy : function(fn, scope){
5528 for(var i = 0, len = this.config.length; i < len; i++){
5529 var c = this.config[i];
5530 if(fn.call(scope||this, c, i) === true){
5538 * Returns true if the specified column is sortable.
5539 * @param {Number} col The column index
5542 isSortable : function(col){
5543 if(typeof this.config[col].sortable == "undefined"){
5544 return this.defaultSortable;
5546 return this.config[col].sortable;
5550 * Returns the rendering (formatting) function defined for the column.
5551 * @param {Number} col The column index.
5552 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5554 getRenderer : function(col){
5555 if(!this.config[col].renderer){
5556 return Roo.grid.ColumnModel.defaultRenderer;
5558 return this.config[col].renderer;
5562 * Sets the rendering (formatting) function for a column.
5563 * @param {Number} col The column index
5564 * @param {Function} fn The function to use to process the cell's raw data
5565 * to return HTML markup for the grid view. The render function is called with
5566 * the following parameters:<ul>
5567 * <li>Data value.</li>
5568 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5569 * <li>css A CSS style string to apply to the table cell.</li>
5570 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5571 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5572 * <li>Row index</li>
5573 * <li>Column index</li>
5574 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5576 setRenderer : function(col, fn){
5577 this.config[col].renderer = fn;
5581 * Returns the width for the specified column.
5582 * @param {Number} col The column index
5585 getColumnWidth : function(col){
5586 return this.config[col].width * 1 || this.defaultWidth;
5590 * Sets the width for a column.
5591 * @param {Number} col The column index
5592 * @param {Number} width The new width
5594 setColumnWidth : function(col, width, suppressEvent){
5595 this.config[col].width = width;
5596 this.totalWidth = null;
5598 this.fireEvent("widthchange", this, col, width);
5603 * Returns the total width of all columns.
5604 * @param {Boolean} includeHidden True to include hidden column widths
5607 getTotalWidth : function(includeHidden){
5608 if(!this.totalWidth){
5609 this.totalWidth = 0;
5610 for(var i = 0, len = this.config.length; i < len; i++){
5611 if(includeHidden || !this.isHidden(i)){
5612 this.totalWidth += this.getColumnWidth(i);
5616 return this.totalWidth;
5620 * Returns the header for the specified column.
5621 * @param {Number} col The column index
5624 getColumnHeader : function(col){
5625 return this.config[col].header;
5629 * Sets the header for a column.
5630 * @param {Number} col The column index
5631 * @param {String} header The new header
5633 setColumnHeader : function(col, header){
5634 this.config[col].header = header;
5635 this.fireEvent("headerchange", this, col, header);
5639 * Returns the tooltip for the specified column.
5640 * @param {Number} col The column index
5643 getColumnTooltip : function(col){
5644 return this.config[col].tooltip;
5647 * Sets the tooltip for a column.
5648 * @param {Number} col The column index
5649 * @param {String} tooltip The new tooltip
5651 setColumnTooltip : function(col, tooltip){
5652 this.config[col].tooltip = tooltip;
5656 * Returns the dataIndex for the specified column.
5657 * @param {Number} col The column index
5660 getDataIndex : function(col){
5661 return this.config[col].dataIndex;
5665 * Sets the dataIndex for a column.
5666 * @param {Number} col The column index
5667 * @param {Number} dataIndex The new dataIndex
5669 setDataIndex : function(col, dataIndex){
5670 this.config[col].dataIndex = dataIndex;
5676 * Returns true if the cell is editable.
5677 * @param {Number} colIndex The column index
5678 * @param {Number} rowIndex The row index - this is nto actually used..?
5681 isCellEditable : function(colIndex, rowIndex){
5682 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5686 * Returns the editor defined for the cell/column.
5687 * return false or null to disable editing.
5688 * @param {Number} colIndex The column index
5689 * @param {Number} rowIndex The row index
5692 getCellEditor : function(colIndex, rowIndex){
5693 return this.config[colIndex].editor;
5697 * Sets if a column is editable.
5698 * @param {Number} col The column index
5699 * @param {Boolean} editable True if the column is editable
5701 setEditable : function(col, editable){
5702 this.config[col].editable = editable;
5707 * Returns true if the column is hidden.
5708 * @param {Number} colIndex The column index
5711 isHidden : function(colIndex){
5712 return this.config[colIndex].hidden;
5717 * Returns true if the column width cannot be changed
5719 isFixed : function(colIndex){
5720 return this.config[colIndex].fixed;
5724 * Returns true if the column can be resized
5727 isResizable : function(colIndex){
5728 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5731 * Sets if a column is hidden.
5732 * @param {Number} colIndex The column index
5733 * @param {Boolean} hidden True if the column is hidden
5735 setHidden : function(colIndex, hidden){
5736 this.config[colIndex].hidden = hidden;
5737 this.totalWidth = null;
5738 this.fireEvent("hiddenchange", this, colIndex, hidden);
5742 * Sets the editor for a column.
5743 * @param {Number} col The column index
5744 * @param {Object} editor The editor object
5746 setEditor : function(col, editor){
5747 this.config[col].editor = editor;
5751 Roo.grid.ColumnModel.defaultRenderer = function(value)
5753 if(typeof value == "object") {
5756 if(typeof value == "string" && value.length < 1){
5760 return String.format("{0}", value);
5763 // Alias for backwards compatibility
5764 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5767 * Ext JS Library 1.1.1
5768 * Copyright(c) 2006-2007, Ext JS, LLC.
5770 * Originally Released Under LGPL - original licence link has changed is not relivant.
5773 * <script type="text/javascript">
5777 * @class Roo.LoadMask
5778 * A simple utility class for generically masking elements while loading data. If the element being masked has
5779 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5780 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5781 * element's UpdateManager load indicator and will be destroyed after the initial load.
5783 * Create a new LoadMask
5784 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5785 * @param {Object} config The config object
5787 Roo.LoadMask = function(el, config){
5788 this.el = Roo.get(el);
5789 Roo.apply(this, config);
5791 this.store.on('beforeload', this.onBeforeLoad, this);
5792 this.store.on('load', this.onLoad, this);
5793 this.store.on('loadexception', this.onLoadException, this);
5794 this.removeMask = false;
5796 var um = this.el.getUpdateManager();
5797 um.showLoadIndicator = false; // disable the default indicator
5798 um.on('beforeupdate', this.onBeforeLoad, this);
5799 um.on('update', this.onLoad, this);
5800 um.on('failure', this.onLoad, this);
5801 this.removeMask = true;
5805 Roo.LoadMask.prototype = {
5807 * @cfg {Boolean} removeMask
5808 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5809 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5813 * The text to display in a centered loading message box (defaults to 'Loading...')
5817 * @cfg {String} msgCls
5818 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5820 msgCls : 'x-mask-loading',
5823 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5829 * Disables the mask to prevent it from being displayed
5831 disable : function(){
5832 this.disabled = true;
5836 * Enables the mask so that it can be displayed
5838 enable : function(){
5839 this.disabled = false;
5842 onLoadException : function()
5846 if (typeof(arguments[3]) != 'undefined') {
5847 Roo.MessageBox.alert("Error loading",arguments[3]);
5851 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5852 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5859 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5864 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5868 onBeforeLoad : function(){
5870 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5875 destroy : function(){
5877 this.store.un('beforeload', this.onBeforeLoad, this);
5878 this.store.un('load', this.onLoad, this);
5879 this.store.un('loadexception', this.onLoadException, this);
5881 var um = this.el.getUpdateManager();
5882 um.un('beforeupdate', this.onBeforeLoad, this);
5883 um.un('update', this.onLoad, this);
5884 um.un('failure', this.onLoad, this);
5895 * @class Roo.bootstrap.Table
5896 * @extends Roo.bootstrap.Component
5897 * Bootstrap Table class
5898 * @cfg {String} cls table class
5899 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5900 * @cfg {String} bgcolor Specifies the background color for a table
5901 * @cfg {Number} border Specifies whether the table cells should have borders or not
5902 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5903 * @cfg {Number} cellspacing Specifies the space between cells
5904 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5905 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5906 * @cfg {String} sortable Specifies that the table should be sortable
5907 * @cfg {String} summary Specifies a summary of the content of a table
5908 * @cfg {Number} width Specifies the width of a table
5909 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5911 * @cfg {boolean} striped Should the rows be alternative striped
5912 * @cfg {boolean} bordered Add borders to the table
5913 * @cfg {boolean} hover Add hover highlighting
5914 * @cfg {boolean} condensed Format condensed
5915 * @cfg {boolean} responsive Format condensed
5916 * @cfg {Boolean} loadMask (true|false) default false
5917 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5918 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5919 * @cfg {Boolean} rowSelection (true|false) default false
5920 * @cfg {Boolean} cellSelection (true|false) default false
5921 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5922 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5923 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5924 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5928 * Create a new Table
5929 * @param {Object} config The config object
5932 Roo.bootstrap.Table = function(config){
5933 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5938 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5939 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5940 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5941 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5943 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5945 this.sm.grid = this;
5946 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5947 this.sm = this.selModel;
5948 this.sm.xmodule = this.xmodule || false;
5951 if (this.cm && typeof(this.cm.config) == 'undefined') {
5952 this.colModel = new Roo.grid.ColumnModel(this.cm);
5953 this.cm = this.colModel;
5954 this.cm.xmodule = this.xmodule || false;
5957 this.store= Roo.factory(this.store, Roo.data);
5958 this.ds = this.store;
5959 this.ds.xmodule = this.xmodule || false;
5962 if (this.footer && this.store) {
5963 this.footer.dataSource = this.ds;
5964 this.footer = Roo.factory(this.footer);
5971 * Fires when a cell is clicked
5972 * @param {Roo.bootstrap.Table} this
5973 * @param {Roo.Element} el
5974 * @param {Number} rowIndex
5975 * @param {Number} columnIndex
5976 * @param {Roo.EventObject} e
5980 * @event celldblclick
5981 * Fires when a cell is double clicked
5982 * @param {Roo.bootstrap.Table} this
5983 * @param {Roo.Element} el
5984 * @param {Number} rowIndex
5985 * @param {Number} columnIndex
5986 * @param {Roo.EventObject} e
5988 "celldblclick" : true,
5991 * Fires when a row is clicked
5992 * @param {Roo.bootstrap.Table} this
5993 * @param {Roo.Element} el
5994 * @param {Number} rowIndex
5995 * @param {Roo.EventObject} e
5999 * @event rowdblclick
6000 * Fires when a row is double clicked
6001 * @param {Roo.bootstrap.Table} this
6002 * @param {Roo.Element} el
6003 * @param {Number} rowIndex
6004 * @param {Roo.EventObject} e
6006 "rowdblclick" : true,
6009 * Fires when a mouseover occur
6010 * @param {Roo.bootstrap.Table} this
6011 * @param {Roo.Element} el
6012 * @param {Number} rowIndex
6013 * @param {Number} columnIndex
6014 * @param {Roo.EventObject} e
6019 * Fires when a mouseout occur
6020 * @param {Roo.bootstrap.Table} this
6021 * @param {Roo.Element} el
6022 * @param {Number} rowIndex
6023 * @param {Number} columnIndex
6024 * @param {Roo.EventObject} e
6029 * Fires when a row is rendered, so you can change add a style to it.
6030 * @param {Roo.bootstrap.Table} this
6031 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6035 * @event rowsrendered
6036 * Fires when all the rows have been rendered
6037 * @param {Roo.bootstrap.Table} this
6039 'rowsrendered' : true,
6041 * @event contextmenu
6042 * The raw contextmenu event for the entire grid.
6043 * @param {Roo.EventObject} e
6045 "contextmenu" : true,
6047 * @event rowcontextmenu
6048 * Fires when a row is right clicked
6049 * @param {Roo.bootstrap.Table} this
6050 * @param {Number} rowIndex
6051 * @param {Roo.EventObject} e
6053 "rowcontextmenu" : true,
6055 * @event cellcontextmenu
6056 * Fires when a cell is right clicked
6057 * @param {Roo.bootstrap.Table} this
6058 * @param {Number} rowIndex
6059 * @param {Number} cellIndex
6060 * @param {Roo.EventObject} e
6062 "cellcontextmenu" : true,
6064 * @event headercontextmenu
6065 * Fires when a header is right clicked
6066 * @param {Roo.bootstrap.Table} this
6067 * @param {Number} columnIndex
6068 * @param {Roo.EventObject} e
6070 "headercontextmenu" : true
6074 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6100 rowSelection : false,
6101 cellSelection : false,
6104 // Roo.Element - the tbody
6106 // Roo.Element - thead element
6109 container: false, // used by gridpanel...
6115 auto_hide_footer : false,
6117 getAutoCreate : function()
6119 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6126 if (this.scrollBody) {
6127 cfg.cls += ' table-body-fixed';
6130 cfg.cls += ' table-striped';
6134 cfg.cls += ' table-hover';
6136 if (this.bordered) {
6137 cfg.cls += ' table-bordered';
6139 if (this.condensed) {
6140 cfg.cls += ' table-condensed';
6142 if (this.responsive) {
6143 cfg.cls += ' table-responsive';
6147 cfg.cls+= ' ' +this.cls;
6150 // this lot should be simplifed...
6163 ].forEach(function(k) {
6171 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6174 if(this.store || this.cm){
6175 if(this.headerShow){
6176 cfg.cn.push(this.renderHeader());
6179 cfg.cn.push(this.renderBody());
6181 if(this.footerShow){
6182 cfg.cn.push(this.renderFooter());
6184 // where does this come from?
6185 //cfg.cls+= ' TableGrid';
6188 return { cn : [ cfg ] };
6191 initEvents : function()
6193 if(!this.store || !this.cm){
6196 if (this.selModel) {
6197 this.selModel.initEvents();
6201 //Roo.log('initEvents with ds!!!!');
6203 this.mainBody = this.el.select('tbody', true).first();
6204 this.mainHead = this.el.select('thead', true).first();
6205 this.mainFoot = this.el.select('tfoot', true).first();
6211 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6212 e.on('click', _this.sort, _this);
6215 this.mainBody.on("click", this.onClick, this);
6216 this.mainBody.on("dblclick", this.onDblClick, this);
6218 // why is this done????? = it breaks dialogs??
6219 //this.parent().el.setStyle('position', 'relative');
6223 this.footer.parentId = this.id;
6224 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6227 this.el.select('tfoot tr td').first().addClass('hide');
6232 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6235 this.store.on('load', this.onLoad, this);
6236 this.store.on('beforeload', this.onBeforeLoad, this);
6237 this.store.on('update', this.onUpdate, this);
6238 this.store.on('add', this.onAdd, this);
6239 this.store.on("clear", this.clear, this);
6241 this.el.on("contextmenu", this.onContextMenu, this);
6243 this.mainBody.on('scroll', this.onBodyScroll, this);
6245 this.cm.on("headerchange", this.onHeaderChange, this);
6247 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6251 onContextMenu : function(e, t)
6253 this.processEvent("contextmenu", e);
6256 processEvent : function(name, e)
6258 if (name != 'touchstart' ) {
6259 this.fireEvent(name, e);
6262 var t = e.getTarget();
6264 var cell = Roo.get(t);
6270 if(cell.findParent('tfoot', false, true)){
6274 if(cell.findParent('thead', false, true)){
6276 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6277 cell = Roo.get(t).findParent('th', false, true);
6279 Roo.log("failed to find th in thead?");
6280 Roo.log(e.getTarget());
6285 var cellIndex = cell.dom.cellIndex;
6287 var ename = name == 'touchstart' ? 'click' : name;
6288 this.fireEvent("header" + ename, this, cellIndex, e);
6293 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6294 cell = Roo.get(t).findParent('td', false, true);
6296 Roo.log("failed to find th in tbody?");
6297 Roo.log(e.getTarget());
6302 var row = cell.findParent('tr', false, true);
6303 var cellIndex = cell.dom.cellIndex;
6304 var rowIndex = row.dom.rowIndex - 1;
6308 this.fireEvent("row" + name, this, rowIndex, e);
6312 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6318 onMouseover : function(e, el)
6320 var cell = Roo.get(el);
6326 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6327 cell = cell.findParent('td', false, true);
6330 var row = cell.findParent('tr', false, true);
6331 var cellIndex = cell.dom.cellIndex;
6332 var rowIndex = row.dom.rowIndex - 1; // start from 0
6334 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6338 onMouseout : function(e, el)
6340 var cell = Roo.get(el);
6346 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6347 cell = cell.findParent('td', false, true);
6350 var row = cell.findParent('tr', false, true);
6351 var cellIndex = cell.dom.cellIndex;
6352 var rowIndex = row.dom.rowIndex - 1; // start from 0
6354 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6358 onClick : function(e, el)
6360 var cell = Roo.get(el);
6362 if(!cell || (!this.cellSelection && !this.rowSelection)){
6366 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6367 cell = cell.findParent('td', false, true);
6370 if(!cell || typeof(cell) == 'undefined'){
6374 var row = cell.findParent('tr', false, true);
6376 if(!row || typeof(row) == 'undefined'){
6380 var cellIndex = cell.dom.cellIndex;
6381 var rowIndex = this.getRowIndex(row);
6383 // why??? - should these not be based on SelectionModel?
6384 if(this.cellSelection){
6385 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6388 if(this.rowSelection){
6389 this.fireEvent('rowclick', this, row, rowIndex, e);
6395 onDblClick : function(e,el)
6397 var cell = Roo.get(el);
6399 if(!cell || (!this.cellSelection && !this.rowSelection)){
6403 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6404 cell = cell.findParent('td', false, true);
6407 if(!cell || typeof(cell) == 'undefined'){
6411 var row = cell.findParent('tr', false, true);
6413 if(!row || typeof(row) == 'undefined'){
6417 var cellIndex = cell.dom.cellIndex;
6418 var rowIndex = this.getRowIndex(row);
6420 if(this.cellSelection){
6421 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6424 if(this.rowSelection){
6425 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6429 sort : function(e,el)
6431 var col = Roo.get(el);
6433 if(!col.hasClass('sortable')){
6437 var sort = col.attr('sort');
6440 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6444 this.store.sortInfo = {field : sort, direction : dir};
6447 Roo.log("calling footer first");
6448 this.footer.onClick('first');
6451 this.store.load({ params : { start : 0 } });
6455 renderHeader : function()
6463 this.totalWidth = 0;
6465 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6467 var config = cm.config[i];
6471 cls : 'x-hcol-' + i,
6473 html: cm.getColumnHeader(i)
6478 if(typeof(config.sortable) != 'undefined' && config.sortable){
6480 c.html = '<i class="glyphicon"></i>' + c.html;
6483 if(typeof(config.lgHeader) != 'undefined'){
6484 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6487 if(typeof(config.mdHeader) != 'undefined'){
6488 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6491 if(typeof(config.smHeader) != 'undefined'){
6492 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6495 if(typeof(config.xsHeader) != 'undefined'){
6496 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6503 if(typeof(config.tooltip) != 'undefined'){
6504 c.tooltip = config.tooltip;
6507 if(typeof(config.colspan) != 'undefined'){
6508 c.colspan = config.colspan;
6511 if(typeof(config.hidden) != 'undefined' && config.hidden){
6512 c.style += ' display:none;';
6515 if(typeof(config.dataIndex) != 'undefined'){
6516 c.sort = config.dataIndex;
6521 if(typeof(config.align) != 'undefined' && config.align.length){
6522 c.style += ' text-align:' + config.align + ';';
6525 if(typeof(config.width) != 'undefined'){
6526 c.style += ' width:' + config.width + 'px;';
6527 this.totalWidth += config.width;
6529 this.totalWidth += 100; // assume minimum of 100 per column?
6532 if(typeof(config.cls) != 'undefined'){
6533 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6536 ['xs','sm','md','lg'].map(function(size){
6538 if(typeof(config[size]) == 'undefined'){
6542 if (!config[size]) { // 0 = hidden
6543 c.cls += ' hidden-' + size;
6547 c.cls += ' col-' + size + '-' + config[size];
6557 renderBody : function()
6567 colspan : this.cm.getColumnCount()
6577 renderFooter : function()
6587 colspan : this.cm.getColumnCount()
6601 // Roo.log('ds onload');
6606 var ds = this.store;
6608 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6609 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6610 if (_this.store.sortInfo) {
6612 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6613 e.select('i', true).addClass(['glyphicon-arrow-up']);
6616 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6617 e.select('i', true).addClass(['glyphicon-arrow-down']);
6622 var tbody = this.mainBody;
6624 if(ds.getCount() > 0){
6625 ds.data.each(function(d,rowIndex){
6626 var row = this.renderRow(cm, ds, rowIndex);
6628 tbody.createChild(row);
6632 if(row.cellObjects.length){
6633 Roo.each(row.cellObjects, function(r){
6634 _this.renderCellObject(r);
6641 var tfoot = this.el.select('tfoot', true).first();
6643 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6645 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6647 var total = this.ds.getTotalCount();
6649 if(this.footer.pageSize < total){
6650 this.mainFoot.show();
6654 Roo.each(this.el.select('tbody td', true).elements, function(e){
6655 e.on('mouseover', _this.onMouseover, _this);
6658 Roo.each(this.el.select('tbody td', true).elements, function(e){
6659 e.on('mouseout', _this.onMouseout, _this);
6661 this.fireEvent('rowsrendered', this);
6667 onUpdate : function(ds,record)
6669 this.refreshRow(record);
6673 onRemove : function(ds, record, index, isUpdate){
6674 if(isUpdate !== true){
6675 this.fireEvent("beforerowremoved", this, index, record);
6677 var bt = this.mainBody.dom;
6679 var rows = this.el.select('tbody > tr', true).elements;
6681 if(typeof(rows[index]) != 'undefined'){
6682 bt.removeChild(rows[index].dom);
6685 // if(bt.rows[index]){
6686 // bt.removeChild(bt.rows[index]);
6689 if(isUpdate !== true){
6690 //this.stripeRows(index);
6691 //this.syncRowHeights(index, index);
6693 this.fireEvent("rowremoved", this, index, record);
6697 onAdd : function(ds, records, rowIndex)
6699 //Roo.log('on Add called');
6700 // - note this does not handle multiple adding very well..
6701 var bt = this.mainBody.dom;
6702 for (var i =0 ; i < records.length;i++) {
6703 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6704 //Roo.log(records[i]);
6705 //Roo.log(this.store.getAt(rowIndex+i));
6706 this.insertRow(this.store, rowIndex + i, false);
6713 refreshRow : function(record){
6714 var ds = this.store, index;
6715 if(typeof record == 'number'){
6717 record = ds.getAt(index);
6719 index = ds.indexOf(record);
6721 this.insertRow(ds, index, true);
6723 this.onRemove(ds, record, index+1, true);
6725 //this.syncRowHeights(index, index);
6727 this.fireEvent("rowupdated", this, index, record);
6730 insertRow : function(dm, rowIndex, isUpdate){
6733 this.fireEvent("beforerowsinserted", this, rowIndex);
6735 //var s = this.getScrollState();
6736 var row = this.renderRow(this.cm, this.store, rowIndex);
6737 // insert before rowIndex..
6738 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6742 if(row.cellObjects.length){
6743 Roo.each(row.cellObjects, function(r){
6744 _this.renderCellObject(r);
6749 this.fireEvent("rowsinserted", this, rowIndex);
6750 //this.syncRowHeights(firstRow, lastRow);
6751 //this.stripeRows(firstRow);
6758 getRowDom : function(rowIndex)
6760 var rows = this.el.select('tbody > tr', true).elements;
6762 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6765 // returns the object tree for a tr..
6768 renderRow : function(cm, ds, rowIndex)
6770 var d = ds.getAt(rowIndex);
6774 cls : 'x-row-' + rowIndex,
6778 var cellObjects = [];
6780 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6781 var config = cm.config[i];
6783 var renderer = cm.getRenderer(i);
6787 if(typeof(renderer) !== 'undefined'){
6788 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6790 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6791 // and are rendered into the cells after the row is rendered - using the id for the element.
6793 if(typeof(value) === 'object'){
6803 rowIndex : rowIndex,
6808 this.fireEvent('rowclass', this, rowcfg);
6812 cls : rowcfg.rowClass + ' x-col-' + i,
6814 html: (typeof(value) === 'object') ? '' : value
6821 if(typeof(config.colspan) != 'undefined'){
6822 td.colspan = config.colspan;
6825 if(typeof(config.hidden) != 'undefined' && config.hidden){
6826 td.style += ' display:none;';
6829 if(typeof(config.align) != 'undefined' && config.align.length){
6830 td.style += ' text-align:' + config.align + ';';
6832 if(typeof(config.valign) != 'undefined' && config.valign.length){
6833 td.style += ' vertical-align:' + config.valign + ';';
6836 if(typeof(config.width) != 'undefined'){
6837 td.style += ' width:' + config.width + 'px;';
6840 if(typeof(config.cursor) != 'undefined'){
6841 td.style += ' cursor:' + config.cursor + ';';
6844 if(typeof(config.cls) != 'undefined'){
6845 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6848 ['xs','sm','md','lg'].map(function(size){
6850 if(typeof(config[size]) == 'undefined'){
6854 if (!config[size]) { // 0 = hidden
6855 td.cls += ' hidden-' + size;
6859 td.cls += ' col-' + size + '-' + config[size];
6867 row.cellObjects = cellObjects;
6875 onBeforeLoad : function()
6884 this.el.select('tbody', true).first().dom.innerHTML = '';
6887 * Show or hide a row.
6888 * @param {Number} rowIndex to show or hide
6889 * @param {Boolean} state hide
6891 setRowVisibility : function(rowIndex, state)
6893 var bt = this.mainBody.dom;
6895 var rows = this.el.select('tbody > tr', true).elements;
6897 if(typeof(rows[rowIndex]) == 'undefined'){
6900 rows[rowIndex].dom.style.display = state ? '' : 'none';
6904 getSelectionModel : function(){
6906 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6908 return this.selModel;
6911 * Render the Roo.bootstrap object from renderder
6913 renderCellObject : function(r)
6917 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6919 var t = r.cfg.render(r.container);
6922 Roo.each(r.cfg.cn, function(c){
6924 container: t.getChildContainer(),
6927 _this.renderCellObject(child);
6932 getRowIndex : function(row)
6936 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6947 * Returns the grid's underlying element = used by panel.Grid
6948 * @return {Element} The element
6950 getGridEl : function(){
6954 * Forces a resize - used by panel.Grid
6955 * @return {Element} The element
6957 autoSize : function()
6959 //var ctr = Roo.get(this.container.dom.parentElement);
6960 var ctr = Roo.get(this.el.dom);
6962 var thd = this.getGridEl().select('thead',true).first();
6963 var tbd = this.getGridEl().select('tbody', true).first();
6964 var tfd = this.getGridEl().select('tfoot', true).first();
6966 var cw = ctr.getWidth();
6970 tbd.setSize(ctr.getWidth(),
6971 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6973 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6976 cw = Math.max(cw, this.totalWidth);
6977 this.getGridEl().select('tr',true).setWidth(cw);
6978 // resize 'expandable coloumn?
6980 return; // we doe not have a view in this design..
6983 onBodyScroll: function()
6985 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6987 this.mainHead.setStyle({
6988 'position' : 'relative',
6989 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6995 var scrollHeight = this.mainBody.dom.scrollHeight;
6997 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6999 var height = this.mainBody.getHeight();
7001 if(scrollHeight - height == scrollTop) {
7003 var total = this.ds.getTotalCount();
7005 if(this.footer.cursor + this.footer.pageSize < total){
7007 this.footer.ds.load({
7009 start : this.footer.cursor + this.footer.pageSize,
7010 limit : this.footer.pageSize
7020 onHeaderChange : function()
7022 var header = this.renderHeader();
7023 var table = this.el.select('table', true).first();
7025 this.mainHead.remove();
7026 this.mainHead = table.createChild(header, this.mainBody, false);
7029 onHiddenChange : function(colModel, colIndex, hidden)
7031 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7032 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7034 this.CSS.updateRule(thSelector, "display", "");
7035 this.CSS.updateRule(tdSelector, "display", "");
7038 this.CSS.updateRule(thSelector, "display", "none");
7039 this.CSS.updateRule(tdSelector, "display", "none");
7042 this.onHeaderChange();
7059 * @class Roo.bootstrap.TableCell
7060 * @extends Roo.bootstrap.Component
7061 * Bootstrap TableCell class
7062 * @cfg {String} html cell contain text
7063 * @cfg {String} cls cell class
7064 * @cfg {String} tag cell tag (td|th) default td
7065 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7066 * @cfg {String} align Aligns the content in a cell
7067 * @cfg {String} axis Categorizes cells
7068 * @cfg {String} bgcolor Specifies the background color of a cell
7069 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7070 * @cfg {Number} colspan Specifies the number of columns a cell should span
7071 * @cfg {String} headers Specifies one or more header cells a cell is related to
7072 * @cfg {Number} height Sets the height of a cell
7073 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7074 * @cfg {Number} rowspan Sets the number of rows a cell should span
7075 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7076 * @cfg {String} valign Vertical aligns the content in a cell
7077 * @cfg {Number} width Specifies the width of a cell
7080 * Create a new TableCell
7081 * @param {Object} config The config object
7084 Roo.bootstrap.TableCell = function(config){
7085 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7088 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7108 getAutoCreate : function(){
7109 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7129 cfg.align=this.align
7135 cfg.bgcolor=this.bgcolor
7138 cfg.charoff=this.charoff
7141 cfg.colspan=this.colspan
7144 cfg.headers=this.headers
7147 cfg.height=this.height
7150 cfg.nowrap=this.nowrap
7153 cfg.rowspan=this.rowspan
7156 cfg.scope=this.scope
7159 cfg.valign=this.valign
7162 cfg.width=this.width
7181 * @class Roo.bootstrap.TableRow
7182 * @extends Roo.bootstrap.Component
7183 * Bootstrap TableRow class
7184 * @cfg {String} cls row class
7185 * @cfg {String} align Aligns the content in a table row
7186 * @cfg {String} bgcolor Specifies a background color for a table row
7187 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7188 * @cfg {String} valign Vertical aligns the content in a table row
7191 * Create a new TableRow
7192 * @param {Object} config The config object
7195 Roo.bootstrap.TableRow = function(config){
7196 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7199 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7207 getAutoCreate : function(){
7208 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7218 cfg.align = this.align;
7221 cfg.bgcolor = this.bgcolor;
7224 cfg.charoff = this.charoff;
7227 cfg.valign = this.valign;
7245 * @class Roo.bootstrap.TableBody
7246 * @extends Roo.bootstrap.Component
7247 * Bootstrap TableBody class
7248 * @cfg {String} cls element class
7249 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7250 * @cfg {String} align Aligns the content inside the element
7251 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7252 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7255 * Create a new TableBody
7256 * @param {Object} config The config object
7259 Roo.bootstrap.TableBody = function(config){
7260 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7263 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7271 getAutoCreate : function(){
7272 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7286 cfg.align = this.align;
7289 cfg.charoff = this.charoff;
7292 cfg.valign = this.valign;
7299 // initEvents : function()
7306 // this.store = Roo.factory(this.store, Roo.data);
7307 // this.store.on('load', this.onLoad, this);
7309 // this.store.load();
7313 // onLoad: function ()
7315 // this.fireEvent('load', this);
7325 * Ext JS Library 1.1.1
7326 * Copyright(c) 2006-2007, Ext JS, LLC.
7328 * Originally Released Under LGPL - original licence link has changed is not relivant.
7331 * <script type="text/javascript">
7334 // as we use this in bootstrap.
7335 Roo.namespace('Roo.form');
7337 * @class Roo.form.Action
7338 * Internal Class used to handle form actions
7340 * @param {Roo.form.BasicForm} el The form element or its id
7341 * @param {Object} config Configuration options
7346 // define the action interface
7347 Roo.form.Action = function(form, options){
7349 this.options = options || {};
7352 * Client Validation Failed
7355 Roo.form.Action.CLIENT_INVALID = 'client';
7357 * Server Validation Failed
7360 Roo.form.Action.SERVER_INVALID = 'server';
7362 * Connect to Server Failed
7365 Roo.form.Action.CONNECT_FAILURE = 'connect';
7367 * Reading Data from Server Failed
7370 Roo.form.Action.LOAD_FAILURE = 'load';
7372 Roo.form.Action.prototype = {
7374 failureType : undefined,
7375 response : undefined,
7379 run : function(options){
7384 success : function(response){
7389 handleResponse : function(response){
7393 // default connection failure
7394 failure : function(response){
7396 this.response = response;
7397 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7398 this.form.afterAction(this, false);
7401 processResponse : function(response){
7402 this.response = response;
7403 if(!response.responseText){
7406 this.result = this.handleResponse(response);
7410 // utility functions used internally
7411 getUrl : function(appendParams){
7412 var url = this.options.url || this.form.url || this.form.el.dom.action;
7414 var p = this.getParams();
7416 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7422 getMethod : function(){
7423 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7426 getParams : function(){
7427 var bp = this.form.baseParams;
7428 var p = this.options.params;
7430 if(typeof p == "object"){
7431 p = Roo.urlEncode(Roo.applyIf(p, bp));
7432 }else if(typeof p == 'string' && bp){
7433 p += '&' + Roo.urlEncode(bp);
7436 p = Roo.urlEncode(bp);
7441 createCallback : function(){
7443 success: this.success,
7444 failure: this.failure,
7446 timeout: (this.form.timeout*1000),
7447 upload: this.form.fileUpload ? this.success : undefined
7452 Roo.form.Action.Submit = function(form, options){
7453 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7456 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7459 haveProgress : false,
7460 uploadComplete : false,
7462 // uploadProgress indicator.
7463 uploadProgress : function()
7465 if (!this.form.progressUrl) {
7469 if (!this.haveProgress) {
7470 Roo.MessageBox.progress("Uploading", "Uploading");
7472 if (this.uploadComplete) {
7473 Roo.MessageBox.hide();
7477 this.haveProgress = true;
7479 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7481 var c = new Roo.data.Connection();
7483 url : this.form.progressUrl,
7488 success : function(req){
7489 //console.log(data);
7493 rdata = Roo.decode(req.responseText)
7495 Roo.log("Invalid data from server..");
7499 if (!rdata || !rdata.success) {
7501 Roo.MessageBox.alert(Roo.encode(rdata));
7504 var data = rdata.data;
7506 if (this.uploadComplete) {
7507 Roo.MessageBox.hide();
7512 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7513 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7516 this.uploadProgress.defer(2000,this);
7519 failure: function(data) {
7520 Roo.log('progress url failed ');
7531 // run get Values on the form, so it syncs any secondary forms.
7532 this.form.getValues();
7534 var o = this.options;
7535 var method = this.getMethod();
7536 var isPost = method == 'POST';
7537 if(o.clientValidation === false || this.form.isValid()){
7539 if (this.form.progressUrl) {
7540 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7541 (new Date() * 1) + '' + Math.random());
7546 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7547 form:this.form.el.dom,
7548 url:this.getUrl(!isPost),
7550 params:isPost ? this.getParams() : null,
7551 isUpload: this.form.fileUpload
7554 this.uploadProgress();
7556 }else if (o.clientValidation !== false){ // client validation failed
7557 this.failureType = Roo.form.Action.CLIENT_INVALID;
7558 this.form.afterAction(this, false);
7562 success : function(response)
7564 this.uploadComplete= true;
7565 if (this.haveProgress) {
7566 Roo.MessageBox.hide();
7570 var result = this.processResponse(response);
7571 if(result === true || result.success){
7572 this.form.afterAction(this, true);
7576 this.form.markInvalid(result.errors);
7577 this.failureType = Roo.form.Action.SERVER_INVALID;
7579 this.form.afterAction(this, false);
7581 failure : function(response)
7583 this.uploadComplete= true;
7584 if (this.haveProgress) {
7585 Roo.MessageBox.hide();
7588 this.response = response;
7589 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7590 this.form.afterAction(this, false);
7593 handleResponse : function(response){
7594 if(this.form.errorReader){
7595 var rs = this.form.errorReader.read(response);
7598 for(var i = 0, len = rs.records.length; i < len; i++) {
7599 var r = rs.records[i];
7603 if(errors.length < 1){
7607 success : rs.success,
7613 ret = Roo.decode(response.responseText);
7617 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7627 Roo.form.Action.Load = function(form, options){
7628 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7629 this.reader = this.form.reader;
7632 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7637 Roo.Ajax.request(Roo.apply(
7638 this.createCallback(), {
7639 method:this.getMethod(),
7640 url:this.getUrl(false),
7641 params:this.getParams()
7645 success : function(response){
7647 var result = this.processResponse(response);
7648 if(result === true || !result.success || !result.data){
7649 this.failureType = Roo.form.Action.LOAD_FAILURE;
7650 this.form.afterAction(this, false);
7653 this.form.clearInvalid();
7654 this.form.setValues(result.data);
7655 this.form.afterAction(this, true);
7658 handleResponse : function(response){
7659 if(this.form.reader){
7660 var rs = this.form.reader.read(response);
7661 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7663 success : rs.success,
7667 return Roo.decode(response.responseText);
7671 Roo.form.Action.ACTION_TYPES = {
7672 'load' : Roo.form.Action.Load,
7673 'submit' : Roo.form.Action.Submit
7682 * @class Roo.bootstrap.Form
7683 * @extends Roo.bootstrap.Component
7684 * Bootstrap Form class
7685 * @cfg {String} method GET | POST (default POST)
7686 * @cfg {String} labelAlign top | left (default top)
7687 * @cfg {String} align left | right - for navbars
7688 * @cfg {Boolean} loadMask load mask when submit (default true)
7693 * @param {Object} config The config object
7697 Roo.bootstrap.Form = function(config){
7699 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7701 Roo.bootstrap.Form.popover.apply();
7705 * @event clientvalidation
7706 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7707 * @param {Form} this
7708 * @param {Boolean} valid true if the form has passed client-side validation
7710 clientvalidation: true,
7712 * @event beforeaction
7713 * Fires before any action is performed. Return false to cancel the action.
7714 * @param {Form} this
7715 * @param {Action} action The action to be performed
7719 * @event actionfailed
7720 * Fires when an action fails.
7721 * @param {Form} this
7722 * @param {Action} action The action that failed
7724 actionfailed : true,
7726 * @event actioncomplete
7727 * Fires when an action is completed.
7728 * @param {Form} this
7729 * @param {Action} action The action that completed
7731 actioncomplete : true
7735 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7738 * @cfg {String} method
7739 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7744 * The URL to use for form actions if one isn't supplied in the action options.
7747 * @cfg {Boolean} fileUpload
7748 * Set to true if this form is a file upload.
7752 * @cfg {Object} baseParams
7753 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7757 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7761 * @cfg {Sting} align (left|right) for navbar forms
7766 activeAction : null,
7769 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7770 * element by passing it or its id or mask the form itself by passing in true.
7773 waitMsgTarget : false,
7778 * @cfg {Boolean} errorMask (true|false) default false
7783 * @cfg {Number} maskOffset Default 100
7788 * @cfg {Boolean} maskBody
7792 getAutoCreate : function(){
7796 method : this.method || 'POST',
7797 id : this.id || Roo.id(),
7800 if (this.parent().xtype.match(/^Nav/)) {
7801 cfg.cls = 'navbar-form navbar-' + this.align;
7805 if (this.labelAlign == 'left' ) {
7806 cfg.cls += ' form-horizontal';
7812 initEvents : function()
7814 this.el.on('submit', this.onSubmit, this);
7815 // this was added as random key presses on the form where triggering form submit.
7816 this.el.on('keypress', function(e) {
7817 if (e.getCharCode() != 13) {
7820 // we might need to allow it for textareas.. and some other items.
7821 // check e.getTarget().
7823 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7827 Roo.log("keypress blocked");
7835 onSubmit : function(e){
7840 * Returns true if client-side validation on the form is successful.
7843 isValid : function(){
7844 var items = this.getItems();
7848 items.each(function(f){
7854 Roo.log('invalid field: ' + f.name);
7858 if(!target && f.el.isVisible(true)){
7864 if(this.errorMask && !valid){
7865 Roo.bootstrap.Form.popover.mask(this, target);
7872 * Returns true if any fields in this form have changed since their original load.
7875 isDirty : function(){
7877 var items = this.getItems();
7878 items.each(function(f){
7888 * Performs a predefined action (submit or load) or custom actions you define on this form.
7889 * @param {String} actionName The name of the action type
7890 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7891 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7892 * accept other config options):
7894 Property Type Description
7895 ---------------- --------------- ----------------------------------------------------------------------------------
7896 url String The url for the action (defaults to the form's url)
7897 method String The form method to use (defaults to the form's method, or POST if not defined)
7898 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7899 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7900 validate the form on the client (defaults to false)
7902 * @return {BasicForm} this
7904 doAction : function(action, options){
7905 if(typeof action == 'string'){
7906 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7908 if(this.fireEvent('beforeaction', this, action) !== false){
7909 this.beforeAction(action);
7910 action.run.defer(100, action);
7916 beforeAction : function(action){
7917 var o = action.options;
7922 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7924 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7927 // not really supported yet.. ??
7929 //if(this.waitMsgTarget === true){
7930 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7931 //}else if(this.waitMsgTarget){
7932 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7933 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7935 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7941 afterAction : function(action, success){
7942 this.activeAction = null;
7943 var o = action.options;
7948 Roo.get(document.body).unmask();
7954 //if(this.waitMsgTarget === true){
7955 // this.el.unmask();
7956 //}else if(this.waitMsgTarget){
7957 // this.waitMsgTarget.unmask();
7959 // Roo.MessageBox.updateProgress(1);
7960 // Roo.MessageBox.hide();
7967 Roo.callback(o.success, o.scope, [this, action]);
7968 this.fireEvent('actioncomplete', this, action);
7972 // failure condition..
7973 // we have a scenario where updates need confirming.
7974 // eg. if a locking scenario exists..
7975 // we look for { errors : { needs_confirm : true }} in the response.
7977 (typeof(action.result) != 'undefined') &&
7978 (typeof(action.result.errors) != 'undefined') &&
7979 (typeof(action.result.errors.needs_confirm) != 'undefined')
7982 Roo.log("not supported yet");
7985 Roo.MessageBox.confirm(
7986 "Change requires confirmation",
7987 action.result.errorMsg,
7992 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8002 Roo.callback(o.failure, o.scope, [this, action]);
8003 // show an error message if no failed handler is set..
8004 if (!this.hasListener('actionfailed')) {
8005 Roo.log("need to add dialog support");
8007 Roo.MessageBox.alert("Error",
8008 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8009 action.result.errorMsg :
8010 "Saving Failed, please check your entries or try again"
8015 this.fireEvent('actionfailed', this, action);
8020 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8021 * @param {String} id The value to search for
8024 findField : function(id){
8025 var items = this.getItems();
8026 var field = items.get(id);
8028 items.each(function(f){
8029 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8036 return field || null;
8039 * Mark fields in this form invalid in bulk.
8040 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8041 * @return {BasicForm} this
8043 markInvalid : function(errors){
8044 if(errors instanceof Array){
8045 for(var i = 0, len = errors.length; i < len; i++){
8046 var fieldError = errors[i];
8047 var f = this.findField(fieldError.id);
8049 f.markInvalid(fieldError.msg);
8055 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8056 field.markInvalid(errors[id]);
8060 //Roo.each(this.childForms || [], function (f) {
8061 // f.markInvalid(errors);
8068 * Set values for fields in this form in bulk.
8069 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8070 * @return {BasicForm} this
8072 setValues : function(values){
8073 if(values instanceof Array){ // array of objects
8074 for(var i = 0, len = values.length; i < len; i++){
8076 var f = this.findField(v.id);
8078 f.setValue(v.value);
8079 if(this.trackResetOnLoad){
8080 f.originalValue = f.getValue();
8084 }else{ // object hash
8087 if(typeof values[id] != 'function' && (field = this.findField(id))){
8089 if (field.setFromData &&
8091 field.displayField &&
8092 // combos' with local stores can
8093 // be queried via setValue()
8094 // to set their value..
8095 (field.store && !field.store.isLocal)
8099 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8100 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8101 field.setFromData(sd);
8103 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8105 field.setFromData(values);
8108 field.setValue(values[id]);
8112 if(this.trackResetOnLoad){
8113 field.originalValue = field.getValue();
8119 //Roo.each(this.childForms || [], function (f) {
8120 // f.setValues(values);
8127 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8128 * they are returned as an array.
8129 * @param {Boolean} asString
8132 getValues : function(asString){
8133 //if (this.childForms) {
8134 // copy values from the child forms
8135 // Roo.each(this.childForms, function (f) {
8136 // this.setValues(f.getValues());
8142 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8143 if(asString === true){
8146 return Roo.urlDecode(fs);
8150 * Returns the fields in this form as an object with key/value pairs.
8151 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8154 getFieldValues : function(with_hidden)
8156 var items = this.getItems();
8158 items.each(function(f){
8164 var v = f.getValue();
8166 if (f.inputType =='radio') {
8167 if (typeof(ret[f.getName()]) == 'undefined') {
8168 ret[f.getName()] = ''; // empty..
8171 if (!f.el.dom.checked) {
8179 if(f.xtype == 'MoneyField'){
8180 ret[f.currencyName] = f.getCurrency();
8183 // not sure if this supported any more..
8184 if ((typeof(v) == 'object') && f.getRawValue) {
8185 v = f.getRawValue() ; // dates..
8187 // combo boxes where name != hiddenName...
8188 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8189 ret[f.name] = f.getRawValue();
8191 ret[f.getName()] = v;
8198 * Clears all invalid messages in this form.
8199 * @return {BasicForm} this
8201 clearInvalid : function(){
8202 var items = this.getItems();
8204 items.each(function(f){
8213 * @return {BasicForm} this
8216 var items = this.getItems();
8217 items.each(function(f){
8221 Roo.each(this.childForms || [], function (f) {
8229 getItems : function()
8231 var r=new Roo.util.MixedCollection(false, function(o){
8232 return o.id || (o.id = Roo.id());
8234 var iter = function(el) {
8241 Roo.each(el.items,function(e) {
8250 hideFields : function(items)
8252 Roo.each(items, function(i){
8254 var f = this.findField(i);
8260 if(f.xtype == 'DateField'){
8261 f.setVisible(false);
8270 showFields : function(items)
8272 Roo.each(items, function(i){
8274 var f = this.findField(i);
8280 if(f.xtype == 'DateField'){
8292 Roo.apply(Roo.bootstrap.Form, {
8319 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8320 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8321 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8322 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8325 this.maskEl.top.enableDisplayMode("block");
8326 this.maskEl.left.enableDisplayMode("block");
8327 this.maskEl.bottom.enableDisplayMode("block");
8328 this.maskEl.right.enableDisplayMode("block");
8330 this.toolTip = new Roo.bootstrap.Tooltip({
8331 cls : 'roo-form-error-popover',
8333 'left' : ['r-l', [-2,0], 'right'],
8334 'right' : ['l-r', [2,0], 'left'],
8335 'bottom' : ['tl-bl', [0,2], 'top'],
8336 'top' : [ 'bl-tl', [0,-2], 'bottom']
8340 this.toolTip.render(Roo.get(document.body));
8342 this.toolTip.el.enableDisplayMode("block");
8344 Roo.get(document.body).on('click', function(){
8348 Roo.get(document.body).on('touchstart', function(){
8352 this.isApplied = true
8355 mask : function(form, target)
8359 this.target = target;
8361 if(!this.form.errorMask || !target.el){
8365 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8367 Roo.log(scrollable);
8369 var ot = this.target.el.calcOffsetsTo(scrollable);
8371 var scrollTo = ot[1] - this.form.maskOffset;
8373 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8375 scrollable.scrollTo('top', scrollTo);
8377 var box = this.target.el.getBox();
8379 var zIndex = Roo.bootstrap.Modal.zIndex++;
8382 this.maskEl.top.setStyle('position', 'absolute');
8383 this.maskEl.top.setStyle('z-index', zIndex);
8384 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8385 this.maskEl.top.setLeft(0);
8386 this.maskEl.top.setTop(0);
8387 this.maskEl.top.show();
8389 this.maskEl.left.setStyle('position', 'absolute');
8390 this.maskEl.left.setStyle('z-index', zIndex);
8391 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8392 this.maskEl.left.setLeft(0);
8393 this.maskEl.left.setTop(box.y - this.padding);
8394 this.maskEl.left.show();
8396 this.maskEl.bottom.setStyle('position', 'absolute');
8397 this.maskEl.bottom.setStyle('z-index', zIndex);
8398 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8399 this.maskEl.bottom.setLeft(0);
8400 this.maskEl.bottom.setTop(box.bottom + this.padding);
8401 this.maskEl.bottom.show();
8403 this.maskEl.right.setStyle('position', 'absolute');
8404 this.maskEl.right.setStyle('z-index', zIndex);
8405 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8406 this.maskEl.right.setLeft(box.right + this.padding);
8407 this.maskEl.right.setTop(box.y - this.padding);
8408 this.maskEl.right.show();
8410 this.toolTip.bindEl = this.target.el;
8412 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8414 var tip = this.target.blankText;
8416 if(this.target.getValue() !== '' ) {
8418 if (this.target.invalidText.length) {
8419 tip = this.target.invalidText;
8420 } else if (this.target.regexText.length){
8421 tip = this.target.regexText;
8425 this.toolTip.show(tip);
8427 this.intervalID = window.setInterval(function() {
8428 Roo.bootstrap.Form.popover.unmask();
8431 window.onwheel = function(){ return false;};
8433 (function(){ this.isMasked = true; }).defer(500, this);
8439 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8443 this.maskEl.top.setStyle('position', 'absolute');
8444 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8445 this.maskEl.top.hide();
8447 this.maskEl.left.setStyle('position', 'absolute');
8448 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8449 this.maskEl.left.hide();
8451 this.maskEl.bottom.setStyle('position', 'absolute');
8452 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8453 this.maskEl.bottom.hide();
8455 this.maskEl.right.setStyle('position', 'absolute');
8456 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8457 this.maskEl.right.hide();
8459 this.toolTip.hide();
8461 this.toolTip.el.hide();
8463 window.onwheel = function(){ return true;};
8465 if(this.intervalID){
8466 window.clearInterval(this.intervalID);
8467 this.intervalID = false;
8470 this.isMasked = false;
8480 * Ext JS Library 1.1.1
8481 * Copyright(c) 2006-2007, Ext JS, LLC.
8483 * Originally Released Under LGPL - original licence link has changed is not relivant.
8486 * <script type="text/javascript">
8489 * @class Roo.form.VTypes
8490 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8493 Roo.form.VTypes = function(){
8494 // closure these in so they are only created once.
8495 var alpha = /^[a-zA-Z_]+$/;
8496 var alphanum = /^[a-zA-Z0-9_]+$/;
8497 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8498 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8500 // All these messages and functions are configurable
8503 * The function used to validate email addresses
8504 * @param {String} value The email address
8506 'email' : function(v){
8507 return email.test(v);
8510 * The error text to display when the email validation function returns false
8513 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8515 * The keystroke filter mask to be applied on email input
8518 'emailMask' : /[a-z0-9_\.\-@]/i,
8521 * The function used to validate URLs
8522 * @param {String} value The URL
8524 'url' : function(v){
8528 * The error text to display when the url validation function returns false
8531 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8534 * The function used to validate alpha values
8535 * @param {String} value The value
8537 'alpha' : function(v){
8538 return alpha.test(v);
8541 * The error text to display when the alpha validation function returns false
8544 'alphaText' : 'This field should only contain letters and _',
8546 * The keystroke filter mask to be applied on alpha input
8549 'alphaMask' : /[a-z_]/i,
8552 * The function used to validate alphanumeric values
8553 * @param {String} value The value
8555 'alphanum' : function(v){
8556 return alphanum.test(v);
8559 * The error text to display when the alphanumeric validation function returns false
8562 'alphanumText' : 'This field should only contain letters, numbers and _',
8564 * The keystroke filter mask to be applied on alphanumeric input
8567 'alphanumMask' : /[a-z0-9_]/i
8577 * @class Roo.bootstrap.Input
8578 * @extends Roo.bootstrap.Component
8579 * Bootstrap Input class
8580 * @cfg {Boolean} disabled is it disabled
8581 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8582 * @cfg {String} name name of the input
8583 * @cfg {string} fieldLabel - the label associated
8584 * @cfg {string} placeholder - placeholder to put in text.
8585 * @cfg {string} before - input group add on before
8586 * @cfg {string} after - input group add on after
8587 * @cfg {string} size - (lg|sm) or leave empty..
8588 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8589 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8590 * @cfg {Number} md colspan out of 12 for computer-sized screens
8591 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8592 * @cfg {string} value default value of the input
8593 * @cfg {Number} labelWidth set the width of label
8594 * @cfg {Number} labellg set the width of label (1-12)
8595 * @cfg {Number} labelmd set the width of label (1-12)
8596 * @cfg {Number} labelsm set the width of label (1-12)
8597 * @cfg {Number} labelxs set the width of label (1-12)
8598 * @cfg {String} labelAlign (top|left)
8599 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8600 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8601 * @cfg {String} indicatorpos (left|right) default left
8602 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8603 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8605 * @cfg {String} align (left|center|right) Default left
8606 * @cfg {Boolean} forceFeedback (true|false) Default false
8609 * Create a new Input
8610 * @param {Object} config The config object
8613 Roo.bootstrap.Input = function(config){
8615 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8620 * Fires when this field receives input focus.
8621 * @param {Roo.form.Field} this
8626 * Fires when this field loses input focus.
8627 * @param {Roo.form.Field} this
8632 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8633 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8634 * @param {Roo.form.Field} this
8635 * @param {Roo.EventObject} e The event object
8640 * Fires just before the field blurs if the field value has changed.
8641 * @param {Roo.form.Field} this
8642 * @param {Mixed} newValue The new value
8643 * @param {Mixed} oldValue The original value
8648 * Fires after the field has been marked as invalid.
8649 * @param {Roo.form.Field} this
8650 * @param {String} msg The validation message
8655 * Fires after the field has been validated with no errors.
8656 * @param {Roo.form.Field} this
8661 * Fires after the key up
8662 * @param {Roo.form.Field} this
8663 * @param {Roo.EventObject} e The event Object
8669 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8671 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8672 automatic validation (defaults to "keyup").
8674 validationEvent : "keyup",
8676 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8678 validateOnBlur : true,
8680 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8682 validationDelay : 250,
8684 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8686 focusClass : "x-form-focus", // not needed???
8690 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8692 invalidClass : "has-warning",
8695 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8697 validClass : "has-success",
8700 * @cfg {Boolean} hasFeedback (true|false) default true
8705 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8707 invalidFeedbackClass : "glyphicon-warning-sign",
8710 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8712 validFeedbackClass : "glyphicon-ok",
8715 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8717 selectOnFocus : false,
8720 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8724 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8729 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8731 disableKeyFilter : false,
8734 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8738 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8742 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8744 blankText : "Please complete this mandatory field",
8747 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8751 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8753 maxLength : Number.MAX_VALUE,
8755 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8757 minLengthText : "The minimum length for this field is {0}",
8759 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8761 maxLengthText : "The maximum length for this field is {0}",
8765 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8766 * If available, this function will be called only after the basic validators all return true, and will be passed the
8767 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8771 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8772 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8773 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8777 * @cfg {String} regexText -- Depricated - use Invalid Text
8782 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8788 autocomplete: false,
8807 formatedValue : false,
8808 forceFeedback : false,
8810 indicatorpos : 'left',
8820 parentLabelAlign : function()
8823 while (parent.parent()) {
8824 parent = parent.parent();
8825 if (typeof(parent.labelAlign) !='undefined') {
8826 return parent.labelAlign;
8833 getAutoCreate : function()
8835 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8841 if(this.inputType != 'hidden'){
8842 cfg.cls = 'form-group' //input-group
8848 type : this.inputType,
8850 cls : 'form-control',
8851 placeholder : this.placeholder || '',
8852 autocomplete : this.autocomplete || 'new-password'
8855 if(this.capture.length){
8856 input.capture = this.capture;
8859 if(this.accept.length){
8860 input.accept = this.accept + "/*";
8864 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8867 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8868 input.maxLength = this.maxLength;
8871 if (this.disabled) {
8872 input.disabled=true;
8875 if (this.readOnly) {
8876 input.readonly=true;
8880 input.name = this.name;
8884 input.cls += ' input-' + this.size;
8888 ['xs','sm','md','lg'].map(function(size){
8889 if (settings[size]) {
8890 cfg.cls += ' col-' + size + '-' + settings[size];
8894 var inputblock = input;
8898 cls: 'glyphicon form-control-feedback'
8901 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8904 cls : 'has-feedback',
8912 if (this.before || this.after) {
8915 cls : 'input-group',
8919 if (this.before && typeof(this.before) == 'string') {
8921 inputblock.cn.push({
8923 cls : 'roo-input-before input-group-addon',
8927 if (this.before && typeof(this.before) == 'object') {
8928 this.before = Roo.factory(this.before);
8930 inputblock.cn.push({
8932 cls : 'roo-input-before input-group-' +
8933 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8937 inputblock.cn.push(input);
8939 if (this.after && typeof(this.after) == 'string') {
8940 inputblock.cn.push({
8942 cls : 'roo-input-after input-group-addon',
8946 if (this.after && typeof(this.after) == 'object') {
8947 this.after = Roo.factory(this.after);
8949 inputblock.cn.push({
8951 cls : 'roo-input-after input-group-' +
8952 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8956 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8957 inputblock.cls += ' has-feedback';
8958 inputblock.cn.push(feedback);
8962 if (align ==='left' && this.fieldLabel.length) {
8964 cfg.cls += ' roo-form-group-label-left';
8969 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8970 tooltip : 'This field is required'
8975 cls : 'control-label',
8976 html : this.fieldLabel
8987 var labelCfg = cfg.cn[1];
8988 var contentCfg = cfg.cn[2];
8990 if(this.indicatorpos == 'right'){
8995 cls : 'control-label',
8999 html : this.fieldLabel
9003 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9004 tooltip : 'This field is required'
9017 labelCfg = cfg.cn[0];
9018 contentCfg = cfg.cn[1];
9022 if(this.labelWidth > 12){
9023 labelCfg.style = "width: " + this.labelWidth + 'px';
9026 if(this.labelWidth < 13 && this.labelmd == 0){
9027 this.labelmd = this.labelWidth;
9030 if(this.labellg > 0){
9031 labelCfg.cls += ' col-lg-' + this.labellg;
9032 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9035 if(this.labelmd > 0){
9036 labelCfg.cls += ' col-md-' + this.labelmd;
9037 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9040 if(this.labelsm > 0){
9041 labelCfg.cls += ' col-sm-' + this.labelsm;
9042 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9045 if(this.labelxs > 0){
9046 labelCfg.cls += ' col-xs-' + this.labelxs;
9047 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9051 } else if ( this.fieldLabel.length) {
9056 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9057 tooltip : 'This field is required'
9061 //cls : 'input-group-addon',
9062 html : this.fieldLabel
9070 if(this.indicatorpos == 'right'){
9075 //cls : 'input-group-addon',
9076 html : this.fieldLabel
9081 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9082 tooltip : 'This field is required'
9102 if (this.parentType === 'Navbar' && this.parent().bar) {
9103 cfg.cls += ' navbar-form';
9106 if (this.parentType === 'NavGroup') {
9107 cfg.cls += ' navbar-form';
9115 * return the real input element.
9117 inputEl: function ()
9119 return this.el.select('input.form-control',true).first();
9122 tooltipEl : function()
9124 return this.inputEl();
9127 indicatorEl : function()
9129 var indicator = this.el.select('i.roo-required-indicator',true).first();
9139 setDisabled : function(v)
9141 var i = this.inputEl().dom;
9143 i.removeAttribute('disabled');
9147 i.setAttribute('disabled','true');
9149 initEvents : function()
9152 this.inputEl().on("keydown" , this.fireKey, this);
9153 this.inputEl().on("focus", this.onFocus, this);
9154 this.inputEl().on("blur", this.onBlur, this);
9156 this.inputEl().relayEvent('keyup', this);
9158 this.indicator = this.indicatorEl();
9161 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9164 // reference to original value for reset
9165 this.originalValue = this.getValue();
9166 //Roo.form.TextField.superclass.initEvents.call(this);
9167 if(this.validationEvent == 'keyup'){
9168 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9169 this.inputEl().on('keyup', this.filterValidation, this);
9171 else if(this.validationEvent !== false){
9172 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9175 if(this.selectOnFocus){
9176 this.on("focus", this.preFocus, this);
9179 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9180 this.inputEl().on("keypress", this.filterKeys, this);
9182 this.inputEl().relayEvent('keypress', this);
9185 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9186 this.el.on("click", this.autoSize, this);
9189 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9190 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9193 if (typeof(this.before) == 'object') {
9194 this.before.render(this.el.select('.roo-input-before',true).first());
9196 if (typeof(this.after) == 'object') {
9197 this.after.render(this.el.select('.roo-input-after',true).first());
9200 this.inputEl().on('change', this.onChange, this);
9203 filterValidation : function(e){
9204 if(!e.isNavKeyPress()){
9205 this.validationTask.delay(this.validationDelay);
9209 * Validates the field value
9210 * @return {Boolean} True if the value is valid, else false
9212 validate : function(){
9213 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9214 if(this.disabled || this.validateValue(this.getRawValue())){
9225 * Validates a value according to the field's validation rules and marks the field as invalid
9226 * if the validation fails
9227 * @param {Mixed} value The value to validate
9228 * @return {Boolean} True if the value is valid, else false
9230 validateValue : function(value)
9232 if(this.getVisibilityEl().hasClass('hidden')){
9236 if(value.length < 1) { // if it's blank
9237 if(this.allowBlank){
9243 if(value.length < this.minLength){
9246 if(value.length > this.maxLength){
9250 var vt = Roo.form.VTypes;
9251 if(!vt[this.vtype](value, this)){
9255 if(typeof this.validator == "function"){
9256 var msg = this.validator(value);
9260 if (typeof(msg) == 'string') {
9261 this.invalidText = msg;
9265 if(this.regex && !this.regex.test(value)){
9273 fireKey : function(e){
9274 //Roo.log('field ' + e.getKey());
9275 if(e.isNavKeyPress()){
9276 this.fireEvent("specialkey", this, e);
9279 focus : function (selectText){
9281 this.inputEl().focus();
9282 if(selectText === true){
9283 this.inputEl().dom.select();
9289 onFocus : function(){
9290 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9291 // this.el.addClass(this.focusClass);
9294 this.hasFocus = true;
9295 this.startValue = this.getValue();
9296 this.fireEvent("focus", this);
9300 beforeBlur : Roo.emptyFn,
9304 onBlur : function(){
9306 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9307 //this.el.removeClass(this.focusClass);
9309 this.hasFocus = false;
9310 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9313 var v = this.getValue();
9314 if(String(v) !== String(this.startValue)){
9315 this.fireEvent('change', this, v, this.startValue);
9317 this.fireEvent("blur", this);
9320 onChange : function(e)
9322 var v = this.getValue();
9323 if(String(v) !== String(this.startValue)){
9324 this.fireEvent('change', this, v, this.startValue);
9330 * Resets the current field value to the originally loaded value and clears any validation messages
9333 this.setValue(this.originalValue);
9337 * Returns the name of the field
9338 * @return {Mixed} name The name field
9340 getName: function(){
9344 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9345 * @return {Mixed} value The field value
9347 getValue : function(){
9349 var v = this.inputEl().getValue();
9354 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9355 * @return {Mixed} value The field value
9357 getRawValue : function(){
9358 var v = this.inputEl().getValue();
9364 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9365 * @param {Mixed} value The value to set
9367 setRawValue : function(v){
9368 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9371 selectText : function(start, end){
9372 var v = this.getRawValue();
9374 start = start === undefined ? 0 : start;
9375 end = end === undefined ? v.length : end;
9376 var d = this.inputEl().dom;
9377 if(d.setSelectionRange){
9378 d.setSelectionRange(start, end);
9379 }else if(d.createTextRange){
9380 var range = d.createTextRange();
9381 range.moveStart("character", start);
9382 range.moveEnd("character", v.length-end);
9389 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9390 * @param {Mixed} value The value to set
9392 setValue : function(v){
9395 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9401 processValue : function(value){
9402 if(this.stripCharsRe){
9403 var newValue = value.replace(this.stripCharsRe, '');
9404 if(newValue !== value){
9405 this.setRawValue(newValue);
9412 preFocus : function(){
9414 if(this.selectOnFocus){
9415 this.inputEl().dom.select();
9418 filterKeys : function(e){
9420 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9423 var c = e.getCharCode(), cc = String.fromCharCode(c);
9424 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9427 if(!this.maskRe.test(cc)){
9432 * Clear any invalid styles/messages for this field
9434 clearInvalid : function(){
9436 if(!this.el || this.preventMark){ // not rendered
9441 this.el.removeClass(this.invalidClass);
9443 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9445 var feedback = this.el.select('.form-control-feedback', true).first();
9448 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9454 this.indicator.removeClass('visible');
9455 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9458 this.fireEvent('valid', this);
9462 * Mark this field as valid
9464 markValid : function()
9466 if(!this.el || this.preventMark){ // not rendered...
9470 this.el.removeClass([this.invalidClass, this.validClass]);
9472 var feedback = this.el.select('.form-control-feedback', true).first();
9475 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9479 this.indicator.removeClass('visible');
9480 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9487 if(this.allowBlank && !this.getRawValue().length){
9491 this.el.addClass(this.validClass);
9493 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9495 var feedback = this.el.select('.form-control-feedback', true).first();
9498 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9499 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9504 this.fireEvent('valid', this);
9508 * Mark this field as invalid
9509 * @param {String} msg The validation message
9511 markInvalid : function(msg)
9513 if(!this.el || this.preventMark){ // not rendered
9517 this.el.removeClass([this.invalidClass, this.validClass]);
9519 var feedback = this.el.select('.form-control-feedback', true).first();
9522 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529 if(this.allowBlank && !this.getRawValue().length){
9534 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9535 this.indicator.addClass('visible');
9538 this.el.addClass(this.invalidClass);
9540 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9542 var feedback = this.el.select('.form-control-feedback', true).first();
9545 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9547 if(this.getValue().length || this.forceFeedback){
9548 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9555 this.fireEvent('invalid', this, msg);
9558 SafariOnKeyDown : function(event)
9560 // this is a workaround for a password hang bug on chrome/ webkit.
9561 if (this.inputEl().dom.type != 'password') {
9565 var isSelectAll = false;
9567 if(this.inputEl().dom.selectionEnd > 0){
9568 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9570 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9571 event.preventDefault();
9576 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9578 event.preventDefault();
9579 // this is very hacky as keydown always get's upper case.
9581 var cc = String.fromCharCode(event.getCharCode());
9582 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9586 adjustWidth : function(tag, w){
9587 tag = tag.toLowerCase();
9588 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9589 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9593 if(tag == 'textarea'){
9596 }else if(Roo.isOpera){
9600 if(tag == 'textarea'){
9608 setFieldLabel : function(v)
9615 var ar = this.el.select('label > span',true);
9617 if (ar.elements.length) {
9618 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9619 this.fieldLabel = v;
9623 var br = this.el.select('label',true);
9625 if(br.elements.length) {
9626 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9627 this.fieldLabel = v;
9631 Roo.log('Cannot Found any of label > span || label in input');
9635 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9636 this.fieldLabel = v;
9651 * @class Roo.bootstrap.TextArea
9652 * @extends Roo.bootstrap.Input
9653 * Bootstrap TextArea class
9654 * @cfg {Number} cols Specifies the visible width of a text area
9655 * @cfg {Number} rows Specifies the visible number of lines in a text area
9656 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9657 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9658 * @cfg {string} html text
9661 * Create a new TextArea
9662 * @param {Object} config The config object
9665 Roo.bootstrap.TextArea = function(config){
9666 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9670 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9680 getAutoCreate : function(){
9682 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9688 if(this.inputType != 'hidden'){
9689 cfg.cls = 'form-group' //input-group
9697 value : this.value || '',
9698 html: this.html || '',
9699 cls : 'form-control',
9700 placeholder : this.placeholder || ''
9704 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9705 input.maxLength = this.maxLength;
9709 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9713 input.cols = this.cols;
9716 if (this.readOnly) {
9717 input.readonly = true;
9721 input.name = this.name;
9725 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9729 ['xs','sm','md','lg'].map(function(size){
9730 if (settings[size]) {
9731 cfg.cls += ' col-' + size + '-' + settings[size];
9735 var inputblock = input;
9737 if(this.hasFeedback && !this.allowBlank){
9741 cls: 'glyphicon form-control-feedback'
9745 cls : 'has-feedback',
9754 if (this.before || this.after) {
9757 cls : 'input-group',
9761 inputblock.cn.push({
9763 cls : 'input-group-addon',
9768 inputblock.cn.push(input);
9770 if(this.hasFeedback && !this.allowBlank){
9771 inputblock.cls += ' has-feedback';
9772 inputblock.cn.push(feedback);
9776 inputblock.cn.push({
9778 cls : 'input-group-addon',
9785 if (align ==='left' && this.fieldLabel.length) {
9790 cls : 'control-label',
9791 html : this.fieldLabel
9802 if(this.labelWidth > 12){
9803 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9806 if(this.labelWidth < 13 && this.labelmd == 0){
9807 this.labelmd = this.labelWidth;
9810 if(this.labellg > 0){
9811 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9812 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9815 if(this.labelmd > 0){
9816 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9817 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9820 if(this.labelsm > 0){
9821 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9822 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9825 if(this.labelxs > 0){
9826 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9827 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9830 } else if ( this.fieldLabel.length) {
9835 //cls : 'input-group-addon',
9836 html : this.fieldLabel
9854 if (this.disabled) {
9855 input.disabled=true;
9862 * return the real textarea element.
9864 inputEl: function ()
9866 return this.el.select('textarea.form-control',true).first();
9870 * Clear any invalid styles/messages for this field
9872 clearInvalid : function()
9875 if(!this.el || this.preventMark){ // not rendered
9879 var label = this.el.select('label', true).first();
9880 var icon = this.el.select('i.fa-star', true).first();
9886 this.el.removeClass(this.invalidClass);
9888 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9890 var feedback = this.el.select('.form-control-feedback', true).first();
9893 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9898 this.fireEvent('valid', this);
9902 * Mark this field as valid
9904 markValid : function()
9906 if(!this.el || this.preventMark){ // not rendered
9910 this.el.removeClass([this.invalidClass, this.validClass]);
9912 var feedback = this.el.select('.form-control-feedback', true).first();
9915 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9918 if(this.disabled || this.allowBlank){
9922 var label = this.el.select('label', true).first();
9923 var icon = this.el.select('i.fa-star', true).first();
9929 this.el.addClass(this.validClass);
9931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9933 var feedback = this.el.select('.form-control-feedback', true).first();
9936 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9937 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9942 this.fireEvent('valid', this);
9946 * Mark this field as invalid
9947 * @param {String} msg The validation message
9949 markInvalid : function(msg)
9951 if(!this.el || this.preventMark){ // not rendered
9955 this.el.removeClass([this.invalidClass, this.validClass]);
9957 var feedback = this.el.select('.form-control-feedback', true).first();
9960 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9963 if(this.disabled || this.allowBlank){
9967 var label = this.el.select('label', true).first();
9968 var icon = this.el.select('i.fa-star', true).first();
9970 if(!this.getValue().length && label && !icon){
9971 this.el.createChild({
9973 cls : 'text-danger fa fa-lg fa-star',
9974 tooltip : 'This field is required',
9975 style : 'margin-right:5px;'
9979 this.el.addClass(this.invalidClass);
9981 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9983 var feedback = this.el.select('.form-control-feedback', true).first();
9986 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9988 if(this.getValue().length || this.forceFeedback){
9989 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9996 this.fireEvent('invalid', this, msg);
10004 * trigger field - base class for combo..
10009 * @class Roo.bootstrap.TriggerField
10010 * @extends Roo.bootstrap.Input
10011 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10012 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10013 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10014 * for which you can provide a custom implementation. For example:
10016 var trigger = new Roo.bootstrap.TriggerField();
10017 trigger.onTriggerClick = myTriggerFn;
10018 trigger.applyTo('my-field');
10021 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10022 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10023 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10024 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10025 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10028 * Create a new TriggerField.
10029 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10030 * to the base TextField)
10032 Roo.bootstrap.TriggerField = function(config){
10033 this.mimicing = false;
10034 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10037 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10039 * @cfg {String} triggerClass A CSS class to apply to the trigger
10042 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10047 * @cfg {Boolean} removable (true|false) special filter default false
10051 /** @cfg {Boolean} grow @hide */
10052 /** @cfg {Number} growMin @hide */
10053 /** @cfg {Number} growMax @hide */
10059 autoSize: Roo.emptyFn,
10063 deferHeight : true,
10066 actionMode : 'wrap',
10071 getAutoCreate : function(){
10073 var align = this.labelAlign || this.parentLabelAlign();
10078 cls: 'form-group' //input-group
10085 type : this.inputType,
10086 cls : 'form-control',
10087 autocomplete: 'new-password',
10088 placeholder : this.placeholder || ''
10092 input.name = this.name;
10095 input.cls += ' input-' + this.size;
10098 if (this.disabled) {
10099 input.disabled=true;
10102 var inputblock = input;
10104 if(this.hasFeedback && !this.allowBlank){
10108 cls: 'glyphicon form-control-feedback'
10111 if(this.removable && !this.editable && !this.tickable){
10113 cls : 'has-feedback',
10119 cls : 'roo-combo-removable-btn close'
10126 cls : 'has-feedback',
10135 if(this.removable && !this.editable && !this.tickable){
10137 cls : 'roo-removable',
10143 cls : 'roo-combo-removable-btn close'
10150 if (this.before || this.after) {
10153 cls : 'input-group',
10157 inputblock.cn.push({
10159 cls : 'input-group-addon',
10164 inputblock.cn.push(input);
10166 if(this.hasFeedback && !this.allowBlank){
10167 inputblock.cls += ' has-feedback';
10168 inputblock.cn.push(feedback);
10172 inputblock.cn.push({
10174 cls : 'input-group-addon',
10187 cls: 'form-hidden-field'
10201 cls: 'form-hidden-field'
10205 cls: 'roo-select2-choices',
10209 cls: 'roo-select2-search-field',
10222 cls: 'roo-select2-container input-group',
10227 // cls: 'typeahead typeahead-long dropdown-menu',
10228 // style: 'display:none'
10233 if(!this.multiple && this.showToggleBtn){
10239 if (this.caret != false) {
10242 cls: 'fa fa-' + this.caret
10249 cls : 'input-group-addon btn dropdown-toggle',
10254 cls: 'combobox-clear',
10268 combobox.cls += ' roo-select2-container-multi';
10271 if (align ==='left' && this.fieldLabel.length) {
10273 cfg.cls += ' roo-form-group-label-left';
10278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10279 tooltip : 'This field is required'
10284 cls : 'control-label',
10285 html : this.fieldLabel
10297 var labelCfg = cfg.cn[1];
10298 var contentCfg = cfg.cn[2];
10300 if(this.indicatorpos == 'right'){
10305 cls : 'control-label',
10309 html : this.fieldLabel
10313 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10314 tooltip : 'This field is required'
10327 labelCfg = cfg.cn[0];
10328 contentCfg = cfg.cn[1];
10331 if(this.labelWidth > 12){
10332 labelCfg.style = "width: " + this.labelWidth + 'px';
10335 if(this.labelWidth < 13 && this.labelmd == 0){
10336 this.labelmd = this.labelWidth;
10339 if(this.labellg > 0){
10340 labelCfg.cls += ' col-lg-' + this.labellg;
10341 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10344 if(this.labelmd > 0){
10345 labelCfg.cls += ' col-md-' + this.labelmd;
10346 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10349 if(this.labelsm > 0){
10350 labelCfg.cls += ' col-sm-' + this.labelsm;
10351 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10354 if(this.labelxs > 0){
10355 labelCfg.cls += ' col-xs-' + this.labelxs;
10356 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10359 } else if ( this.fieldLabel.length) {
10360 // Roo.log(" label");
10364 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10365 tooltip : 'This field is required'
10369 //cls : 'input-group-addon',
10370 html : this.fieldLabel
10378 if(this.indicatorpos == 'right'){
10386 html : this.fieldLabel
10390 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10391 tooltip : 'This field is required'
10404 // Roo.log(" no label && no align");
10411 ['xs','sm','md','lg'].map(function(size){
10412 if (settings[size]) {
10413 cfg.cls += ' col-' + size + '-' + settings[size];
10424 onResize : function(w, h){
10425 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10426 // if(typeof w == 'number'){
10427 // var x = w - this.trigger.getWidth();
10428 // this.inputEl().setWidth(this.adjustWidth('input', x));
10429 // this.trigger.setStyle('left', x+'px');
10434 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10437 getResizeEl : function(){
10438 return this.inputEl();
10442 getPositionEl : function(){
10443 return this.inputEl();
10447 alignErrorIcon : function(){
10448 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10452 initEvents : function(){
10456 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10457 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10458 if(!this.multiple && this.showToggleBtn){
10459 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10460 if(this.hideTrigger){
10461 this.trigger.setDisplayed(false);
10463 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10467 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10470 if(this.removable && !this.editable && !this.tickable){
10471 var close = this.closeTriggerEl();
10474 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10475 close.on('click', this.removeBtnClick, this, close);
10479 //this.trigger.addClassOnOver('x-form-trigger-over');
10480 //this.trigger.addClassOnClick('x-form-trigger-click');
10483 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10487 closeTriggerEl : function()
10489 var close = this.el.select('.roo-combo-removable-btn', true).first();
10490 return close ? close : false;
10493 removeBtnClick : function(e, h, el)
10495 e.preventDefault();
10497 if(this.fireEvent("remove", this) !== false){
10499 this.fireEvent("afterremove", this)
10503 createList : function()
10505 this.list = Roo.get(document.body).createChild({
10507 cls: 'typeahead typeahead-long dropdown-menu',
10508 style: 'display:none'
10511 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10516 initTrigger : function(){
10521 onDestroy : function(){
10523 this.trigger.removeAllListeners();
10524 // this.trigger.remove();
10527 // this.wrap.remove();
10529 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10533 onFocus : function(){
10534 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10536 if(!this.mimicing){
10537 this.wrap.addClass('x-trigger-wrap-focus');
10538 this.mimicing = true;
10539 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10540 if(this.monitorTab){
10541 this.el.on("keydown", this.checkTab, this);
10548 checkTab : function(e){
10549 if(e.getKey() == e.TAB){
10550 this.triggerBlur();
10555 onBlur : function(){
10560 mimicBlur : function(e, t){
10562 if(!this.wrap.contains(t) && this.validateBlur()){
10563 this.triggerBlur();
10569 triggerBlur : function(){
10570 this.mimicing = false;
10571 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10572 if(this.monitorTab){
10573 this.el.un("keydown", this.checkTab, this);
10575 //this.wrap.removeClass('x-trigger-wrap-focus');
10576 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10580 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10581 validateBlur : function(e, t){
10586 onDisable : function(){
10587 this.inputEl().dom.disabled = true;
10588 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10590 // this.wrap.addClass('x-item-disabled');
10595 onEnable : function(){
10596 this.inputEl().dom.disabled = false;
10597 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10599 // this.el.removeClass('x-item-disabled');
10604 onShow : function(){
10605 var ae = this.getActionEl();
10608 ae.dom.style.display = '';
10609 ae.dom.style.visibility = 'visible';
10615 onHide : function(){
10616 var ae = this.getActionEl();
10617 ae.dom.style.display = 'none';
10621 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10622 * by an implementing function.
10624 * @param {EventObject} e
10626 onTriggerClick : Roo.emptyFn
10630 * Ext JS Library 1.1.1
10631 * Copyright(c) 2006-2007, Ext JS, LLC.
10633 * Originally Released Under LGPL - original licence link has changed is not relivant.
10636 * <script type="text/javascript">
10641 * @class Roo.data.SortTypes
10643 * Defines the default sorting (casting?) comparison functions used when sorting data.
10645 Roo.data.SortTypes = {
10647 * Default sort that does nothing
10648 * @param {Mixed} s The value being converted
10649 * @return {Mixed} The comparison value
10651 none : function(s){
10656 * The regular expression used to strip tags
10660 stripTagsRE : /<\/?[^>]+>/gi,
10663 * Strips all HTML tags to sort on text only
10664 * @param {Mixed} s The value being converted
10665 * @return {String} The comparison value
10667 asText : function(s){
10668 return String(s).replace(this.stripTagsRE, "");
10672 * Strips all HTML tags to sort on text only - Case insensitive
10673 * @param {Mixed} s The value being converted
10674 * @return {String} The comparison value
10676 asUCText : function(s){
10677 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10681 * Case insensitive string
10682 * @param {Mixed} s The value being converted
10683 * @return {String} The comparison value
10685 asUCString : function(s) {
10686 return String(s).toUpperCase();
10691 * @param {Mixed} s The value being converted
10692 * @return {Number} The comparison value
10694 asDate : function(s) {
10698 if(s instanceof Date){
10699 return s.getTime();
10701 return Date.parse(String(s));
10706 * @param {Mixed} s The value being converted
10707 * @return {Float} The comparison value
10709 asFloat : function(s) {
10710 var val = parseFloat(String(s).replace(/,/g, ""));
10719 * @param {Mixed} s The value being converted
10720 * @return {Number} The comparison value
10722 asInt : function(s) {
10723 var val = parseInt(String(s).replace(/,/g, ""));
10731 * Ext JS Library 1.1.1
10732 * Copyright(c) 2006-2007, Ext JS, LLC.
10734 * Originally Released Under LGPL - original licence link has changed is not relivant.
10737 * <script type="text/javascript">
10741 * @class Roo.data.Record
10742 * Instances of this class encapsulate both record <em>definition</em> information, and record
10743 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10744 * to access Records cached in an {@link Roo.data.Store} object.<br>
10746 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10747 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10750 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10752 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10753 * {@link #create}. The parameters are the same.
10754 * @param {Array} data An associative Array of data values keyed by the field name.
10755 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10756 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10757 * not specified an integer id is generated.
10759 Roo.data.Record = function(data, id){
10760 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10765 * Generate a constructor for a specific record layout.
10766 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10767 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10768 * Each field definition object may contain the following properties: <ul>
10769 * <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,
10770 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10771 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10772 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10773 * is being used, then this is a string containing the javascript expression to reference the data relative to
10774 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10775 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10776 * this may be omitted.</p></li>
10777 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10778 * <ul><li>auto (Default, implies no conversion)</li>
10783 * <li>date</li></ul></p></li>
10784 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10785 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10786 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10787 * by the Reader into an object that will be stored in the Record. It is passed the
10788 * following parameters:<ul>
10789 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10791 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10793 * <br>usage:<br><pre><code>
10794 var TopicRecord = Roo.data.Record.create(
10795 {name: 'title', mapping: 'topic_title'},
10796 {name: 'author', mapping: 'username'},
10797 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10798 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10799 {name: 'lastPoster', mapping: 'user2'},
10800 {name: 'excerpt', mapping: 'post_text'}
10803 var myNewRecord = new TopicRecord({
10804 title: 'Do my job please',
10807 lastPost: new Date(),
10808 lastPoster: 'Animal',
10809 excerpt: 'No way dude!'
10811 myStore.add(myNewRecord);
10816 Roo.data.Record.create = function(o){
10817 var f = function(){
10818 f.superclass.constructor.apply(this, arguments);
10820 Roo.extend(f, Roo.data.Record);
10821 var p = f.prototype;
10822 p.fields = new Roo.util.MixedCollection(false, function(field){
10825 for(var i = 0, len = o.length; i < len; i++){
10826 p.fields.add(new Roo.data.Field(o[i]));
10828 f.getField = function(name){
10829 return p.fields.get(name);
10834 Roo.data.Record.AUTO_ID = 1000;
10835 Roo.data.Record.EDIT = 'edit';
10836 Roo.data.Record.REJECT = 'reject';
10837 Roo.data.Record.COMMIT = 'commit';
10839 Roo.data.Record.prototype = {
10841 * Readonly flag - true if this record has been modified.
10850 join : function(store){
10851 this.store = store;
10855 * Set the named field to the specified value.
10856 * @param {String} name The name of the field to set.
10857 * @param {Object} value The value to set the field to.
10859 set : function(name, value){
10860 if(this.data[name] == value){
10864 if(!this.modified){
10865 this.modified = {};
10867 if(typeof this.modified[name] == 'undefined'){
10868 this.modified[name] = this.data[name];
10870 this.data[name] = value;
10871 if(!this.editing && this.store){
10872 this.store.afterEdit(this);
10877 * Get the value of the named field.
10878 * @param {String} name The name of the field to get the value of.
10879 * @return {Object} The value of the field.
10881 get : function(name){
10882 return this.data[name];
10886 beginEdit : function(){
10887 this.editing = true;
10888 this.modified = {};
10892 cancelEdit : function(){
10893 this.editing = false;
10894 delete this.modified;
10898 endEdit : function(){
10899 this.editing = false;
10900 if(this.dirty && this.store){
10901 this.store.afterEdit(this);
10906 * Usually called by the {@link Roo.data.Store} which owns the Record.
10907 * Rejects all changes made to the Record since either creation, or the last commit operation.
10908 * Modified fields are reverted to their original values.
10910 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10911 * of reject operations.
10913 reject : function(){
10914 var m = this.modified;
10916 if(typeof m[n] != "function"){
10917 this.data[n] = m[n];
10920 this.dirty = false;
10921 delete this.modified;
10922 this.editing = false;
10924 this.store.afterReject(this);
10929 * Usually called by the {@link Roo.data.Store} which owns the Record.
10930 * Commits all changes made to the Record since either creation, or the last commit operation.
10932 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10933 * of commit operations.
10935 commit : function(){
10936 this.dirty = false;
10937 delete this.modified;
10938 this.editing = false;
10940 this.store.afterCommit(this);
10945 hasError : function(){
10946 return this.error != null;
10950 clearError : function(){
10955 * Creates a copy of this record.
10956 * @param {String} id (optional) A new record id if you don't want to use this record's id
10959 copy : function(newId) {
10960 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10964 * Ext JS Library 1.1.1
10965 * Copyright(c) 2006-2007, Ext JS, LLC.
10967 * Originally Released Under LGPL - original licence link has changed is not relivant.
10970 * <script type="text/javascript">
10976 * @class Roo.data.Store
10977 * @extends Roo.util.Observable
10978 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10979 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10981 * 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
10982 * has no knowledge of the format of the data returned by the Proxy.<br>
10984 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10985 * instances from the data object. These records are cached and made available through accessor functions.
10987 * Creates a new Store.
10988 * @param {Object} config A config object containing the objects needed for the Store to access data,
10989 * and read the data into Records.
10991 Roo.data.Store = function(config){
10992 this.data = new Roo.util.MixedCollection(false);
10993 this.data.getKey = function(o){
10996 this.baseParams = {};
10998 this.paramNames = {
11003 "multisort" : "_multisort"
11006 if(config && config.data){
11007 this.inlineData = config.data;
11008 delete config.data;
11011 Roo.apply(this, config);
11013 if(this.reader){ // reader passed
11014 this.reader = Roo.factory(this.reader, Roo.data);
11015 this.reader.xmodule = this.xmodule || false;
11016 if(!this.recordType){
11017 this.recordType = this.reader.recordType;
11019 if(this.reader.onMetaChange){
11020 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11024 if(this.recordType){
11025 this.fields = this.recordType.prototype.fields;
11027 this.modified = [];
11031 * @event datachanged
11032 * Fires when the data cache has changed, and a widget which is using this Store
11033 * as a Record cache should refresh its view.
11034 * @param {Store} this
11036 datachanged : true,
11038 * @event metachange
11039 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11040 * @param {Store} this
11041 * @param {Object} meta The JSON metadata
11046 * Fires when Records have been added to the Store
11047 * @param {Store} this
11048 * @param {Roo.data.Record[]} records The array of Records added
11049 * @param {Number} index The index at which the record(s) were added
11054 * Fires when a Record has been removed from the Store
11055 * @param {Store} this
11056 * @param {Roo.data.Record} record The Record that was removed
11057 * @param {Number} index The index at which the record was removed
11062 * Fires when a Record has been updated
11063 * @param {Store} this
11064 * @param {Roo.data.Record} record The Record that was updated
11065 * @param {String} operation The update operation being performed. Value may be one of:
11067 Roo.data.Record.EDIT
11068 Roo.data.Record.REJECT
11069 Roo.data.Record.COMMIT
11075 * Fires when the data cache has been cleared.
11076 * @param {Store} this
11080 * @event beforeload
11081 * Fires before a request is made for a new data object. If the beforeload handler returns false
11082 * the load action will be canceled.
11083 * @param {Store} this
11084 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11088 * @event beforeloadadd
11089 * Fires after a new set of Records has been loaded.
11090 * @param {Store} this
11091 * @param {Roo.data.Record[]} records The Records that were loaded
11092 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11094 beforeloadadd : true,
11097 * Fires after a new set of Records has been loaded, before they are added to the store.
11098 * @param {Store} this
11099 * @param {Roo.data.Record[]} records The Records that were loaded
11100 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11101 * @params {Object} return from reader
11105 * @event loadexception
11106 * Fires if an exception occurs in the Proxy during loading.
11107 * Called with the signature of the Proxy's "loadexception" event.
11108 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11111 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11112 * @param {Object} load options
11113 * @param {Object} jsonData from your request (normally this contains the Exception)
11115 loadexception : true
11119 this.proxy = Roo.factory(this.proxy, Roo.data);
11120 this.proxy.xmodule = this.xmodule || false;
11121 this.relayEvents(this.proxy, ["loadexception"]);
11123 this.sortToggle = {};
11124 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11126 Roo.data.Store.superclass.constructor.call(this);
11128 if(this.inlineData){
11129 this.loadData(this.inlineData);
11130 delete this.inlineData;
11134 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11136 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11137 * without a remote query - used by combo/forms at present.
11141 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11144 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11147 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11148 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11151 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11152 * on any HTTP request
11155 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11158 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11162 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11163 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11165 remoteSort : false,
11168 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11169 * loaded or when a record is removed. (defaults to false).
11171 pruneModifiedRecords : false,
11174 lastOptions : null,
11177 * Add Records to the Store and fires the add event.
11178 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11180 add : function(records){
11181 records = [].concat(records);
11182 for(var i = 0, len = records.length; i < len; i++){
11183 records[i].join(this);
11185 var index = this.data.length;
11186 this.data.addAll(records);
11187 this.fireEvent("add", this, records, index);
11191 * Remove a Record from the Store and fires the remove event.
11192 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11194 remove : function(record){
11195 var index = this.data.indexOf(record);
11196 this.data.removeAt(index);
11198 if(this.pruneModifiedRecords){
11199 this.modified.remove(record);
11201 this.fireEvent("remove", this, record, index);
11205 * Remove all Records from the Store and fires the clear event.
11207 removeAll : function(){
11209 if(this.pruneModifiedRecords){
11210 this.modified = [];
11212 this.fireEvent("clear", this);
11216 * Inserts Records to the Store at the given index and fires the add event.
11217 * @param {Number} index The start index at which to insert the passed Records.
11218 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11220 insert : function(index, records){
11221 records = [].concat(records);
11222 for(var i = 0, len = records.length; i < len; i++){
11223 this.data.insert(index, records[i]);
11224 records[i].join(this);
11226 this.fireEvent("add", this, records, index);
11230 * Get the index within the cache of the passed Record.
11231 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11232 * @return {Number} The index of the passed Record. Returns -1 if not found.
11234 indexOf : function(record){
11235 return this.data.indexOf(record);
11239 * Get the index within the cache of the Record with the passed id.
11240 * @param {String} id The id of the Record to find.
11241 * @return {Number} The index of the Record. Returns -1 if not found.
11243 indexOfId : function(id){
11244 return this.data.indexOfKey(id);
11248 * Get the Record with the specified id.
11249 * @param {String} id The id of the Record to find.
11250 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11252 getById : function(id){
11253 return this.data.key(id);
11257 * Get the Record at the specified index.
11258 * @param {Number} index The index of the Record to find.
11259 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11261 getAt : function(index){
11262 return this.data.itemAt(index);
11266 * Returns a range of Records between specified indices.
11267 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11268 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11269 * @return {Roo.data.Record[]} An array of Records
11271 getRange : function(start, end){
11272 return this.data.getRange(start, end);
11276 storeOptions : function(o){
11277 o = Roo.apply({}, o);
11280 this.lastOptions = o;
11284 * Loads the Record cache from the configured Proxy using the configured Reader.
11286 * If using remote paging, then the first load call must specify the <em>start</em>
11287 * and <em>limit</em> properties in the options.params property to establish the initial
11288 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11290 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11291 * and this call will return before the new data has been loaded. Perform any post-processing
11292 * in a callback function, or in a "load" event handler.</strong>
11294 * @param {Object} options An object containing properties which control loading options:<ul>
11295 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11296 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11297 * passed the following arguments:<ul>
11298 * <li>r : Roo.data.Record[]</li>
11299 * <li>options: Options object from the load call</li>
11300 * <li>success: Boolean success indicator</li></ul></li>
11301 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11302 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11305 load : function(options){
11306 options = options || {};
11307 if(this.fireEvent("beforeload", this, options) !== false){
11308 this.storeOptions(options);
11309 var p = Roo.apply(options.params || {}, this.baseParams);
11310 // if meta was not loaded from remote source.. try requesting it.
11311 if (!this.reader.metaFromRemote) {
11312 p._requestMeta = 1;
11314 if(this.sortInfo && this.remoteSort){
11315 var pn = this.paramNames;
11316 p[pn["sort"]] = this.sortInfo.field;
11317 p[pn["dir"]] = this.sortInfo.direction;
11319 if (this.multiSort) {
11320 var pn = this.paramNames;
11321 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11324 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11329 * Reloads the Record cache from the configured Proxy using the configured Reader and
11330 * the options from the last load operation performed.
11331 * @param {Object} options (optional) An object containing properties which may override the options
11332 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11333 * the most recently used options are reused).
11335 reload : function(options){
11336 this.load(Roo.applyIf(options||{}, this.lastOptions));
11340 // Called as a callback by the Reader during a load operation.
11341 loadRecords : function(o, options, success){
11342 if(!o || success === false){
11343 if(success !== false){
11344 this.fireEvent("load", this, [], options, o);
11346 if(options.callback){
11347 options.callback.call(options.scope || this, [], options, false);
11351 // if data returned failure - throw an exception.
11352 if (o.success === false) {
11353 // show a message if no listener is registered.
11354 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11355 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11357 // loadmask wil be hooked into this..
11358 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11361 var r = o.records, t = o.totalRecords || r.length;
11363 this.fireEvent("beforeloadadd", this, r, options, o);
11365 if(!options || options.add !== true){
11366 if(this.pruneModifiedRecords){
11367 this.modified = [];
11369 for(var i = 0, len = r.length; i < len; i++){
11373 this.data = this.snapshot;
11374 delete this.snapshot;
11377 this.data.addAll(r);
11378 this.totalLength = t;
11380 this.fireEvent("datachanged", this);
11382 this.totalLength = Math.max(t, this.data.length+r.length);
11386 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11388 var e = new Roo.data.Record({});
11390 e.set(this.parent.displayField, this.parent.emptyTitle);
11391 e.set(this.parent.valueField, '');
11396 this.fireEvent("load", this, r, options, o);
11397 if(options.callback){
11398 options.callback.call(options.scope || this, r, options, true);
11404 * Loads data from a passed data block. A Reader which understands the format of the data
11405 * must have been configured in the constructor.
11406 * @param {Object} data The data block from which to read the Records. The format of the data expected
11407 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11408 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11410 loadData : function(o, append){
11411 var r = this.reader.readRecords(o);
11412 this.loadRecords(r, {add: append}, true);
11416 * Gets the number of cached records.
11418 * <em>If using paging, this may not be the total size of the dataset. If the data object
11419 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11420 * the data set size</em>
11422 getCount : function(){
11423 return this.data.length || 0;
11427 * Gets the total number of records in the dataset as returned by the server.
11429 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11430 * the dataset size</em>
11432 getTotalCount : function(){
11433 return this.totalLength || 0;
11437 * Returns the sort state of the Store as an object with two properties:
11439 field {String} The name of the field by which the Records are sorted
11440 direction {String} The sort order, "ASC" or "DESC"
11443 getSortState : function(){
11444 return this.sortInfo;
11448 applySort : function(){
11449 if(this.sortInfo && !this.remoteSort){
11450 var s = this.sortInfo, f = s.field;
11451 var st = this.fields.get(f).sortType;
11452 var fn = function(r1, r2){
11453 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11454 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11456 this.data.sort(s.direction, fn);
11457 if(this.snapshot && this.snapshot != this.data){
11458 this.snapshot.sort(s.direction, fn);
11464 * Sets the default sort column and order to be used by the next load operation.
11465 * @param {String} fieldName The name of the field to sort by.
11466 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11468 setDefaultSort : function(field, dir){
11469 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11473 * Sort the Records.
11474 * If remote sorting is used, the sort is performed on the server, and the cache is
11475 * reloaded. If local sorting is used, the cache is sorted internally.
11476 * @param {String} fieldName The name of the field to sort by.
11477 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11479 sort : function(fieldName, dir){
11480 var f = this.fields.get(fieldName);
11482 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11484 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11485 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11490 this.sortToggle[f.name] = dir;
11491 this.sortInfo = {field: f.name, direction: dir};
11492 if(!this.remoteSort){
11494 this.fireEvent("datachanged", this);
11496 this.load(this.lastOptions);
11501 * Calls the specified function for each of the Records in the cache.
11502 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11503 * Returning <em>false</em> aborts and exits the iteration.
11504 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11506 each : function(fn, scope){
11507 this.data.each(fn, scope);
11511 * Gets all records modified since the last commit. Modified records are persisted across load operations
11512 * (e.g., during paging).
11513 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11515 getModifiedRecords : function(){
11516 return this.modified;
11520 createFilterFn : function(property, value, anyMatch){
11521 if(!value.exec){ // not a regex
11522 value = String(value);
11523 if(value.length == 0){
11526 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11528 return function(r){
11529 return value.test(r.data[property]);
11534 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11535 * @param {String} property A field on your records
11536 * @param {Number} start The record index to start at (defaults to 0)
11537 * @param {Number} end The last record index to include (defaults to length - 1)
11538 * @return {Number} The sum
11540 sum : function(property, start, end){
11541 var rs = this.data.items, v = 0;
11542 start = start || 0;
11543 end = (end || end === 0) ? end : rs.length-1;
11545 for(var i = start; i <= end; i++){
11546 v += (rs[i].data[property] || 0);
11552 * Filter the records by a specified property.
11553 * @param {String} field A field on your records
11554 * @param {String/RegExp} value Either a string that the field
11555 * should start with or a RegExp to test against the field
11556 * @param {Boolean} anyMatch True to match any part not just the beginning
11558 filter : function(property, value, anyMatch){
11559 var fn = this.createFilterFn(property, value, anyMatch);
11560 return fn ? this.filterBy(fn) : this.clearFilter();
11564 * Filter by a function. The specified function will be called with each
11565 * record in this data source. If the function returns true the record is included,
11566 * otherwise it is filtered.
11567 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11568 * @param {Object} scope (optional) The scope of the function (defaults to this)
11570 filterBy : function(fn, scope){
11571 this.snapshot = this.snapshot || this.data;
11572 this.data = this.queryBy(fn, scope||this);
11573 this.fireEvent("datachanged", this);
11577 * Query the records by a specified property.
11578 * @param {String} field A field on your records
11579 * @param {String/RegExp} value Either a string that the field
11580 * should start with or a RegExp to test against the field
11581 * @param {Boolean} anyMatch True to match any part not just the beginning
11582 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11584 query : function(property, value, anyMatch){
11585 var fn = this.createFilterFn(property, value, anyMatch);
11586 return fn ? this.queryBy(fn) : this.data.clone();
11590 * Query by a function. The specified function will be called with each
11591 * record in this data source. If the function returns true the record is included
11593 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11594 * @param {Object} scope (optional) The scope of the function (defaults to this)
11595 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11597 queryBy : function(fn, scope){
11598 var data = this.snapshot || this.data;
11599 return data.filterBy(fn, scope||this);
11603 * Collects unique values for a particular dataIndex from this store.
11604 * @param {String} dataIndex The property to collect
11605 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11606 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11607 * @return {Array} An array of the unique values
11609 collect : function(dataIndex, allowNull, bypassFilter){
11610 var d = (bypassFilter === true && this.snapshot) ?
11611 this.snapshot.items : this.data.items;
11612 var v, sv, r = [], l = {};
11613 for(var i = 0, len = d.length; i < len; i++){
11614 v = d[i].data[dataIndex];
11616 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11625 * Revert to a view of the Record cache with no filtering applied.
11626 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11628 clearFilter : function(suppressEvent){
11629 if(this.snapshot && this.snapshot != this.data){
11630 this.data = this.snapshot;
11631 delete this.snapshot;
11632 if(suppressEvent !== true){
11633 this.fireEvent("datachanged", this);
11639 afterEdit : function(record){
11640 if(this.modified.indexOf(record) == -1){
11641 this.modified.push(record);
11643 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11647 afterReject : function(record){
11648 this.modified.remove(record);
11649 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11653 afterCommit : function(record){
11654 this.modified.remove(record);
11655 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11659 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11660 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11662 commitChanges : function(){
11663 var m = this.modified.slice(0);
11664 this.modified = [];
11665 for(var i = 0, len = m.length; i < len; i++){
11671 * Cancel outstanding changes on all changed records.
11673 rejectChanges : function(){
11674 var m = this.modified.slice(0);
11675 this.modified = [];
11676 for(var i = 0, len = m.length; i < len; i++){
11681 onMetaChange : function(meta, rtype, o){
11682 this.recordType = rtype;
11683 this.fields = rtype.prototype.fields;
11684 delete this.snapshot;
11685 this.sortInfo = meta.sortInfo || this.sortInfo;
11686 this.modified = [];
11687 this.fireEvent('metachange', this, this.reader.meta);
11690 moveIndex : function(data, type)
11692 var index = this.indexOf(data);
11694 var newIndex = index + type;
11698 this.insert(newIndex, data);
11703 * Ext JS Library 1.1.1
11704 * Copyright(c) 2006-2007, Ext JS, LLC.
11706 * Originally Released Under LGPL - original licence link has changed is not relivant.
11709 * <script type="text/javascript">
11713 * @class Roo.data.SimpleStore
11714 * @extends Roo.data.Store
11715 * Small helper class to make creating Stores from Array data easier.
11716 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11717 * @cfg {Array} fields An array of field definition objects, or field name strings.
11718 * @cfg {Array} data The multi-dimensional array of data
11720 * @param {Object} config
11722 Roo.data.SimpleStore = function(config){
11723 Roo.data.SimpleStore.superclass.constructor.call(this, {
11725 reader: new Roo.data.ArrayReader({
11728 Roo.data.Record.create(config.fields)
11730 proxy : new Roo.data.MemoryProxy(config.data)
11734 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11736 * Ext JS Library 1.1.1
11737 * Copyright(c) 2006-2007, Ext JS, LLC.
11739 * Originally Released Under LGPL - original licence link has changed is not relivant.
11742 * <script type="text/javascript">
11747 * @extends Roo.data.Store
11748 * @class Roo.data.JsonStore
11749 * Small helper class to make creating Stores for JSON data easier. <br/>
11751 var store = new Roo.data.JsonStore({
11752 url: 'get-images.php',
11754 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11757 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11758 * JsonReader and HttpProxy (unless inline data is provided).</b>
11759 * @cfg {Array} fields An array of field definition objects, or field name strings.
11761 * @param {Object} config
11763 Roo.data.JsonStore = function(c){
11764 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11765 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11766 reader: new Roo.data.JsonReader(c, c.fields)
11769 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11771 * Ext JS Library 1.1.1
11772 * Copyright(c) 2006-2007, Ext JS, LLC.
11774 * Originally Released Under LGPL - original licence link has changed is not relivant.
11777 * <script type="text/javascript">
11781 Roo.data.Field = function(config){
11782 if(typeof config == "string"){
11783 config = {name: config};
11785 Roo.apply(this, config);
11788 this.type = "auto";
11791 var st = Roo.data.SortTypes;
11792 // named sortTypes are supported, here we look them up
11793 if(typeof this.sortType == "string"){
11794 this.sortType = st[this.sortType];
11797 // set default sortType for strings and dates
11798 if(!this.sortType){
11801 this.sortType = st.asUCString;
11804 this.sortType = st.asDate;
11807 this.sortType = st.none;
11812 var stripRe = /[\$,%]/g;
11814 // prebuilt conversion function for this field, instead of
11815 // switching every time we're reading a value
11817 var cv, dateFormat = this.dateFormat;
11822 cv = function(v){ return v; };
11825 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11829 return v !== undefined && v !== null && v !== '' ?
11830 parseInt(String(v).replace(stripRe, ""), 10) : '';
11835 return v !== undefined && v !== null && v !== '' ?
11836 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11841 cv = function(v){ return v === true || v === "true" || v == 1; };
11848 if(v instanceof Date){
11852 if(dateFormat == "timestamp"){
11853 return new Date(v*1000);
11855 return Date.parseDate(v, dateFormat);
11857 var parsed = Date.parse(v);
11858 return parsed ? new Date(parsed) : null;
11867 Roo.data.Field.prototype = {
11875 * Ext JS Library 1.1.1
11876 * Copyright(c) 2006-2007, Ext JS, LLC.
11878 * Originally Released Under LGPL - original licence link has changed is not relivant.
11881 * <script type="text/javascript">
11884 // Base class for reading structured data from a data source. This class is intended to be
11885 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11888 * @class Roo.data.DataReader
11889 * Base class for reading structured data from a data source. This class is intended to be
11890 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11893 Roo.data.DataReader = function(meta, recordType){
11897 this.recordType = recordType instanceof Array ?
11898 Roo.data.Record.create(recordType) : recordType;
11901 Roo.data.DataReader.prototype = {
11903 * Create an empty record
11904 * @param {Object} data (optional) - overlay some values
11905 * @return {Roo.data.Record} record created.
11907 newRow : function(d) {
11909 this.recordType.prototype.fields.each(function(c) {
11911 case 'int' : da[c.name] = 0; break;
11912 case 'date' : da[c.name] = new Date(); break;
11913 case 'float' : da[c.name] = 0.0; break;
11914 case 'boolean' : da[c.name] = false; break;
11915 default : da[c.name] = ""; break;
11919 return new this.recordType(Roo.apply(da, d));
11924 * Ext JS Library 1.1.1
11925 * Copyright(c) 2006-2007, Ext JS, LLC.
11927 * Originally Released Under LGPL - original licence link has changed is not relivant.
11930 * <script type="text/javascript">
11934 * @class Roo.data.DataProxy
11935 * @extends Roo.data.Observable
11936 * This class is an abstract base class for implementations which provide retrieval of
11937 * unformatted data objects.<br>
11939 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11940 * (of the appropriate type which knows how to parse the data object) to provide a block of
11941 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11943 * Custom implementations must implement the load method as described in
11944 * {@link Roo.data.HttpProxy#load}.
11946 Roo.data.DataProxy = function(){
11949 * @event beforeload
11950 * Fires before a network request is made to retrieve a data object.
11951 * @param {Object} This DataProxy object.
11952 * @param {Object} params The params parameter to the load function.
11957 * Fires before the load method's callback is called.
11958 * @param {Object} This DataProxy object.
11959 * @param {Object} o The data object.
11960 * @param {Object} arg The callback argument object passed to the load function.
11964 * @event loadexception
11965 * Fires if an Exception occurs during data retrieval.
11966 * @param {Object} This DataProxy object.
11967 * @param {Object} o The data object.
11968 * @param {Object} arg The callback argument object passed to the load function.
11969 * @param {Object} e The Exception.
11971 loadexception : true
11973 Roo.data.DataProxy.superclass.constructor.call(this);
11976 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11979 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11983 * Ext JS Library 1.1.1
11984 * Copyright(c) 2006-2007, Ext JS, LLC.
11986 * Originally Released Under LGPL - original licence link has changed is not relivant.
11989 * <script type="text/javascript">
11992 * @class Roo.data.MemoryProxy
11993 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11994 * to the Reader when its load method is called.
11996 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11998 Roo.data.MemoryProxy = function(data){
12002 Roo.data.MemoryProxy.superclass.constructor.call(this);
12006 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12009 * Load data from the requested source (in this case an in-memory
12010 * data object passed to the constructor), read the data object into
12011 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12012 * process that block using the passed callback.
12013 * @param {Object} params This parameter is not used by the MemoryProxy class.
12014 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12015 * object into a block of Roo.data.Records.
12016 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12017 * The function must be passed <ul>
12018 * <li>The Record block object</li>
12019 * <li>The "arg" argument from the load function</li>
12020 * <li>A boolean success indicator</li>
12022 * @param {Object} scope The scope in which to call the callback
12023 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12025 load : function(params, reader, callback, scope, arg){
12026 params = params || {};
12029 result = reader.readRecords(this.data);
12031 this.fireEvent("loadexception", this, arg, null, e);
12032 callback.call(scope, null, arg, false);
12035 callback.call(scope, result, arg, true);
12039 update : function(params, records){
12044 * Ext JS Library 1.1.1
12045 * Copyright(c) 2006-2007, Ext JS, LLC.
12047 * Originally Released Under LGPL - original licence link has changed is not relivant.
12050 * <script type="text/javascript">
12053 * @class Roo.data.HttpProxy
12054 * @extends Roo.data.DataProxy
12055 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12056 * configured to reference a certain URL.<br><br>
12058 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12059 * from which the running page was served.<br><br>
12061 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12063 * Be aware that to enable the browser to parse an XML document, the server must set
12064 * the Content-Type header in the HTTP response to "text/xml".
12066 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12067 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12068 * will be used to make the request.
12070 Roo.data.HttpProxy = function(conn){
12071 Roo.data.HttpProxy.superclass.constructor.call(this);
12072 // is conn a conn config or a real conn?
12074 this.useAjax = !conn || !conn.events;
12078 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12079 // thse are take from connection...
12082 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12085 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12086 * extra parameters to each request made by this object. (defaults to undefined)
12089 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12090 * to each request made by this object. (defaults to undefined)
12093 * @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)
12096 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12099 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12105 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12109 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12110 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12111 * a finer-grained basis than the DataProxy events.
12113 getConnection : function(){
12114 return this.useAjax ? Roo.Ajax : this.conn;
12118 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12119 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12120 * process that block using the passed callback.
12121 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12122 * for the request to the remote server.
12123 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12124 * object into a block of Roo.data.Records.
12125 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12126 * The function must be passed <ul>
12127 * <li>The Record block object</li>
12128 * <li>The "arg" argument from the load function</li>
12129 * <li>A boolean success indicator</li>
12131 * @param {Object} scope The scope in which to call the callback
12132 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12134 load : function(params, reader, callback, scope, arg){
12135 if(this.fireEvent("beforeload", this, params) !== false){
12137 params : params || {},
12139 callback : callback,
12144 callback : this.loadResponse,
12148 Roo.applyIf(o, this.conn);
12149 if(this.activeRequest){
12150 Roo.Ajax.abort(this.activeRequest);
12152 this.activeRequest = Roo.Ajax.request(o);
12154 this.conn.request(o);
12157 callback.call(scope||this, null, arg, false);
12162 loadResponse : function(o, success, response){
12163 delete this.activeRequest;
12165 this.fireEvent("loadexception", this, o, response);
12166 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12171 result = o.reader.read(response);
12173 this.fireEvent("loadexception", this, o, response, e);
12174 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12178 this.fireEvent("load", this, o, o.request.arg);
12179 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12183 update : function(dataSet){
12188 updateResponse : function(dataSet){
12193 * Ext JS Library 1.1.1
12194 * Copyright(c) 2006-2007, Ext JS, LLC.
12196 * Originally Released Under LGPL - original licence link has changed is not relivant.
12199 * <script type="text/javascript">
12203 * @class Roo.data.ScriptTagProxy
12204 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12205 * other than the originating domain of the running page.<br><br>
12207 * <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
12208 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12210 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12211 * source code that is used as the source inside a <script> tag.<br><br>
12213 * In order for the browser to process the returned data, the server must wrap the data object
12214 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12215 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12216 * depending on whether the callback name was passed:
12219 boolean scriptTag = false;
12220 String cb = request.getParameter("callback");
12223 response.setContentType("text/javascript");
12225 response.setContentType("application/x-json");
12227 Writer out = response.getWriter();
12229 out.write(cb + "(");
12231 out.print(dataBlock.toJsonString());
12238 * @param {Object} config A configuration object.
12240 Roo.data.ScriptTagProxy = function(config){
12241 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12242 Roo.apply(this, config);
12243 this.head = document.getElementsByTagName("head")[0];
12246 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12248 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12250 * @cfg {String} url The URL from which to request the data object.
12253 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12257 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12258 * the server the name of the callback function set up by the load call to process the returned data object.
12259 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12260 * javascript output which calls this named function passing the data object as its only parameter.
12262 callbackParam : "callback",
12264 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12265 * name to the request.
12270 * Load data from the configured URL, read the data object into
12271 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12272 * process that block using the passed callback.
12273 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12274 * for the request to the remote server.
12275 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12276 * object into a block of Roo.data.Records.
12277 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12278 * The function must be passed <ul>
12279 * <li>The Record block object</li>
12280 * <li>The "arg" argument from the load function</li>
12281 * <li>A boolean success indicator</li>
12283 * @param {Object} scope The scope in which to call the callback
12284 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12286 load : function(params, reader, callback, scope, arg){
12287 if(this.fireEvent("beforeload", this, params) !== false){
12289 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12291 var url = this.url;
12292 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12294 url += "&_dc=" + (new Date().getTime());
12296 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12299 cb : "stcCallback"+transId,
12300 scriptId : "stcScript"+transId,
12304 callback : callback,
12310 window[trans.cb] = function(o){
12311 conn.handleResponse(o, trans);
12314 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12316 if(this.autoAbort !== false){
12320 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12322 var script = document.createElement("script");
12323 script.setAttribute("src", url);
12324 script.setAttribute("type", "text/javascript");
12325 script.setAttribute("id", trans.scriptId);
12326 this.head.appendChild(script);
12328 this.trans = trans;
12330 callback.call(scope||this, null, arg, false);
12335 isLoading : function(){
12336 return this.trans ? true : false;
12340 * Abort the current server request.
12342 abort : function(){
12343 if(this.isLoading()){
12344 this.destroyTrans(this.trans);
12349 destroyTrans : function(trans, isLoaded){
12350 this.head.removeChild(document.getElementById(trans.scriptId));
12351 clearTimeout(trans.timeoutId);
12353 window[trans.cb] = undefined;
12355 delete window[trans.cb];
12358 // if hasn't been loaded, wait for load to remove it to prevent script error
12359 window[trans.cb] = function(){
12360 window[trans.cb] = undefined;
12362 delete window[trans.cb];
12369 handleResponse : function(o, trans){
12370 this.trans = false;
12371 this.destroyTrans(trans, true);
12374 result = trans.reader.readRecords(o);
12376 this.fireEvent("loadexception", this, o, trans.arg, e);
12377 trans.callback.call(trans.scope||window, null, trans.arg, false);
12380 this.fireEvent("load", this, o, trans.arg);
12381 trans.callback.call(trans.scope||window, result, trans.arg, true);
12385 handleFailure : function(trans){
12386 this.trans = false;
12387 this.destroyTrans(trans, false);
12388 this.fireEvent("loadexception", this, null, trans.arg);
12389 trans.callback.call(trans.scope||window, null, trans.arg, false);
12393 * Ext JS Library 1.1.1
12394 * Copyright(c) 2006-2007, Ext JS, LLC.
12396 * Originally Released Under LGPL - original licence link has changed is not relivant.
12399 * <script type="text/javascript">
12403 * @class Roo.data.JsonReader
12404 * @extends Roo.data.DataReader
12405 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12406 * based on mappings in a provided Roo.data.Record constructor.
12408 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12409 * in the reply previously.
12414 var RecordDef = Roo.data.Record.create([
12415 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12416 {name: 'occupation'} // This field will use "occupation" as the mapping.
12418 var myReader = new Roo.data.JsonReader({
12419 totalProperty: "results", // The property which contains the total dataset size (optional)
12420 root: "rows", // The property which contains an Array of row objects
12421 id: "id" // The property within each row object that provides an ID for the record (optional)
12425 * This would consume a JSON file like this:
12427 { 'results': 2, 'rows': [
12428 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12429 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12432 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12433 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12434 * paged from the remote server.
12435 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12436 * @cfg {String} root name of the property which contains the Array of row objects.
12437 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12438 * @cfg {Array} fields Array of field definition objects
12440 * Create a new JsonReader
12441 * @param {Object} meta Metadata configuration options
12442 * @param {Object} recordType Either an Array of field definition objects,
12443 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12445 Roo.data.JsonReader = function(meta, recordType){
12448 // set some defaults:
12449 Roo.applyIf(meta, {
12450 totalProperty: 'total',
12451 successProperty : 'success',
12456 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12458 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12461 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12462 * Used by Store query builder to append _requestMeta to params.
12465 metaFromRemote : false,
12467 * This method is only used by a DataProxy which has retrieved data from a remote server.
12468 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12469 * @return {Object} data A data block which is used by an Roo.data.Store object as
12470 * a cache of Roo.data.Records.
12472 read : function(response){
12473 var json = response.responseText;
12475 var o = /* eval:var:o */ eval("("+json+")");
12477 throw {message: "JsonReader.read: Json object not found"};
12483 this.metaFromRemote = true;
12484 this.meta = o.metaData;
12485 this.recordType = Roo.data.Record.create(o.metaData.fields);
12486 this.onMetaChange(this.meta, this.recordType, o);
12488 return this.readRecords(o);
12491 // private function a store will implement
12492 onMetaChange : function(meta, recordType, o){
12499 simpleAccess: function(obj, subsc) {
12506 getJsonAccessor: function(){
12508 return function(expr) {
12510 return(re.test(expr))
12511 ? new Function("obj", "return obj." + expr)
12516 return Roo.emptyFn;
12521 * Create a data block containing Roo.data.Records from an XML document.
12522 * @param {Object} o An object which contains an Array of row objects in the property specified
12523 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12524 * which contains the total size of the dataset.
12525 * @return {Object} data A data block which is used by an Roo.data.Store object as
12526 * a cache of Roo.data.Records.
12528 readRecords : function(o){
12530 * After any data loads, the raw JSON data is available for further custom processing.
12534 var s = this.meta, Record = this.recordType,
12535 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12537 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12539 if(s.totalProperty) {
12540 this.getTotal = this.getJsonAccessor(s.totalProperty);
12542 if(s.successProperty) {
12543 this.getSuccess = this.getJsonAccessor(s.successProperty);
12545 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12547 var g = this.getJsonAccessor(s.id);
12548 this.getId = function(rec) {
12550 return (r === undefined || r === "") ? null : r;
12553 this.getId = function(){return null;};
12556 for(var jj = 0; jj < fl; jj++){
12558 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12559 this.ef[jj] = this.getJsonAccessor(map);
12563 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12564 if(s.totalProperty){
12565 var vt = parseInt(this.getTotal(o), 10);
12570 if(s.successProperty){
12571 var vs = this.getSuccess(o);
12572 if(vs === false || vs === 'false'){
12577 for(var i = 0; i < c; i++){
12580 var id = this.getId(n);
12581 for(var j = 0; j < fl; j++){
12583 var v = this.ef[j](n);
12585 Roo.log('missing convert for ' + f.name);
12589 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12591 var record = new Record(values, id);
12593 records[i] = record;
12599 totalRecords : totalRecords
12604 * Ext JS Library 1.1.1
12605 * Copyright(c) 2006-2007, Ext JS, LLC.
12607 * Originally Released Under LGPL - original licence link has changed is not relivant.
12610 * <script type="text/javascript">
12614 * @class Roo.data.ArrayReader
12615 * @extends Roo.data.DataReader
12616 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12617 * Each element of that Array represents a row of data fields. The
12618 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12619 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12623 var RecordDef = Roo.data.Record.create([
12624 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12625 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12627 var myReader = new Roo.data.ArrayReader({
12628 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12632 * This would consume an Array like this:
12634 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12636 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12638 * Create a new JsonReader
12639 * @param {Object} meta Metadata configuration options.
12640 * @param {Object} recordType Either an Array of field definition objects
12641 * as specified to {@link Roo.data.Record#create},
12642 * or an {@link Roo.data.Record} object
12643 * created using {@link Roo.data.Record#create}.
12645 Roo.data.ArrayReader = function(meta, recordType){
12646 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12649 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12651 * Create a data block containing Roo.data.Records from an XML document.
12652 * @param {Object} o An Array of row objects which represents the dataset.
12653 * @return {Object} data A data block which is used by an Roo.data.Store object as
12654 * a cache of Roo.data.Records.
12656 readRecords : function(o){
12657 var sid = this.meta ? this.meta.id : null;
12658 var recordType = this.recordType, fields = recordType.prototype.fields;
12661 for(var i = 0; i < root.length; i++){
12664 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12665 for(var j = 0, jlen = fields.length; j < jlen; j++){
12666 var f = fields.items[j];
12667 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12668 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12670 values[f.name] = v;
12672 var record = new recordType(values, id);
12674 records[records.length] = record;
12678 totalRecords : records.length
12687 * @class Roo.bootstrap.ComboBox
12688 * @extends Roo.bootstrap.TriggerField
12689 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12690 * @cfg {Boolean} append (true|false) default false
12691 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12692 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12693 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12694 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12695 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12696 * @cfg {Boolean} animate default true
12697 * @cfg {Boolean} emptyResultText only for touch device
12698 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12699 * @cfg {String} emptyTitle default ''
12701 * Create a new ComboBox.
12702 * @param {Object} config Configuration options
12704 Roo.bootstrap.ComboBox = function(config){
12705 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12709 * Fires when the dropdown list is expanded
12710 * @param {Roo.bootstrap.ComboBox} combo This combo box
12715 * Fires when the dropdown list is collapsed
12716 * @param {Roo.bootstrap.ComboBox} combo This combo box
12720 * @event beforeselect
12721 * Fires before a list item is selected. Return false to cancel the selection.
12722 * @param {Roo.bootstrap.ComboBox} combo This combo box
12723 * @param {Roo.data.Record} record The data record returned from the underlying store
12724 * @param {Number} index The index of the selected item in the dropdown list
12726 'beforeselect' : true,
12729 * Fires when a list item is selected
12730 * @param {Roo.bootstrap.ComboBox} combo This combo box
12731 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12732 * @param {Number} index The index of the selected item in the dropdown list
12736 * @event beforequery
12737 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12738 * The event object passed has these properties:
12739 * @param {Roo.bootstrap.ComboBox} combo This combo box
12740 * @param {String} query The query
12741 * @param {Boolean} forceAll true to force "all" query
12742 * @param {Boolean} cancel true to cancel the query
12743 * @param {Object} e The query event object
12745 'beforequery': true,
12748 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12749 * @param {Roo.bootstrap.ComboBox} combo This combo box
12754 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12755 * @param {Roo.bootstrap.ComboBox} combo This combo box
12756 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12761 * Fires when the remove value from the combobox array
12762 * @param {Roo.bootstrap.ComboBox} combo This combo box
12766 * @event afterremove
12767 * Fires when the remove value from the combobox array
12768 * @param {Roo.bootstrap.ComboBox} combo This combo box
12770 'afterremove' : true,
12772 * @event specialfilter
12773 * Fires when specialfilter
12774 * @param {Roo.bootstrap.ComboBox} combo This combo box
12776 'specialfilter' : true,
12779 * Fires when tick the element
12780 * @param {Roo.bootstrap.ComboBox} combo This combo box
12784 * @event touchviewdisplay
12785 * Fires when touch view require special display (default is using displayField)
12786 * @param {Roo.bootstrap.ComboBox} combo This combo box
12787 * @param {Object} cfg set html .
12789 'touchviewdisplay' : true
12794 this.tickItems = [];
12796 this.selectedIndex = -1;
12797 if(this.mode == 'local'){
12798 if(config.queryDelay === undefined){
12799 this.queryDelay = 10;
12801 if(config.minChars === undefined){
12807 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12810 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12811 * rendering into an Roo.Editor, defaults to false)
12814 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12815 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12818 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12821 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12822 * the dropdown list (defaults to undefined, with no header element)
12826 * @cfg {String/Roo.Template} tpl The template to use to render the output
12830 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12832 listWidth: undefined,
12834 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12835 * mode = 'remote' or 'text' if mode = 'local')
12837 displayField: undefined,
12840 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12841 * mode = 'remote' or 'value' if mode = 'local').
12842 * Note: use of a valueField requires the user make a selection
12843 * in order for a value to be mapped.
12845 valueField: undefined,
12847 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12852 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12853 * field's data value (defaults to the underlying DOM element's name)
12855 hiddenName: undefined,
12857 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12861 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12863 selectedClass: 'active',
12866 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12870 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12871 * anchor positions (defaults to 'tl-bl')
12873 listAlign: 'tl-bl?',
12875 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12879 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12880 * query specified by the allQuery config option (defaults to 'query')
12882 triggerAction: 'query',
12884 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12885 * (defaults to 4, does not apply if editable = false)
12889 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12890 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12894 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12895 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12899 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12900 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12904 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12905 * when editable = true (defaults to false)
12907 selectOnFocus:false,
12909 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12911 queryParam: 'query',
12913 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12914 * when mode = 'remote' (defaults to 'Loading...')
12916 loadingText: 'Loading...',
12918 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12922 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12926 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12927 * traditional select (defaults to true)
12931 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12935 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12939 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12940 * listWidth has a higher value)
12944 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12945 * allow the user to set arbitrary text into the field (defaults to false)
12947 forceSelection:false,
12949 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12950 * if typeAhead = true (defaults to 250)
12952 typeAheadDelay : 250,
12954 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12955 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12957 valueNotFoundText : undefined,
12959 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12961 blockFocus : false,
12964 * @cfg {Boolean} disableClear Disable showing of clear button.
12966 disableClear : false,
12968 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12970 alwaysQuery : false,
12973 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12978 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12980 invalidClass : "has-warning",
12983 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12985 validClass : "has-success",
12988 * @cfg {Boolean} specialFilter (true|false) special filter default false
12990 specialFilter : false,
12993 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12995 mobileTouchView : true,
12998 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13000 useNativeIOS : false,
13002 ios_options : false,
13014 btnPosition : 'right',
13015 triggerList : true,
13016 showToggleBtn : true,
13018 emptyResultText: 'Empty',
13019 triggerText : 'Select',
13022 // element that contains real text value.. (when hidden is used..)
13024 getAutoCreate : function()
13029 * Render classic select for iso
13032 if(Roo.isIOS && this.useNativeIOS){
13033 cfg = this.getAutoCreateNativeIOS();
13041 if(Roo.isTouch && this.mobileTouchView){
13042 cfg = this.getAutoCreateTouchView();
13049 if(!this.tickable){
13050 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13055 * ComboBox with tickable selections
13058 var align = this.labelAlign || this.parentLabelAlign();
13061 cls : 'form-group roo-combobox-tickable' //input-group
13064 var btn_text_select = '';
13065 var btn_text_done = '';
13066 var btn_text_cancel = '';
13068 if (this.btn_text_show) {
13069 btn_text_select = 'Select';
13070 btn_text_done = 'Done';
13071 btn_text_cancel = 'Cancel';
13076 cls : 'tickable-buttons',
13081 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13082 //html : this.triggerText
13083 html: btn_text_select
13089 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13091 html: btn_text_done
13097 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13099 html: btn_text_cancel
13105 buttons.cn.unshift({
13107 cls: 'roo-select2-search-field-input'
13113 Roo.each(buttons.cn, function(c){
13115 c.cls += ' btn-' + _this.size;
13118 if (_this.disabled) {
13129 cls: 'form-hidden-field'
13133 cls: 'roo-select2-choices',
13137 cls: 'roo-select2-search-field',
13148 cls: 'roo-select2-container input-group roo-select2-container-multi',
13153 // cls: 'typeahead typeahead-long dropdown-menu',
13154 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13159 if(this.hasFeedback && !this.allowBlank){
13163 cls: 'glyphicon form-control-feedback'
13166 combobox.cn.push(feedback);
13170 if (align ==='left' && this.fieldLabel.length) {
13172 cfg.cls += ' roo-form-group-label-left';
13177 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13178 tooltip : 'This field is required'
13183 cls : 'control-label',
13184 html : this.fieldLabel
13196 var labelCfg = cfg.cn[1];
13197 var contentCfg = cfg.cn[2];
13200 if(this.indicatorpos == 'right'){
13206 cls : 'control-label',
13210 html : this.fieldLabel
13214 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13215 tooltip : 'This field is required'
13230 labelCfg = cfg.cn[0];
13231 contentCfg = cfg.cn[1];
13235 if(this.labelWidth > 12){
13236 labelCfg.style = "width: " + this.labelWidth + 'px';
13239 if(this.labelWidth < 13 && this.labelmd == 0){
13240 this.labelmd = this.labelWidth;
13243 if(this.labellg > 0){
13244 labelCfg.cls += ' col-lg-' + this.labellg;
13245 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13248 if(this.labelmd > 0){
13249 labelCfg.cls += ' col-md-' + this.labelmd;
13250 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13253 if(this.labelsm > 0){
13254 labelCfg.cls += ' col-sm-' + this.labelsm;
13255 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13258 if(this.labelxs > 0){
13259 labelCfg.cls += ' col-xs-' + this.labelxs;
13260 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13264 } else if ( this.fieldLabel.length) {
13265 // Roo.log(" label");
13269 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13270 tooltip : 'This field is required'
13274 //cls : 'input-group-addon',
13275 html : this.fieldLabel
13280 if(this.indicatorpos == 'right'){
13284 //cls : 'input-group-addon',
13285 html : this.fieldLabel
13289 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13290 tooltip : 'This field is required'
13299 // Roo.log(" no label && no align");
13306 ['xs','sm','md','lg'].map(function(size){
13307 if (settings[size]) {
13308 cfg.cls += ' col-' + size + '-' + settings[size];
13316 _initEventsCalled : false,
13319 initEvents: function()
13321 if (this._initEventsCalled) { // as we call render... prevent looping...
13324 this._initEventsCalled = true;
13327 throw "can not find store for combo";
13330 this.indicator = this.indicatorEl();
13332 this.store = Roo.factory(this.store, Roo.data);
13333 this.store.parent = this;
13335 // if we are building from html. then this element is so complex, that we can not really
13336 // use the rendered HTML.
13337 // so we have to trash and replace the previous code.
13338 if (Roo.XComponent.build_from_html) {
13339 // remove this element....
13340 var e = this.el.dom, k=0;
13341 while (e ) { e = e.previousSibling; ++k;}
13346 this.rendered = false;
13348 this.render(this.parent().getChildContainer(true), k);
13351 if(Roo.isIOS && this.useNativeIOS){
13352 this.initIOSView();
13360 if(Roo.isTouch && this.mobileTouchView){
13361 this.initTouchView();
13366 this.initTickableEvents();
13370 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13372 if(this.hiddenName){
13374 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13376 this.hiddenField.dom.value =
13377 this.hiddenValue !== undefined ? this.hiddenValue :
13378 this.value !== undefined ? this.value : '';
13380 // prevent input submission
13381 this.el.dom.removeAttribute('name');
13382 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13387 // this.el.dom.setAttribute('autocomplete', 'off');
13390 var cls = 'x-combo-list';
13392 //this.list = new Roo.Layer({
13393 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13399 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13400 _this.list.setWidth(lw);
13403 this.list.on('mouseover', this.onViewOver, this);
13404 this.list.on('mousemove', this.onViewMove, this);
13405 this.list.on('scroll', this.onViewScroll, this);
13408 this.list.swallowEvent('mousewheel');
13409 this.assetHeight = 0;
13412 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13413 this.assetHeight += this.header.getHeight();
13416 this.innerList = this.list.createChild({cls:cls+'-inner'});
13417 this.innerList.on('mouseover', this.onViewOver, this);
13418 this.innerList.on('mousemove', this.onViewMove, this);
13419 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13421 if(this.allowBlank && !this.pageSize && !this.disableClear){
13422 this.footer = this.list.createChild({cls:cls+'-ft'});
13423 this.pageTb = new Roo.Toolbar(this.footer);
13427 this.footer = this.list.createChild({cls:cls+'-ft'});
13428 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13429 {pageSize: this.pageSize});
13433 if (this.pageTb && this.allowBlank && !this.disableClear) {
13435 this.pageTb.add(new Roo.Toolbar.Fill(), {
13436 cls: 'x-btn-icon x-btn-clear',
13438 handler: function()
13441 _this.clearValue();
13442 _this.onSelect(false, -1);
13447 this.assetHeight += this.footer.getHeight();
13452 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13455 this.view = new Roo.View(this.list, this.tpl, {
13456 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13458 //this.view.wrapEl.setDisplayed(false);
13459 this.view.on('click', this.onViewClick, this);
13462 this.store.on('beforeload', this.onBeforeLoad, this);
13463 this.store.on('load', this.onLoad, this);
13464 this.store.on('loadexception', this.onLoadException, this);
13466 if(this.resizable){
13467 this.resizer = new Roo.Resizable(this.list, {
13468 pinned:true, handles:'se'
13470 this.resizer.on('resize', function(r, w, h){
13471 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13472 this.listWidth = w;
13473 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13474 this.restrictHeight();
13476 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13479 if(!this.editable){
13480 this.editable = true;
13481 this.setEditable(false);
13486 if (typeof(this.events.add.listeners) != 'undefined') {
13488 this.addicon = this.wrap.createChild(
13489 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13491 this.addicon.on('click', function(e) {
13492 this.fireEvent('add', this);
13495 if (typeof(this.events.edit.listeners) != 'undefined') {
13497 this.editicon = this.wrap.createChild(
13498 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13499 if (this.addicon) {
13500 this.editicon.setStyle('margin-left', '40px');
13502 this.editicon.on('click', function(e) {
13504 // we fire even if inothing is selected..
13505 this.fireEvent('edit', this, this.lastData );
13511 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13512 "up" : function(e){
13513 this.inKeyMode = true;
13517 "down" : function(e){
13518 if(!this.isExpanded()){
13519 this.onTriggerClick();
13521 this.inKeyMode = true;
13526 "enter" : function(e){
13527 // this.onViewClick();
13531 if(this.fireEvent("specialkey", this, e)){
13532 this.onViewClick(false);
13538 "esc" : function(e){
13542 "tab" : function(e){
13545 if(this.fireEvent("specialkey", this, e)){
13546 this.onViewClick(false);
13554 doRelay : function(foo, bar, hname){
13555 if(hname == 'down' || this.scope.isExpanded()){
13556 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13565 this.queryDelay = Math.max(this.queryDelay || 10,
13566 this.mode == 'local' ? 10 : 250);
13569 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13571 if(this.typeAhead){
13572 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13574 if(this.editable !== false){
13575 this.inputEl().on("keyup", this.onKeyUp, this);
13577 if(this.forceSelection){
13578 this.inputEl().on('blur', this.doForce, this);
13582 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13583 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13587 initTickableEvents: function()
13591 if(this.hiddenName){
13593 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13595 this.hiddenField.dom.value =
13596 this.hiddenValue !== undefined ? this.hiddenValue :
13597 this.value !== undefined ? this.value : '';
13599 // prevent input submission
13600 this.el.dom.removeAttribute('name');
13601 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13606 // this.list = this.el.select('ul.dropdown-menu',true).first();
13608 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13609 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13610 if(this.triggerList){
13611 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13614 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13615 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13617 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13618 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13620 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13621 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13623 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13624 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13625 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13628 this.cancelBtn.hide();
13633 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13634 _this.list.setWidth(lw);
13637 this.list.on('mouseover', this.onViewOver, this);
13638 this.list.on('mousemove', this.onViewMove, this);
13640 this.list.on('scroll', this.onViewScroll, this);
13643 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13644 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13647 this.view = new Roo.View(this.list, this.tpl, {
13652 selectedClass: this.selectedClass
13655 //this.view.wrapEl.setDisplayed(false);
13656 this.view.on('click', this.onViewClick, this);
13660 this.store.on('beforeload', this.onBeforeLoad, this);
13661 this.store.on('load', this.onLoad, this);
13662 this.store.on('loadexception', this.onLoadException, this);
13665 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13666 "up" : function(e){
13667 this.inKeyMode = true;
13671 "down" : function(e){
13672 this.inKeyMode = true;
13676 "enter" : function(e){
13677 if(this.fireEvent("specialkey", this, e)){
13678 this.onViewClick(false);
13684 "esc" : function(e){
13685 this.onTickableFooterButtonClick(e, false, false);
13688 "tab" : function(e){
13689 this.fireEvent("specialkey", this, e);
13691 this.onTickableFooterButtonClick(e, false, false);
13698 doRelay : function(e, fn, key){
13699 if(this.scope.isExpanded()){
13700 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13709 this.queryDelay = Math.max(this.queryDelay || 10,
13710 this.mode == 'local' ? 10 : 250);
13713 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13715 if(this.typeAhead){
13716 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13719 if(this.editable !== false){
13720 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13723 this.indicator = this.indicatorEl();
13725 if(this.indicator){
13726 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13727 this.indicator.hide();
13732 onDestroy : function(){
13734 this.view.setStore(null);
13735 this.view.el.removeAllListeners();
13736 this.view.el.remove();
13737 this.view.purgeListeners();
13740 this.list.dom.innerHTML = '';
13744 this.store.un('beforeload', this.onBeforeLoad, this);
13745 this.store.un('load', this.onLoad, this);
13746 this.store.un('loadexception', this.onLoadException, this);
13748 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13752 fireKey : function(e){
13753 if(e.isNavKeyPress() && !this.list.isVisible()){
13754 this.fireEvent("specialkey", this, e);
13759 onResize: function(w, h){
13760 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13762 // if(typeof w != 'number'){
13763 // // we do not handle it!?!?
13766 // var tw = this.trigger.getWidth();
13767 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13768 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13770 // this.inputEl().setWidth( this.adjustWidth('input', x));
13772 // //this.trigger.setStyle('left', x+'px');
13774 // if(this.list && this.listWidth === undefined){
13775 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13776 // this.list.setWidth(lw);
13777 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13785 * Allow or prevent the user from directly editing the field text. If false is passed,
13786 * the user will only be able to select from the items defined in the dropdown list. This method
13787 * is the runtime equivalent of setting the 'editable' config option at config time.
13788 * @param {Boolean} value True to allow the user to directly edit the field text
13790 setEditable : function(value){
13791 if(value == this.editable){
13794 this.editable = value;
13796 this.inputEl().dom.setAttribute('readOnly', true);
13797 this.inputEl().on('mousedown', this.onTriggerClick, this);
13798 this.inputEl().addClass('x-combo-noedit');
13800 this.inputEl().dom.setAttribute('readOnly', false);
13801 this.inputEl().un('mousedown', this.onTriggerClick, this);
13802 this.inputEl().removeClass('x-combo-noedit');
13808 onBeforeLoad : function(combo,opts){
13809 if(!this.hasFocus){
13813 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13815 this.restrictHeight();
13816 this.selectedIndex = -1;
13820 onLoad : function(){
13822 this.hasQuery = false;
13824 if(!this.hasFocus){
13828 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13829 this.loading.hide();
13832 if(this.store.getCount() > 0){
13835 this.restrictHeight();
13836 if(this.lastQuery == this.allQuery){
13837 if(this.editable && !this.tickable){
13838 this.inputEl().dom.select();
13842 !this.selectByValue(this.value, true) &&
13845 !this.store.lastOptions ||
13846 typeof(this.store.lastOptions.add) == 'undefined' ||
13847 this.store.lastOptions.add != true
13850 this.select(0, true);
13853 if(this.autoFocus){
13856 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13857 this.taTask.delay(this.typeAheadDelay);
13861 this.onEmptyResults();
13867 onLoadException : function()
13869 this.hasQuery = false;
13871 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13872 this.loading.hide();
13875 if(this.tickable && this.editable){
13880 // only causes errors at present
13881 //Roo.log(this.store.reader.jsonData);
13882 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13884 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13890 onTypeAhead : function(){
13891 if(this.store.getCount() > 0){
13892 var r = this.store.getAt(0);
13893 var newValue = r.data[this.displayField];
13894 var len = newValue.length;
13895 var selStart = this.getRawValue().length;
13897 if(selStart != len){
13898 this.setRawValue(newValue);
13899 this.selectText(selStart, newValue.length);
13905 onSelect : function(record, index){
13907 if(this.fireEvent('beforeselect', this, record, index) !== false){
13909 this.setFromData(index > -1 ? record.data : false);
13912 this.fireEvent('select', this, record, index);
13917 * Returns the currently selected field value or empty string if no value is set.
13918 * @return {String} value The selected value
13920 getValue : function()
13922 if(Roo.isIOS && this.useNativeIOS){
13923 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13927 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13930 if(this.valueField){
13931 return typeof this.value != 'undefined' ? this.value : '';
13933 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13937 getRawValue : function()
13939 if(Roo.isIOS && this.useNativeIOS){
13940 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13943 var v = this.inputEl().getValue();
13949 * Clears any text/value currently set in the field
13951 clearValue : function(){
13953 if(this.hiddenField){
13954 this.hiddenField.dom.value = '';
13957 this.setRawValue('');
13958 this.lastSelectionText = '';
13959 this.lastData = false;
13961 var close = this.closeTriggerEl();
13972 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13973 * will be displayed in the field. If the value does not match the data value of an existing item,
13974 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13975 * Otherwise the field will be blank (although the value will still be set).
13976 * @param {String} value The value to match
13978 setValue : function(v)
13980 if(Roo.isIOS && this.useNativeIOS){
13981 this.setIOSValue(v);
13991 if(this.valueField){
13992 var r = this.findRecord(this.valueField, v);
13994 text = r.data[this.displayField];
13995 }else if(this.valueNotFoundText !== undefined){
13996 text = this.valueNotFoundText;
13999 this.lastSelectionText = text;
14000 if(this.hiddenField){
14001 this.hiddenField.dom.value = v;
14003 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14006 var close = this.closeTriggerEl();
14009 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14015 * @property {Object} the last set data for the element
14020 * Sets the value of the field based on a object which is related to the record format for the store.
14021 * @param {Object} value the value to set as. or false on reset?
14023 setFromData : function(o){
14030 var dv = ''; // display value
14031 var vv = ''; // value value..
14033 if (this.displayField) {
14034 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14036 // this is an error condition!!!
14037 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14040 if(this.valueField){
14041 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14044 var close = this.closeTriggerEl();
14047 if(dv.length || vv * 1 > 0){
14049 this.blockFocus=true;
14055 if(this.hiddenField){
14056 this.hiddenField.dom.value = vv;
14058 this.lastSelectionText = dv;
14059 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14063 // no hidden field.. - we store the value in 'value', but still display
14064 // display field!!!!
14065 this.lastSelectionText = dv;
14066 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14073 reset : function(){
14074 // overridden so that last data is reset..
14081 this.setValue(this.originalValue);
14082 //this.clearInvalid();
14083 this.lastData = false;
14085 this.view.clearSelections();
14091 findRecord : function(prop, value){
14093 if(this.store.getCount() > 0){
14094 this.store.each(function(r){
14095 if(r.data[prop] == value){
14105 getName: function()
14107 // returns hidden if it's set..
14108 if (!this.rendered) {return ''};
14109 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14113 onViewMove : function(e, t){
14114 this.inKeyMode = false;
14118 onViewOver : function(e, t){
14119 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14122 var item = this.view.findItemFromChild(t);
14125 var index = this.view.indexOf(item);
14126 this.select(index, false);
14131 onViewClick : function(view, doFocus, el, e)
14133 var index = this.view.getSelectedIndexes()[0];
14135 var r = this.store.getAt(index);
14139 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14146 Roo.each(this.tickItems, function(v,k){
14148 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14150 _this.tickItems.splice(k, 1);
14152 if(typeof(e) == 'undefined' && view == false){
14153 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14165 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14166 this.tickItems.push(r.data);
14169 if(typeof(e) == 'undefined' && view == false){
14170 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14177 this.onSelect(r, index);
14179 if(doFocus !== false && !this.blockFocus){
14180 this.inputEl().focus();
14185 restrictHeight : function(){
14186 //this.innerList.dom.style.height = '';
14187 //var inner = this.innerList.dom;
14188 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14189 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14190 //this.list.beginUpdate();
14191 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14192 this.list.alignTo(this.inputEl(), this.listAlign);
14193 this.list.alignTo(this.inputEl(), this.listAlign);
14194 //this.list.endUpdate();
14198 onEmptyResults : function(){
14200 if(this.tickable && this.editable){
14201 this.hasFocus = false;
14202 this.restrictHeight();
14210 * Returns true if the dropdown list is expanded, else false.
14212 isExpanded : function(){
14213 return this.list.isVisible();
14217 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14218 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14219 * @param {String} value The data value of the item to select
14220 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14221 * selected item if it is not currently in view (defaults to true)
14222 * @return {Boolean} True if the value matched an item in the list, else false
14224 selectByValue : function(v, scrollIntoView){
14225 if(v !== undefined && v !== null){
14226 var r = this.findRecord(this.valueField || this.displayField, v);
14228 this.select(this.store.indexOf(r), scrollIntoView);
14236 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14237 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14238 * @param {Number} index The zero-based index of the list item to select
14239 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14240 * selected item if it is not currently in view (defaults to true)
14242 select : function(index, scrollIntoView){
14243 this.selectedIndex = index;
14244 this.view.select(index);
14245 if(scrollIntoView !== false){
14246 var el = this.view.getNode(index);
14248 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14251 this.list.scrollChildIntoView(el, false);
14257 selectNext : function(){
14258 var ct = this.store.getCount();
14260 if(this.selectedIndex == -1){
14262 }else if(this.selectedIndex < ct-1){
14263 this.select(this.selectedIndex+1);
14269 selectPrev : function(){
14270 var ct = this.store.getCount();
14272 if(this.selectedIndex == -1){
14274 }else if(this.selectedIndex != 0){
14275 this.select(this.selectedIndex-1);
14281 onKeyUp : function(e){
14282 if(this.editable !== false && !e.isSpecialKey()){
14283 this.lastKey = e.getKey();
14284 this.dqTask.delay(this.queryDelay);
14289 validateBlur : function(){
14290 return !this.list || !this.list.isVisible();
14294 initQuery : function(){
14296 var v = this.getRawValue();
14298 if(this.tickable && this.editable){
14299 v = this.tickableInputEl().getValue();
14306 doForce : function(){
14307 if(this.inputEl().dom.value.length > 0){
14308 this.inputEl().dom.value =
14309 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14315 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14316 * query allowing the query action to be canceled if needed.
14317 * @param {String} query The SQL query to execute
14318 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14319 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14320 * saved in the current store (defaults to false)
14322 doQuery : function(q, forceAll){
14324 if(q === undefined || q === null){
14329 forceAll: forceAll,
14333 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14338 forceAll = qe.forceAll;
14339 if(forceAll === true || (q.length >= this.minChars)){
14341 this.hasQuery = true;
14343 if(this.lastQuery != q || this.alwaysQuery){
14344 this.lastQuery = q;
14345 if(this.mode == 'local'){
14346 this.selectedIndex = -1;
14348 this.store.clearFilter();
14351 if(this.specialFilter){
14352 this.fireEvent('specialfilter', this);
14357 this.store.filter(this.displayField, q);
14360 this.store.fireEvent("datachanged", this.store);
14367 this.store.baseParams[this.queryParam] = q;
14369 var options = {params : this.getParams(q)};
14372 options.add = true;
14373 options.params.start = this.page * this.pageSize;
14376 this.store.load(options);
14379 * this code will make the page width larger, at the beginning, the list not align correctly,
14380 * we should expand the list on onLoad
14381 * so command out it
14386 this.selectedIndex = -1;
14391 this.loadNext = false;
14395 getParams : function(q){
14397 //p[this.queryParam] = q;
14401 p.limit = this.pageSize;
14407 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14409 collapse : function(){
14410 if(!this.isExpanded()){
14416 this.hasFocus = false;
14420 this.cancelBtn.hide();
14421 this.trigger.show();
14424 this.tickableInputEl().dom.value = '';
14425 this.tickableInputEl().blur();
14430 Roo.get(document).un('mousedown', this.collapseIf, this);
14431 Roo.get(document).un('mousewheel', this.collapseIf, this);
14432 if (!this.editable) {
14433 Roo.get(document).un('keydown', this.listKeyPress, this);
14435 this.fireEvent('collapse', this);
14441 collapseIf : function(e){
14442 var in_combo = e.within(this.el);
14443 var in_list = e.within(this.list);
14444 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14446 if (in_combo || in_list || is_list) {
14447 //e.stopPropagation();
14452 this.onTickableFooterButtonClick(e, false, false);
14460 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14462 expand : function(){
14464 if(this.isExpanded() || !this.hasFocus){
14468 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14469 this.list.setWidth(lw);
14475 this.restrictHeight();
14479 this.tickItems = Roo.apply([], this.item);
14482 this.cancelBtn.show();
14483 this.trigger.hide();
14486 this.tickableInputEl().focus();
14491 Roo.get(document).on('mousedown', this.collapseIf, this);
14492 Roo.get(document).on('mousewheel', this.collapseIf, this);
14493 if (!this.editable) {
14494 Roo.get(document).on('keydown', this.listKeyPress, this);
14497 this.fireEvent('expand', this);
14501 // Implements the default empty TriggerField.onTriggerClick function
14502 onTriggerClick : function(e)
14504 Roo.log('trigger click');
14506 if(this.disabled || !this.triggerList){
14511 this.loadNext = false;
14513 if(this.isExpanded()){
14515 if (!this.blockFocus) {
14516 this.inputEl().focus();
14520 this.hasFocus = true;
14521 if(this.triggerAction == 'all') {
14522 this.doQuery(this.allQuery, true);
14524 this.doQuery(this.getRawValue());
14526 if (!this.blockFocus) {
14527 this.inputEl().focus();
14532 onTickableTriggerClick : function(e)
14539 this.loadNext = false;
14540 this.hasFocus = true;
14542 if(this.triggerAction == 'all') {
14543 this.doQuery(this.allQuery, true);
14545 this.doQuery(this.getRawValue());
14549 onSearchFieldClick : function(e)
14551 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14552 this.onTickableFooterButtonClick(e, false, false);
14556 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14561 this.loadNext = false;
14562 this.hasFocus = true;
14564 if(this.triggerAction == 'all') {
14565 this.doQuery(this.allQuery, true);
14567 this.doQuery(this.getRawValue());
14571 listKeyPress : function(e)
14573 //Roo.log('listkeypress');
14574 // scroll to first matching element based on key pres..
14575 if (e.isSpecialKey()) {
14578 var k = String.fromCharCode(e.getKey()).toUpperCase();
14581 var csel = this.view.getSelectedNodes();
14582 var cselitem = false;
14584 var ix = this.view.indexOf(csel[0]);
14585 cselitem = this.store.getAt(ix);
14586 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14592 this.store.each(function(v) {
14594 // start at existing selection.
14595 if (cselitem.id == v.id) {
14601 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14602 match = this.store.indexOf(v);
14608 if (match === false) {
14609 return true; // no more action?
14612 this.view.select(match);
14613 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14614 sn.scrollIntoView(sn.dom.parentNode, false);
14617 onViewScroll : function(e, t){
14619 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){
14623 this.hasQuery = true;
14625 this.loading = this.list.select('.loading', true).first();
14627 if(this.loading === null){
14628 this.list.createChild({
14630 cls: 'loading roo-select2-more-results roo-select2-active',
14631 html: 'Loading more results...'
14634 this.loading = this.list.select('.loading', true).first();
14636 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14638 this.loading.hide();
14641 this.loading.show();
14646 this.loadNext = true;
14648 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14653 addItem : function(o)
14655 var dv = ''; // display value
14657 if (this.displayField) {
14658 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14660 // this is an error condition!!!
14661 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14668 var choice = this.choices.createChild({
14670 cls: 'roo-select2-search-choice',
14679 cls: 'roo-select2-search-choice-close fa fa-times',
14684 }, this.searchField);
14686 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14688 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14696 this.inputEl().dom.value = '';
14701 onRemoveItem : function(e, _self, o)
14703 e.preventDefault();
14705 this.lastItem = Roo.apply([], this.item);
14707 var index = this.item.indexOf(o.data) * 1;
14710 Roo.log('not this item?!');
14714 this.item.splice(index, 1);
14719 this.fireEvent('remove', this, e);
14725 syncValue : function()
14727 if(!this.item.length){
14734 Roo.each(this.item, function(i){
14735 if(_this.valueField){
14736 value.push(i[_this.valueField]);
14743 this.value = value.join(',');
14745 if(this.hiddenField){
14746 this.hiddenField.dom.value = this.value;
14749 this.store.fireEvent("datachanged", this.store);
14754 clearItem : function()
14756 if(!this.multiple){
14762 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14770 if(this.tickable && !Roo.isTouch){
14771 this.view.refresh();
14775 inputEl: function ()
14777 if(Roo.isIOS && this.useNativeIOS){
14778 return this.el.select('select.roo-ios-select', true).first();
14781 if(Roo.isTouch && this.mobileTouchView){
14782 return this.el.select('input.form-control',true).first();
14786 return this.searchField;
14789 return this.el.select('input.form-control',true).first();
14792 onTickableFooterButtonClick : function(e, btn, el)
14794 e.preventDefault();
14796 this.lastItem = Roo.apply([], this.item);
14798 if(btn && btn.name == 'cancel'){
14799 this.tickItems = Roo.apply([], this.item);
14808 Roo.each(this.tickItems, function(o){
14816 validate : function()
14818 if(this.getVisibilityEl().hasClass('hidden')){
14822 var v = this.getRawValue();
14825 v = this.getValue();
14828 if(this.disabled || this.allowBlank || v.length){
14833 this.markInvalid();
14837 tickableInputEl : function()
14839 if(!this.tickable || !this.editable){
14840 return this.inputEl();
14843 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14847 getAutoCreateTouchView : function()
14852 cls: 'form-group' //input-group
14858 type : this.inputType,
14859 cls : 'form-control x-combo-noedit',
14860 autocomplete: 'new-password',
14861 placeholder : this.placeholder || '',
14866 input.name = this.name;
14870 input.cls += ' input-' + this.size;
14873 if (this.disabled) {
14874 input.disabled = true;
14885 inputblock.cls += ' input-group';
14887 inputblock.cn.unshift({
14889 cls : 'input-group-addon',
14894 if(this.removable && !this.multiple){
14895 inputblock.cls += ' roo-removable';
14897 inputblock.cn.push({
14900 cls : 'roo-combo-removable-btn close'
14904 if(this.hasFeedback && !this.allowBlank){
14906 inputblock.cls += ' has-feedback';
14908 inputblock.cn.push({
14910 cls: 'glyphicon form-control-feedback'
14917 inputblock.cls += (this.before) ? '' : ' input-group';
14919 inputblock.cn.push({
14921 cls : 'input-group-addon',
14932 cls: 'form-hidden-field'
14946 cls: 'form-hidden-field'
14950 cls: 'roo-select2-choices',
14954 cls: 'roo-select2-search-field',
14967 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14973 if(!this.multiple && this.showToggleBtn){
14980 if (this.caret != false) {
14983 cls: 'fa fa-' + this.caret
14990 cls : 'input-group-addon btn dropdown-toggle',
14995 cls: 'combobox-clear',
15009 combobox.cls += ' roo-select2-container-multi';
15012 var align = this.labelAlign || this.parentLabelAlign();
15014 if (align ==='left' && this.fieldLabel.length) {
15019 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15020 tooltip : 'This field is required'
15024 cls : 'control-label',
15025 html : this.fieldLabel
15036 var labelCfg = cfg.cn[1];
15037 var contentCfg = cfg.cn[2];
15040 if(this.indicatorpos == 'right'){
15045 cls : 'control-label',
15049 html : this.fieldLabel
15053 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15054 tooltip : 'This field is required'
15067 labelCfg = cfg.cn[0];
15068 contentCfg = cfg.cn[1];
15073 if(this.labelWidth > 12){
15074 labelCfg.style = "width: " + this.labelWidth + 'px';
15077 if(this.labelWidth < 13 && this.labelmd == 0){
15078 this.labelmd = this.labelWidth;
15081 if(this.labellg > 0){
15082 labelCfg.cls += ' col-lg-' + this.labellg;
15083 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15086 if(this.labelmd > 0){
15087 labelCfg.cls += ' col-md-' + this.labelmd;
15088 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15091 if(this.labelsm > 0){
15092 labelCfg.cls += ' col-sm-' + this.labelsm;
15093 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15096 if(this.labelxs > 0){
15097 labelCfg.cls += ' col-xs-' + this.labelxs;
15098 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15102 } else if ( this.fieldLabel.length) {
15106 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15107 tooltip : 'This field is required'
15111 cls : 'control-label',
15112 html : this.fieldLabel
15123 if(this.indicatorpos == 'right'){
15127 cls : 'control-label',
15128 html : this.fieldLabel,
15132 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15133 tooltip : 'This field is required'
15150 var settings = this;
15152 ['xs','sm','md','lg'].map(function(size){
15153 if (settings[size]) {
15154 cfg.cls += ' col-' + size + '-' + settings[size];
15161 initTouchView : function()
15163 this.renderTouchView();
15165 this.touchViewEl.on('scroll', function(){
15166 this.el.dom.scrollTop = 0;
15169 this.originalValue = this.getValue();
15171 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15173 this.inputEl().on("click", this.showTouchView, this);
15174 if (this.triggerEl) {
15175 this.triggerEl.on("click", this.showTouchView, this);
15179 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15180 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15182 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15184 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15185 this.store.on('load', this.onTouchViewLoad, this);
15186 this.store.on('loadexception', this.onTouchViewLoadException, this);
15188 if(this.hiddenName){
15190 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15192 this.hiddenField.dom.value =
15193 this.hiddenValue !== undefined ? this.hiddenValue :
15194 this.value !== undefined ? this.value : '';
15196 this.el.dom.removeAttribute('name');
15197 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15201 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15202 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15205 if(this.removable && !this.multiple){
15206 var close = this.closeTriggerEl();
15208 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15209 close.on('click', this.removeBtnClick, this, close);
15213 * fix the bug in Safari iOS8
15215 this.inputEl().on("focus", function(e){
15216 document.activeElement.blur();
15224 renderTouchView : function()
15226 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15227 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15229 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15230 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15232 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15233 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15234 this.touchViewBodyEl.setStyle('overflow', 'auto');
15236 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15237 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15239 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15240 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15244 showTouchView : function()
15250 this.touchViewHeaderEl.hide();
15252 if(this.modalTitle.length){
15253 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15254 this.touchViewHeaderEl.show();
15257 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15258 this.touchViewEl.show();
15260 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15262 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15263 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15265 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15267 if(this.modalTitle.length){
15268 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15271 this.touchViewBodyEl.setHeight(bodyHeight);
15275 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15277 this.touchViewEl.addClass('in');
15280 this.doTouchViewQuery();
15284 hideTouchView : function()
15286 this.touchViewEl.removeClass('in');
15290 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15292 this.touchViewEl.setStyle('display', 'none');
15297 setTouchViewValue : function()
15304 Roo.each(this.tickItems, function(o){
15309 this.hideTouchView();
15312 doTouchViewQuery : function()
15321 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15325 if(!this.alwaysQuery || this.mode == 'local'){
15326 this.onTouchViewLoad();
15333 onTouchViewBeforeLoad : function(combo,opts)
15339 onTouchViewLoad : function()
15341 if(this.store.getCount() < 1){
15342 this.onTouchViewEmptyResults();
15346 this.clearTouchView();
15348 var rawValue = this.getRawValue();
15350 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15352 this.tickItems = [];
15354 this.store.data.each(function(d, rowIndex){
15355 var row = this.touchViewListGroup.createChild(template);
15357 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15358 row.addClass(d.data.cls);
15361 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15364 html : d.data[this.displayField]
15367 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15368 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15371 row.removeClass('selected');
15372 if(!this.multiple && this.valueField &&
15373 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15376 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15377 row.addClass('selected');
15380 if(this.multiple && this.valueField &&
15381 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15385 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15386 this.tickItems.push(d.data);
15389 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15393 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15395 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15397 if(this.modalTitle.length){
15398 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15401 var listHeight = this.touchViewListGroup.getHeight();
15405 if(firstChecked && listHeight > bodyHeight){
15406 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15411 onTouchViewLoadException : function()
15413 this.hideTouchView();
15416 onTouchViewEmptyResults : function()
15418 this.clearTouchView();
15420 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15422 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15426 clearTouchView : function()
15428 this.touchViewListGroup.dom.innerHTML = '';
15431 onTouchViewClick : function(e, el, o)
15433 e.preventDefault();
15436 var rowIndex = o.rowIndex;
15438 var r = this.store.getAt(rowIndex);
15440 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15442 if(!this.multiple){
15443 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15444 c.dom.removeAttribute('checked');
15447 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15449 this.setFromData(r.data);
15451 var close = this.closeTriggerEl();
15457 this.hideTouchView();
15459 this.fireEvent('select', this, r, rowIndex);
15464 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15465 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15466 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15470 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15471 this.addItem(r.data);
15472 this.tickItems.push(r.data);
15476 getAutoCreateNativeIOS : function()
15479 cls: 'form-group' //input-group,
15484 cls : 'roo-ios-select'
15488 combobox.name = this.name;
15491 if (this.disabled) {
15492 combobox.disabled = true;
15495 var settings = this;
15497 ['xs','sm','md','lg'].map(function(size){
15498 if (settings[size]) {
15499 cfg.cls += ' col-' + size + '-' + settings[size];
15509 initIOSView : function()
15511 this.store.on('load', this.onIOSViewLoad, this);
15516 onIOSViewLoad : function()
15518 if(this.store.getCount() < 1){
15522 this.clearIOSView();
15524 if(this.allowBlank) {
15526 var default_text = '-- SELECT --';
15528 if(this.placeholder.length){
15529 default_text = this.placeholder;
15532 if(this.emptyTitle.length){
15533 default_text += ' - ' + this.emptyTitle + ' -';
15536 var opt = this.inputEl().createChild({
15539 html : default_text
15543 o[this.valueField] = 0;
15544 o[this.displayField] = default_text;
15546 this.ios_options.push({
15553 this.store.data.each(function(d, rowIndex){
15557 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15558 html = d.data[this.displayField];
15563 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15564 value = d.data[this.valueField];
15573 if(this.value == d.data[this.valueField]){
15574 option['selected'] = true;
15577 var opt = this.inputEl().createChild(option);
15579 this.ios_options.push({
15586 this.inputEl().on('change', function(){
15587 this.fireEvent('select', this);
15592 clearIOSView: function()
15594 this.inputEl().dom.innerHTML = '';
15596 this.ios_options = [];
15599 setIOSValue: function(v)
15603 if(!this.ios_options){
15607 Roo.each(this.ios_options, function(opts){
15609 opts.el.dom.removeAttribute('selected');
15611 if(opts.data[this.valueField] != v){
15615 opts.el.dom.setAttribute('selected', true);
15621 * @cfg {Boolean} grow
15625 * @cfg {Number} growMin
15629 * @cfg {Number} growMax
15638 Roo.apply(Roo.bootstrap.ComboBox, {
15642 cls: 'modal-header',
15664 cls: 'list-group-item',
15668 cls: 'roo-combobox-list-group-item-value'
15672 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15686 listItemCheckbox : {
15688 cls: 'list-group-item',
15692 cls: 'roo-combobox-list-group-item-value'
15696 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15712 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15717 cls: 'modal-footer',
15725 cls: 'col-xs-6 text-left',
15728 cls: 'btn btn-danger roo-touch-view-cancel',
15734 cls: 'col-xs-6 text-right',
15737 cls: 'btn btn-success roo-touch-view-ok',
15748 Roo.apply(Roo.bootstrap.ComboBox, {
15750 touchViewTemplate : {
15752 cls: 'modal fade roo-combobox-touch-view',
15756 cls: 'modal-dialog',
15757 style : 'position:fixed', // we have to fix position....
15761 cls: 'modal-content',
15763 Roo.bootstrap.ComboBox.header,
15764 Roo.bootstrap.ComboBox.body,
15765 Roo.bootstrap.ComboBox.footer
15774 * Ext JS Library 1.1.1
15775 * Copyright(c) 2006-2007, Ext JS, LLC.
15777 * Originally Released Under LGPL - original licence link has changed is not relivant.
15780 * <script type="text/javascript">
15785 * @extends Roo.util.Observable
15786 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15787 * This class also supports single and multi selection modes. <br>
15788 * Create a data model bound view:
15790 var store = new Roo.data.Store(...);
15792 var view = new Roo.View({
15794 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15796 singleSelect: true,
15797 selectedClass: "ydataview-selected",
15801 // listen for node click?
15802 view.on("click", function(vw, index, node, e){
15803 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15807 dataModel.load("foobar.xml");
15809 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15811 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15812 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15814 * Note: old style constructor is still suported (container, template, config)
15817 * Create a new View
15818 * @param {Object} config The config object
15821 Roo.View = function(config, depreciated_tpl, depreciated_config){
15823 this.parent = false;
15825 if (typeof(depreciated_tpl) == 'undefined') {
15826 // new way.. - universal constructor.
15827 Roo.apply(this, config);
15828 this.el = Roo.get(this.el);
15831 this.el = Roo.get(config);
15832 this.tpl = depreciated_tpl;
15833 Roo.apply(this, depreciated_config);
15835 this.wrapEl = this.el.wrap().wrap();
15836 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15839 if(typeof(this.tpl) == "string"){
15840 this.tpl = new Roo.Template(this.tpl);
15842 // support xtype ctors..
15843 this.tpl = new Roo.factory(this.tpl, Roo);
15847 this.tpl.compile();
15852 * @event beforeclick
15853 * Fires before a click is processed. Returns false to cancel the default action.
15854 * @param {Roo.View} this
15855 * @param {Number} index The index of the target node
15856 * @param {HTMLElement} node The target node
15857 * @param {Roo.EventObject} e The raw event object
15859 "beforeclick" : true,
15862 * Fires when a template node is clicked.
15863 * @param {Roo.View} this
15864 * @param {Number} index The index of the target node
15865 * @param {HTMLElement} node The target node
15866 * @param {Roo.EventObject} e The raw event object
15871 * Fires when a template node is double clicked.
15872 * @param {Roo.View} this
15873 * @param {Number} index The index of the target node
15874 * @param {HTMLElement} node The target node
15875 * @param {Roo.EventObject} e The raw event object
15879 * @event contextmenu
15880 * Fires when a template node is right clicked.
15881 * @param {Roo.View} this
15882 * @param {Number} index The index of the target node
15883 * @param {HTMLElement} node The target node
15884 * @param {Roo.EventObject} e The raw event object
15886 "contextmenu" : true,
15888 * @event selectionchange
15889 * Fires when the selected nodes change.
15890 * @param {Roo.View} this
15891 * @param {Array} selections Array of the selected nodes
15893 "selectionchange" : true,
15896 * @event beforeselect
15897 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15898 * @param {Roo.View} this
15899 * @param {HTMLElement} node The node to be selected
15900 * @param {Array} selections Array of currently selected nodes
15902 "beforeselect" : true,
15904 * @event preparedata
15905 * Fires on every row to render, to allow you to change the data.
15906 * @param {Roo.View} this
15907 * @param {Object} data to be rendered (change this)
15909 "preparedata" : true
15917 "click": this.onClick,
15918 "dblclick": this.onDblClick,
15919 "contextmenu": this.onContextMenu,
15923 this.selections = [];
15925 this.cmp = new Roo.CompositeElementLite([]);
15927 this.store = Roo.factory(this.store, Roo.data);
15928 this.setStore(this.store, true);
15931 if ( this.footer && this.footer.xtype) {
15933 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15935 this.footer.dataSource = this.store;
15936 this.footer.container = fctr;
15937 this.footer = Roo.factory(this.footer, Roo);
15938 fctr.insertFirst(this.el);
15940 // this is a bit insane - as the paging toolbar seems to detach the el..
15941 // dom.parentNode.parentNode.parentNode
15942 // they get detached?
15946 Roo.View.superclass.constructor.call(this);
15951 Roo.extend(Roo.View, Roo.util.Observable, {
15954 * @cfg {Roo.data.Store} store Data store to load data from.
15959 * @cfg {String|Roo.Element} el The container element.
15964 * @cfg {String|Roo.Template} tpl The template used by this View
15968 * @cfg {String} dataName the named area of the template to use as the data area
15969 * Works with domtemplates roo-name="name"
15973 * @cfg {String} selectedClass The css class to add to selected nodes
15975 selectedClass : "x-view-selected",
15977 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15982 * @cfg {String} text to display on mask (default Loading)
15986 * @cfg {Boolean} multiSelect Allow multiple selection
15988 multiSelect : false,
15990 * @cfg {Boolean} singleSelect Allow single selection
15992 singleSelect: false,
15995 * @cfg {Boolean} toggleSelect - selecting
15997 toggleSelect : false,
16000 * @cfg {Boolean} tickable - selecting
16005 * Returns the element this view is bound to.
16006 * @return {Roo.Element}
16008 getEl : function(){
16009 return this.wrapEl;
16015 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16017 refresh : function(){
16018 //Roo.log('refresh');
16021 // if we are using something like 'domtemplate', then
16022 // the what gets used is:
16023 // t.applySubtemplate(NAME, data, wrapping data..)
16024 // the outer template then get' applied with
16025 // the store 'extra data'
16026 // and the body get's added to the
16027 // roo-name="data" node?
16028 // <span class='roo-tpl-{name}'></span> ?????
16032 this.clearSelections();
16033 this.el.update("");
16035 var records = this.store.getRange();
16036 if(records.length < 1) {
16038 // is this valid?? = should it render a template??
16040 this.el.update(this.emptyText);
16044 if (this.dataName) {
16045 this.el.update(t.apply(this.store.meta)); //????
16046 el = this.el.child('.roo-tpl-' + this.dataName);
16049 for(var i = 0, len = records.length; i < len; i++){
16050 var data = this.prepareData(records[i].data, i, records[i]);
16051 this.fireEvent("preparedata", this, data, i, records[i]);
16053 var d = Roo.apply({}, data);
16056 Roo.apply(d, {'roo-id' : Roo.id()});
16060 Roo.each(this.parent.item, function(item){
16061 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16064 Roo.apply(d, {'roo-data-checked' : 'checked'});
16068 html[html.length] = Roo.util.Format.trim(
16070 t.applySubtemplate(this.dataName, d, this.store.meta) :
16077 el.update(html.join(""));
16078 this.nodes = el.dom.childNodes;
16079 this.updateIndexes(0);
16084 * Function to override to reformat the data that is sent to
16085 * the template for each node.
16086 * DEPRICATED - use the preparedata event handler.
16087 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16088 * a JSON object for an UpdateManager bound view).
16090 prepareData : function(data, index, record)
16092 this.fireEvent("preparedata", this, data, index, record);
16096 onUpdate : function(ds, record){
16097 // Roo.log('on update');
16098 this.clearSelections();
16099 var index = this.store.indexOf(record);
16100 var n = this.nodes[index];
16101 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16102 n.parentNode.removeChild(n);
16103 this.updateIndexes(index, index);
16109 onAdd : function(ds, records, index)
16111 //Roo.log(['on Add', ds, records, index] );
16112 this.clearSelections();
16113 if(this.nodes.length == 0){
16117 var n = this.nodes[index];
16118 for(var i = 0, len = records.length; i < len; i++){
16119 var d = this.prepareData(records[i].data, i, records[i]);
16121 this.tpl.insertBefore(n, d);
16124 this.tpl.append(this.el, d);
16127 this.updateIndexes(index);
16130 onRemove : function(ds, record, index){
16131 // Roo.log('onRemove');
16132 this.clearSelections();
16133 var el = this.dataName ?
16134 this.el.child('.roo-tpl-' + this.dataName) :
16137 el.dom.removeChild(this.nodes[index]);
16138 this.updateIndexes(index);
16142 * Refresh an individual node.
16143 * @param {Number} index
16145 refreshNode : function(index){
16146 this.onUpdate(this.store, this.store.getAt(index));
16149 updateIndexes : function(startIndex, endIndex){
16150 var ns = this.nodes;
16151 startIndex = startIndex || 0;
16152 endIndex = endIndex || ns.length - 1;
16153 for(var i = startIndex; i <= endIndex; i++){
16154 ns[i].nodeIndex = i;
16159 * Changes the data store this view uses and refresh the view.
16160 * @param {Store} store
16162 setStore : function(store, initial){
16163 if(!initial && this.store){
16164 this.store.un("datachanged", this.refresh);
16165 this.store.un("add", this.onAdd);
16166 this.store.un("remove", this.onRemove);
16167 this.store.un("update", this.onUpdate);
16168 this.store.un("clear", this.refresh);
16169 this.store.un("beforeload", this.onBeforeLoad);
16170 this.store.un("load", this.onLoad);
16171 this.store.un("loadexception", this.onLoad);
16175 store.on("datachanged", this.refresh, this);
16176 store.on("add", this.onAdd, this);
16177 store.on("remove", this.onRemove, this);
16178 store.on("update", this.onUpdate, this);
16179 store.on("clear", this.refresh, this);
16180 store.on("beforeload", this.onBeforeLoad, this);
16181 store.on("load", this.onLoad, this);
16182 store.on("loadexception", this.onLoad, this);
16190 * onbeforeLoad - masks the loading area.
16193 onBeforeLoad : function(store,opts)
16195 //Roo.log('onBeforeLoad');
16197 this.el.update("");
16199 this.el.mask(this.mask ? this.mask : "Loading" );
16201 onLoad : function ()
16208 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16209 * @param {HTMLElement} node
16210 * @return {HTMLElement} The template node
16212 findItemFromChild : function(node){
16213 var el = this.dataName ?
16214 this.el.child('.roo-tpl-' + this.dataName,true) :
16217 if(!node || node.parentNode == el){
16220 var p = node.parentNode;
16221 while(p && p != el){
16222 if(p.parentNode == el){
16231 onClick : function(e){
16232 var item = this.findItemFromChild(e.getTarget());
16234 var index = this.indexOf(item);
16235 if(this.onItemClick(item, index, e) !== false){
16236 this.fireEvent("click", this, index, item, e);
16239 this.clearSelections();
16244 onContextMenu : function(e){
16245 var item = this.findItemFromChild(e.getTarget());
16247 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16252 onDblClick : function(e){
16253 var item = this.findItemFromChild(e.getTarget());
16255 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16259 onItemClick : function(item, index, e)
16261 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16264 if (this.toggleSelect) {
16265 var m = this.isSelected(item) ? 'unselect' : 'select';
16268 _t[m](item, true, false);
16271 if(this.multiSelect || this.singleSelect){
16272 if(this.multiSelect && e.shiftKey && this.lastSelection){
16273 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16275 this.select(item, this.multiSelect && e.ctrlKey);
16276 this.lastSelection = item;
16279 if(!this.tickable){
16280 e.preventDefault();
16288 * Get the number of selected nodes.
16291 getSelectionCount : function(){
16292 return this.selections.length;
16296 * Get the currently selected nodes.
16297 * @return {Array} An array of HTMLElements
16299 getSelectedNodes : function(){
16300 return this.selections;
16304 * Get the indexes of the selected nodes.
16307 getSelectedIndexes : function(){
16308 var indexes = [], s = this.selections;
16309 for(var i = 0, len = s.length; i < len; i++){
16310 indexes.push(s[i].nodeIndex);
16316 * Clear all selections
16317 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16319 clearSelections : function(suppressEvent){
16320 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16321 this.cmp.elements = this.selections;
16322 this.cmp.removeClass(this.selectedClass);
16323 this.selections = [];
16324 if(!suppressEvent){
16325 this.fireEvent("selectionchange", this, this.selections);
16331 * Returns true if the passed node is selected
16332 * @param {HTMLElement/Number} node The node or node index
16333 * @return {Boolean}
16335 isSelected : function(node){
16336 var s = this.selections;
16340 node = this.getNode(node);
16341 return s.indexOf(node) !== -1;
16346 * @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
16347 * @param {Boolean} keepExisting (optional) true to keep existing selections
16348 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16350 select : function(nodeInfo, keepExisting, suppressEvent){
16351 if(nodeInfo instanceof Array){
16353 this.clearSelections(true);
16355 for(var i = 0, len = nodeInfo.length; i < len; i++){
16356 this.select(nodeInfo[i], true, true);
16360 var node = this.getNode(nodeInfo);
16361 if(!node || this.isSelected(node)){
16362 return; // already selected.
16365 this.clearSelections(true);
16368 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16369 Roo.fly(node).addClass(this.selectedClass);
16370 this.selections.push(node);
16371 if(!suppressEvent){
16372 this.fireEvent("selectionchange", this, this.selections);
16380 * @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
16381 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16382 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16384 unselect : function(nodeInfo, keepExisting, suppressEvent)
16386 if(nodeInfo instanceof Array){
16387 Roo.each(this.selections, function(s) {
16388 this.unselect(s, nodeInfo);
16392 var node = this.getNode(nodeInfo);
16393 if(!node || !this.isSelected(node)){
16394 //Roo.log("not selected");
16395 return; // not selected.
16399 Roo.each(this.selections, function(s) {
16401 Roo.fly(node).removeClass(this.selectedClass);
16408 this.selections= ns;
16409 this.fireEvent("selectionchange", this, this.selections);
16413 * Gets a template node.
16414 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16415 * @return {HTMLElement} The node or null if it wasn't found
16417 getNode : function(nodeInfo){
16418 if(typeof nodeInfo == "string"){
16419 return document.getElementById(nodeInfo);
16420 }else if(typeof nodeInfo == "number"){
16421 return this.nodes[nodeInfo];
16427 * Gets a range template nodes.
16428 * @param {Number} startIndex
16429 * @param {Number} endIndex
16430 * @return {Array} An array of nodes
16432 getNodes : function(start, end){
16433 var ns = this.nodes;
16434 start = start || 0;
16435 end = typeof end == "undefined" ? ns.length - 1 : end;
16438 for(var i = start; i <= end; i++){
16442 for(var i = start; i >= end; i--){
16450 * Finds the index of the passed node
16451 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16452 * @return {Number} The index of the node or -1
16454 indexOf : function(node){
16455 node = this.getNode(node);
16456 if(typeof node.nodeIndex == "number"){
16457 return node.nodeIndex;
16459 var ns = this.nodes;
16460 for(var i = 0, len = ns.length; i < len; i++){
16471 * based on jquery fullcalendar
16475 Roo.bootstrap = Roo.bootstrap || {};
16477 * @class Roo.bootstrap.Calendar
16478 * @extends Roo.bootstrap.Component
16479 * Bootstrap Calendar class
16480 * @cfg {Boolean} loadMask (true|false) default false
16481 * @cfg {Object} header generate the user specific header of the calendar, default false
16484 * Create a new Container
16485 * @param {Object} config The config object
16490 Roo.bootstrap.Calendar = function(config){
16491 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16495 * Fires when a date is selected
16496 * @param {DatePicker} this
16497 * @param {Date} date The selected date
16501 * @event monthchange
16502 * Fires when the displayed month changes
16503 * @param {DatePicker} this
16504 * @param {Date} date The selected month
16506 'monthchange': true,
16508 * @event evententer
16509 * Fires when mouse over an event
16510 * @param {Calendar} this
16511 * @param {event} Event
16513 'evententer': true,
16515 * @event eventleave
16516 * Fires when the mouse leaves an
16517 * @param {Calendar} this
16520 'eventleave': true,
16522 * @event eventclick
16523 * Fires when the mouse click an
16524 * @param {Calendar} this
16533 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16536 * @cfg {Number} startDay
16537 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16545 getAutoCreate : function(){
16548 var fc_button = function(name, corner, style, content ) {
16549 return Roo.apply({},{
16551 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16553 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16556 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16567 style : 'width:100%',
16574 cls : 'fc-header-left',
16576 fc_button('prev', 'left', 'arrow', '‹' ),
16577 fc_button('next', 'right', 'arrow', '›' ),
16578 { tag: 'span', cls: 'fc-header-space' },
16579 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16587 cls : 'fc-header-center',
16591 cls: 'fc-header-title',
16594 html : 'month / year'
16602 cls : 'fc-header-right',
16604 /* fc_button('month', 'left', '', 'month' ),
16605 fc_button('week', '', '', 'week' ),
16606 fc_button('day', 'right', '', 'day' )
16618 header = this.header;
16621 var cal_heads = function() {
16623 // fixme - handle this.
16625 for (var i =0; i < Date.dayNames.length; i++) {
16626 var d = Date.dayNames[i];
16629 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16630 html : d.substring(0,3)
16634 ret[0].cls += ' fc-first';
16635 ret[6].cls += ' fc-last';
16638 var cal_cell = function(n) {
16641 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16646 cls: 'fc-day-number',
16650 cls: 'fc-day-content',
16654 style: 'position: relative;' // height: 17px;
16666 var cal_rows = function() {
16669 for (var r = 0; r < 6; r++) {
16676 for (var i =0; i < Date.dayNames.length; i++) {
16677 var d = Date.dayNames[i];
16678 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16681 row.cn[0].cls+=' fc-first';
16682 row.cn[0].cn[0].style = 'min-height:90px';
16683 row.cn[6].cls+=' fc-last';
16687 ret[0].cls += ' fc-first';
16688 ret[4].cls += ' fc-prev-last';
16689 ret[5].cls += ' fc-last';
16696 cls: 'fc-border-separate',
16697 style : 'width:100%',
16705 cls : 'fc-first fc-last',
16723 cls : 'fc-content',
16724 style : "position: relative;",
16727 cls : 'fc-view fc-view-month fc-grid',
16728 style : 'position: relative',
16729 unselectable : 'on',
16732 cls : 'fc-event-container',
16733 style : 'position:absolute;z-index:8;top:0;left:0;'
16751 initEvents : function()
16754 throw "can not find store for calendar";
16760 style: "text-align:center",
16764 style: "background-color:white;width:50%;margin:250 auto",
16768 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16779 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16781 var size = this.el.select('.fc-content', true).first().getSize();
16782 this.maskEl.setSize(size.width, size.height);
16783 this.maskEl.enableDisplayMode("block");
16784 if(!this.loadMask){
16785 this.maskEl.hide();
16788 this.store = Roo.factory(this.store, Roo.data);
16789 this.store.on('load', this.onLoad, this);
16790 this.store.on('beforeload', this.onBeforeLoad, this);
16794 this.cells = this.el.select('.fc-day',true);
16795 //Roo.log(this.cells);
16796 this.textNodes = this.el.query('.fc-day-number');
16797 this.cells.addClassOnOver('fc-state-hover');
16799 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16800 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16801 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16802 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16804 this.on('monthchange', this.onMonthChange, this);
16806 this.update(new Date().clearTime());
16809 resize : function() {
16810 var sz = this.el.getSize();
16812 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16813 this.el.select('.fc-day-content div',true).setHeight(34);
16818 showPrevMonth : function(e){
16819 this.update(this.activeDate.add("mo", -1));
16821 showToday : function(e){
16822 this.update(new Date().clearTime());
16825 showNextMonth : function(e){
16826 this.update(this.activeDate.add("mo", 1));
16830 showPrevYear : function(){
16831 this.update(this.activeDate.add("y", -1));
16835 showNextYear : function(){
16836 this.update(this.activeDate.add("y", 1));
16841 update : function(date)
16843 var vd = this.activeDate;
16844 this.activeDate = date;
16845 // if(vd && this.el){
16846 // var t = date.getTime();
16847 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16848 // Roo.log('using add remove');
16850 // this.fireEvent('monthchange', this, date);
16852 // this.cells.removeClass("fc-state-highlight");
16853 // this.cells.each(function(c){
16854 // if(c.dateValue == t){
16855 // c.addClass("fc-state-highlight");
16856 // setTimeout(function(){
16857 // try{c.dom.firstChild.focus();}catch(e){}
16867 var days = date.getDaysInMonth();
16869 var firstOfMonth = date.getFirstDateOfMonth();
16870 var startingPos = firstOfMonth.getDay()-this.startDay;
16872 if(startingPos < this.startDay){
16876 var pm = date.add(Date.MONTH, -1);
16877 var prevStart = pm.getDaysInMonth()-startingPos;
16879 this.cells = this.el.select('.fc-day',true);
16880 this.textNodes = this.el.query('.fc-day-number');
16881 this.cells.addClassOnOver('fc-state-hover');
16883 var cells = this.cells.elements;
16884 var textEls = this.textNodes;
16886 Roo.each(cells, function(cell){
16887 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16890 days += startingPos;
16892 // convert everything to numbers so it's fast
16893 var day = 86400000;
16894 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16897 //Roo.log(prevStart);
16899 var today = new Date().clearTime().getTime();
16900 var sel = date.clearTime().getTime();
16901 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16902 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16903 var ddMatch = this.disabledDatesRE;
16904 var ddText = this.disabledDatesText;
16905 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16906 var ddaysText = this.disabledDaysText;
16907 var format = this.format;
16909 var setCellClass = function(cal, cell){
16913 //Roo.log('set Cell Class');
16915 var t = d.getTime();
16919 cell.dateValue = t;
16921 cell.className += " fc-today";
16922 cell.className += " fc-state-highlight";
16923 cell.title = cal.todayText;
16926 // disable highlight in other month..
16927 //cell.className += " fc-state-highlight";
16932 cell.className = " fc-state-disabled";
16933 cell.title = cal.minText;
16937 cell.className = " fc-state-disabled";
16938 cell.title = cal.maxText;
16942 if(ddays.indexOf(d.getDay()) != -1){
16943 cell.title = ddaysText;
16944 cell.className = " fc-state-disabled";
16947 if(ddMatch && format){
16948 var fvalue = d.dateFormat(format);
16949 if(ddMatch.test(fvalue)){
16950 cell.title = ddText.replace("%0", fvalue);
16951 cell.className = " fc-state-disabled";
16955 if (!cell.initialClassName) {
16956 cell.initialClassName = cell.dom.className;
16959 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16964 for(; i < startingPos; i++) {
16965 textEls[i].innerHTML = (++prevStart);
16966 d.setDate(d.getDate()+1);
16968 cells[i].className = "fc-past fc-other-month";
16969 setCellClass(this, cells[i]);
16974 for(; i < days; i++){
16975 intDay = i - startingPos + 1;
16976 textEls[i].innerHTML = (intDay);
16977 d.setDate(d.getDate()+1);
16979 cells[i].className = ''; // "x-date-active";
16980 setCellClass(this, cells[i]);
16984 for(; i < 42; i++) {
16985 textEls[i].innerHTML = (++extraDays);
16986 d.setDate(d.getDate()+1);
16988 cells[i].className = "fc-future fc-other-month";
16989 setCellClass(this, cells[i]);
16992 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16994 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16996 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16997 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16999 if(totalRows != 6){
17000 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17001 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17004 this.fireEvent('monthchange', this, date);
17008 if(!this.internalRender){
17009 var main = this.el.dom.firstChild;
17010 var w = main.offsetWidth;
17011 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17012 Roo.fly(main).setWidth(w);
17013 this.internalRender = true;
17014 // opera does not respect the auto grow header center column
17015 // then, after it gets a width opera refuses to recalculate
17016 // without a second pass
17017 if(Roo.isOpera && !this.secondPass){
17018 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17019 this.secondPass = true;
17020 this.update.defer(10, this, [date]);
17027 findCell : function(dt) {
17028 dt = dt.clearTime().getTime();
17030 this.cells.each(function(c){
17031 //Roo.log("check " +c.dateValue + '?=' + dt);
17032 if(c.dateValue == dt){
17042 findCells : function(ev) {
17043 var s = ev.start.clone().clearTime().getTime();
17045 var e= ev.end.clone().clearTime().getTime();
17048 this.cells.each(function(c){
17049 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17051 if(c.dateValue > e){
17054 if(c.dateValue < s){
17063 // findBestRow: function(cells)
17067 // for (var i =0 ; i < cells.length;i++) {
17068 // ret = Math.max(cells[i].rows || 0,ret);
17075 addItem : function(ev)
17077 // look for vertical location slot in
17078 var cells = this.findCells(ev);
17080 // ev.row = this.findBestRow(cells);
17082 // work out the location.
17086 for(var i =0; i < cells.length; i++) {
17088 cells[i].row = cells[0].row;
17091 cells[i].row = cells[i].row + 1;
17101 if (crow.start.getY() == cells[i].getY()) {
17103 crow.end = cells[i];
17120 cells[0].events.push(ev);
17122 this.calevents.push(ev);
17125 clearEvents: function() {
17127 if(!this.calevents){
17131 Roo.each(this.cells.elements, function(c){
17137 Roo.each(this.calevents, function(e) {
17138 Roo.each(e.els, function(el) {
17139 el.un('mouseenter' ,this.onEventEnter, this);
17140 el.un('mouseleave' ,this.onEventLeave, this);
17145 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17151 renderEvents: function()
17155 this.cells.each(function(c) {
17164 if(c.row != c.events.length){
17165 r = 4 - (4 - (c.row - c.events.length));
17168 c.events = ev.slice(0, r);
17169 c.more = ev.slice(r);
17171 if(c.more.length && c.more.length == 1){
17172 c.events.push(c.more.pop());
17175 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17179 this.cells.each(function(c) {
17181 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17184 for (var e = 0; e < c.events.length; e++){
17185 var ev = c.events[e];
17186 var rows = ev.rows;
17188 for(var i = 0; i < rows.length; i++) {
17190 // how many rows should it span..
17193 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17194 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17196 unselectable : "on",
17199 cls: 'fc-event-inner',
17203 // cls: 'fc-event-time',
17204 // html : cells.length > 1 ? '' : ev.time
17208 cls: 'fc-event-title',
17209 html : String.format('{0}', ev.title)
17216 cls: 'ui-resizable-handle ui-resizable-e',
17217 html : '  '
17224 cfg.cls += ' fc-event-start';
17226 if ((i+1) == rows.length) {
17227 cfg.cls += ' fc-event-end';
17230 var ctr = _this.el.select('.fc-event-container',true).first();
17231 var cg = ctr.createChild(cfg);
17233 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17234 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17236 var r = (c.more.length) ? 1 : 0;
17237 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17238 cg.setWidth(ebox.right - sbox.x -2);
17240 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17241 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17242 cg.on('click', _this.onEventClick, _this, ev);
17253 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17254 style : 'position: absolute',
17255 unselectable : "on",
17258 cls: 'fc-event-inner',
17262 cls: 'fc-event-title',
17270 cls: 'ui-resizable-handle ui-resizable-e',
17271 html : '  '
17277 var ctr = _this.el.select('.fc-event-container',true).first();
17278 var cg = ctr.createChild(cfg);
17280 var sbox = c.select('.fc-day-content',true).first().getBox();
17281 var ebox = c.select('.fc-day-content',true).first().getBox();
17283 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17284 cg.setWidth(ebox.right - sbox.x -2);
17286 cg.on('click', _this.onMoreEventClick, _this, c.more);
17296 onEventEnter: function (e, el,event,d) {
17297 this.fireEvent('evententer', this, el, event);
17300 onEventLeave: function (e, el,event,d) {
17301 this.fireEvent('eventleave', this, el, event);
17304 onEventClick: function (e, el,event,d) {
17305 this.fireEvent('eventclick', this, el, event);
17308 onMonthChange: function () {
17312 onMoreEventClick: function(e, el, more)
17316 this.calpopover.placement = 'right';
17317 this.calpopover.setTitle('More');
17319 this.calpopover.setContent('');
17321 var ctr = this.calpopover.el.select('.popover-content', true).first();
17323 Roo.each(more, function(m){
17325 cls : 'fc-event-hori fc-event-draggable',
17328 var cg = ctr.createChild(cfg);
17330 cg.on('click', _this.onEventClick, _this, m);
17333 this.calpopover.show(el);
17338 onLoad: function ()
17340 this.calevents = [];
17343 if(this.store.getCount() > 0){
17344 this.store.data.each(function(d){
17347 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17348 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17349 time : d.data.start_time,
17350 title : d.data.title,
17351 description : d.data.description,
17352 venue : d.data.venue
17357 this.renderEvents();
17359 if(this.calevents.length && this.loadMask){
17360 this.maskEl.hide();
17364 onBeforeLoad: function()
17366 this.clearEvents();
17368 this.maskEl.show();
17382 * @class Roo.bootstrap.Popover
17383 * @extends Roo.bootstrap.Component
17384 * Bootstrap Popover class
17385 * @cfg {String} html contents of the popover (or false to use children..)
17386 * @cfg {String} title of popover (or false to hide)
17387 * @cfg {String} placement how it is placed
17388 * @cfg {String} trigger click || hover (or false to trigger manually)
17389 * @cfg {String} over what (parent or false to trigger manually.)
17390 * @cfg {Number} delay - delay before showing
17393 * Create a new Popover
17394 * @param {Object} config The config object
17397 Roo.bootstrap.Popover = function(config){
17398 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17404 * After the popover show
17406 * @param {Roo.bootstrap.Popover} this
17411 * After the popover hide
17413 * @param {Roo.bootstrap.Popover} this
17419 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17421 title: 'Fill in a title',
17424 placement : 'right',
17425 trigger : 'hover', // hover
17431 can_build_overlaid : false,
17433 getChildContainer : function()
17435 return this.el.select('.popover-content',true).first();
17438 getAutoCreate : function(){
17441 cls : 'popover roo-dynamic',
17442 style: 'display:block',
17448 cls : 'popover-inner',
17452 cls: 'popover-title',
17456 cls : 'popover-content',
17467 setTitle: function(str)
17470 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17472 setContent: function(str)
17475 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17477 // as it get's added to the bottom of the page.
17478 onRender : function(ct, position)
17480 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17482 var cfg = Roo.apply({}, this.getAutoCreate());
17486 cfg.cls += ' ' + this.cls;
17489 cfg.style = this.style;
17491 //Roo.log("adding to ");
17492 this.el = Roo.get(document.body).createChild(cfg, position);
17493 // Roo.log(this.el);
17498 initEvents : function()
17500 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17501 this.el.enableDisplayMode('block');
17503 if (this.over === false) {
17506 if (this.triggers === false) {
17509 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17510 var triggers = this.trigger ? this.trigger.split(' ') : [];
17511 Roo.each(triggers, function(trigger) {
17513 if (trigger == 'click') {
17514 on_el.on('click', this.toggle, this);
17515 } else if (trigger != 'manual') {
17516 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17517 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17519 on_el.on(eventIn ,this.enter, this);
17520 on_el.on(eventOut, this.leave, this);
17531 toggle : function () {
17532 this.hoverState == 'in' ? this.leave() : this.enter();
17535 enter : function () {
17537 clearTimeout(this.timeout);
17539 this.hoverState = 'in';
17541 if (!this.delay || !this.delay.show) {
17546 this.timeout = setTimeout(function () {
17547 if (_t.hoverState == 'in') {
17550 }, this.delay.show)
17553 leave : function() {
17554 clearTimeout(this.timeout);
17556 this.hoverState = 'out';
17558 if (!this.delay || !this.delay.hide) {
17563 this.timeout = setTimeout(function () {
17564 if (_t.hoverState == 'out') {
17567 }, this.delay.hide)
17570 show : function (on_el)
17573 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17577 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17578 if (this.html !== false) {
17579 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17581 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17582 if (!this.title.length) {
17583 this.el.select('.popover-title',true).hide();
17586 var placement = typeof this.placement == 'function' ?
17587 this.placement.call(this, this.el, on_el) :
17590 var autoToken = /\s?auto?\s?/i;
17591 var autoPlace = autoToken.test(placement);
17593 placement = placement.replace(autoToken, '') || 'top';
17597 //this.el.setXY([0,0]);
17599 this.el.dom.style.display='block';
17600 this.el.addClass(placement);
17602 //this.el.appendTo(on_el);
17604 var p = this.getPosition();
17605 var box = this.el.getBox();
17610 var align = Roo.bootstrap.Popover.alignment[placement];
17613 this.el.alignTo(on_el, align[0],align[1]);
17614 //var arrow = this.el.select('.arrow',true).first();
17615 //arrow.set(align[2],
17617 this.el.addClass('in');
17620 if (this.el.hasClass('fade')) {
17624 this.hoverState = 'in';
17626 this.fireEvent('show', this);
17631 this.el.setXY([0,0]);
17632 this.el.removeClass('in');
17634 this.hoverState = null;
17636 this.fireEvent('hide', this);
17641 Roo.bootstrap.Popover.alignment = {
17642 'left' : ['r-l', [-10,0], 'right'],
17643 'right' : ['l-r', [10,0], 'left'],
17644 'bottom' : ['t-b', [0,10], 'top'],
17645 'top' : [ 'b-t', [0,-10], 'bottom']
17656 * @class Roo.bootstrap.Progress
17657 * @extends Roo.bootstrap.Component
17658 * Bootstrap Progress class
17659 * @cfg {Boolean} striped striped of the progress bar
17660 * @cfg {Boolean} active animated of the progress bar
17664 * Create a new Progress
17665 * @param {Object} config The config object
17668 Roo.bootstrap.Progress = function(config){
17669 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17672 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17677 getAutoCreate : function(){
17685 cfg.cls += ' progress-striped';
17689 cfg.cls += ' active';
17708 * @class Roo.bootstrap.ProgressBar
17709 * @extends Roo.bootstrap.Component
17710 * Bootstrap ProgressBar class
17711 * @cfg {Number} aria_valuenow aria-value now
17712 * @cfg {Number} aria_valuemin aria-value min
17713 * @cfg {Number} aria_valuemax aria-value max
17714 * @cfg {String} label label for the progress bar
17715 * @cfg {String} panel (success | info | warning | danger )
17716 * @cfg {String} role role of the progress bar
17717 * @cfg {String} sr_only text
17721 * Create a new ProgressBar
17722 * @param {Object} config The config object
17725 Roo.bootstrap.ProgressBar = function(config){
17726 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17729 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17733 aria_valuemax : 100,
17739 getAutoCreate : function()
17744 cls: 'progress-bar',
17745 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17757 cfg.role = this.role;
17760 if(this.aria_valuenow){
17761 cfg['aria-valuenow'] = this.aria_valuenow;
17764 if(this.aria_valuemin){
17765 cfg['aria-valuemin'] = this.aria_valuemin;
17768 if(this.aria_valuemax){
17769 cfg['aria-valuemax'] = this.aria_valuemax;
17772 if(this.label && !this.sr_only){
17773 cfg.html = this.label;
17777 cfg.cls += ' progress-bar-' + this.panel;
17783 update : function(aria_valuenow)
17785 this.aria_valuenow = aria_valuenow;
17787 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17802 * @class Roo.bootstrap.TabGroup
17803 * @extends Roo.bootstrap.Column
17804 * Bootstrap Column class
17805 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17806 * @cfg {Boolean} carousel true to make the group behave like a carousel
17807 * @cfg {Boolean} bullets show bullets for the panels
17808 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17809 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17810 * @cfg {Boolean} showarrow (true|false) show arrow default true
17813 * Create a new TabGroup
17814 * @param {Object} config The config object
17817 Roo.bootstrap.TabGroup = function(config){
17818 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17820 this.navId = Roo.id();
17823 Roo.bootstrap.TabGroup.register(this);
17827 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17830 transition : false,
17835 slideOnTouch : false,
17838 getAutoCreate : function()
17840 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17842 cfg.cls += ' tab-content';
17844 if (this.carousel) {
17845 cfg.cls += ' carousel slide';
17848 cls : 'carousel-inner',
17852 if(this.bullets && !Roo.isTouch){
17855 cls : 'carousel-bullets',
17859 if(this.bullets_cls){
17860 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17867 cfg.cn[0].cn.push(bullets);
17870 if(this.showarrow){
17871 cfg.cn[0].cn.push({
17873 class : 'carousel-arrow',
17877 class : 'carousel-prev',
17881 class : 'fa fa-chevron-left'
17887 class : 'carousel-next',
17891 class : 'fa fa-chevron-right'
17904 initEvents: function()
17906 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17907 // this.el.on("touchstart", this.onTouchStart, this);
17910 if(this.autoslide){
17913 this.slideFn = window.setInterval(function() {
17914 _this.showPanelNext();
17918 if(this.showarrow){
17919 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17920 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17926 // onTouchStart : function(e, el, o)
17928 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17932 // this.showPanelNext();
17936 getChildContainer : function()
17938 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17942 * register a Navigation item
17943 * @param {Roo.bootstrap.NavItem} the navitem to add
17945 register : function(item)
17947 this.tabs.push( item);
17948 item.navId = this.navId; // not really needed..
17953 getActivePanel : function()
17956 Roo.each(this.tabs, function(t) {
17966 getPanelByName : function(n)
17969 Roo.each(this.tabs, function(t) {
17970 if (t.tabId == n) {
17978 indexOfPanel : function(p)
17981 Roo.each(this.tabs, function(t,i) {
17982 if (t.tabId == p.tabId) {
17991 * show a specific panel
17992 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17993 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17995 showPanel : function (pan)
17997 if(this.transition || typeof(pan) == 'undefined'){
17998 Roo.log("waiting for the transitionend");
18002 if (typeof(pan) == 'number') {
18003 pan = this.tabs[pan];
18006 if (typeof(pan) == 'string') {
18007 pan = this.getPanelByName(pan);
18010 var cur = this.getActivePanel();
18013 Roo.log('pan or acitve pan is undefined');
18017 if (pan.tabId == this.getActivePanel().tabId) {
18021 if (false === cur.fireEvent('beforedeactivate')) {
18025 if(this.bullets > 0 && !Roo.isTouch){
18026 this.setActiveBullet(this.indexOfPanel(pan));
18029 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18031 this.transition = true;
18032 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18033 var lr = dir == 'next' ? 'left' : 'right';
18034 pan.el.addClass(dir); // or prev
18035 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18036 cur.el.addClass(lr); // or right
18037 pan.el.addClass(lr);
18040 cur.el.on('transitionend', function() {
18041 Roo.log("trans end?");
18043 pan.el.removeClass([lr,dir]);
18044 pan.setActive(true);
18046 cur.el.removeClass([lr]);
18047 cur.setActive(false);
18049 _this.transition = false;
18051 }, this, { single: true } );
18056 cur.setActive(false);
18057 pan.setActive(true);
18062 showPanelNext : function()
18064 var i = this.indexOfPanel(this.getActivePanel());
18066 if (i >= this.tabs.length - 1 && !this.autoslide) {
18070 if (i >= this.tabs.length - 1 && this.autoslide) {
18074 this.showPanel(this.tabs[i+1]);
18077 showPanelPrev : function()
18079 var i = this.indexOfPanel(this.getActivePanel());
18081 if (i < 1 && !this.autoslide) {
18085 if (i < 1 && this.autoslide) {
18086 i = this.tabs.length;
18089 this.showPanel(this.tabs[i-1]);
18093 addBullet: function()
18095 if(!this.bullets || Roo.isTouch){
18098 var ctr = this.el.select('.carousel-bullets',true).first();
18099 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18100 var bullet = ctr.createChild({
18101 cls : 'bullet bullet-' + i
18102 },ctr.dom.lastChild);
18107 bullet.on('click', (function(e, el, o, ii, t){
18109 e.preventDefault();
18111 this.showPanel(ii);
18113 if(this.autoslide && this.slideFn){
18114 clearInterval(this.slideFn);
18115 this.slideFn = window.setInterval(function() {
18116 _this.showPanelNext();
18120 }).createDelegate(this, [i, bullet], true));
18125 setActiveBullet : function(i)
18131 Roo.each(this.el.select('.bullet', true).elements, function(el){
18132 el.removeClass('selected');
18135 var bullet = this.el.select('.bullet-' + i, true).first();
18141 bullet.addClass('selected');
18152 Roo.apply(Roo.bootstrap.TabGroup, {
18156 * register a Navigation Group
18157 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18159 register : function(navgrp)
18161 this.groups[navgrp.navId] = navgrp;
18165 * fetch a Navigation Group based on the navigation ID
18166 * if one does not exist , it will get created.
18167 * @param {string} the navgroup to add
18168 * @returns {Roo.bootstrap.NavGroup} the navgroup
18170 get: function(navId) {
18171 if (typeof(this.groups[navId]) == 'undefined') {
18172 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18174 return this.groups[navId] ;
18189 * @class Roo.bootstrap.TabPanel
18190 * @extends Roo.bootstrap.Component
18191 * Bootstrap TabPanel class
18192 * @cfg {Boolean} active panel active
18193 * @cfg {String} html panel content
18194 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18195 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18196 * @cfg {String} href click to link..
18200 * Create a new TabPanel
18201 * @param {Object} config The config object
18204 Roo.bootstrap.TabPanel = function(config){
18205 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18209 * Fires when the active status changes
18210 * @param {Roo.bootstrap.TabPanel} this
18211 * @param {Boolean} state the new state
18216 * @event beforedeactivate
18217 * Fires before a tab is de-activated - can be used to do validation on a form.
18218 * @param {Roo.bootstrap.TabPanel} this
18219 * @return {Boolean} false if there is an error
18222 'beforedeactivate': true
18225 this.tabId = this.tabId || Roo.id();
18229 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18237 getAutoCreate : function(){
18240 // item is needed for carousel - not sure if it has any effect otherwise
18241 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18242 html: this.html || ''
18246 cfg.cls += ' active';
18250 cfg.tabId = this.tabId;
18257 initEvents: function()
18259 var p = this.parent();
18261 this.navId = this.navId || p.navId;
18263 if (typeof(this.navId) != 'undefined') {
18264 // not really needed.. but just in case.. parent should be a NavGroup.
18265 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18269 var i = tg.tabs.length - 1;
18271 if(this.active && tg.bullets > 0 && i < tg.bullets){
18272 tg.setActiveBullet(i);
18276 this.el.on('click', this.onClick, this);
18279 this.el.on("touchstart", this.onTouchStart, this);
18280 this.el.on("touchmove", this.onTouchMove, this);
18281 this.el.on("touchend", this.onTouchEnd, this);
18286 onRender : function(ct, position)
18288 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18291 setActive : function(state)
18293 Roo.log("panel - set active " + this.tabId + "=" + state);
18295 this.active = state;
18297 this.el.removeClass('active');
18299 } else if (!this.el.hasClass('active')) {
18300 this.el.addClass('active');
18303 this.fireEvent('changed', this, state);
18306 onClick : function(e)
18308 e.preventDefault();
18310 if(!this.href.length){
18314 window.location.href = this.href;
18323 onTouchStart : function(e)
18325 this.swiping = false;
18327 this.startX = e.browserEvent.touches[0].clientX;
18328 this.startY = e.browserEvent.touches[0].clientY;
18331 onTouchMove : function(e)
18333 this.swiping = true;
18335 this.endX = e.browserEvent.touches[0].clientX;
18336 this.endY = e.browserEvent.touches[0].clientY;
18339 onTouchEnd : function(e)
18346 var tabGroup = this.parent();
18348 if(this.endX > this.startX){ // swiping right
18349 tabGroup.showPanelPrev();
18353 if(this.startX > this.endX){ // swiping left
18354 tabGroup.showPanelNext();
18373 * @class Roo.bootstrap.DateField
18374 * @extends Roo.bootstrap.Input
18375 * Bootstrap DateField class
18376 * @cfg {Number} weekStart default 0
18377 * @cfg {String} viewMode default empty, (months|years)
18378 * @cfg {String} minViewMode default empty, (months|years)
18379 * @cfg {Number} startDate default -Infinity
18380 * @cfg {Number} endDate default Infinity
18381 * @cfg {Boolean} todayHighlight default false
18382 * @cfg {Boolean} todayBtn default false
18383 * @cfg {Boolean} calendarWeeks default false
18384 * @cfg {Object} daysOfWeekDisabled default empty
18385 * @cfg {Boolean} singleMode default false (true | false)
18387 * @cfg {Boolean} keyboardNavigation default true
18388 * @cfg {String} language default en
18391 * Create a new DateField
18392 * @param {Object} config The config object
18395 Roo.bootstrap.DateField = function(config){
18396 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18400 * Fires when this field show.
18401 * @param {Roo.bootstrap.DateField} this
18402 * @param {Mixed} date The date value
18407 * Fires when this field hide.
18408 * @param {Roo.bootstrap.DateField} this
18409 * @param {Mixed} date The date value
18414 * Fires when select a date.
18415 * @param {Roo.bootstrap.DateField} this
18416 * @param {Mixed} date The date value
18420 * @event beforeselect
18421 * Fires when before select a date.
18422 * @param {Roo.bootstrap.DateField} this
18423 * @param {Mixed} date The date value
18425 beforeselect : true
18429 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18432 * @cfg {String} format
18433 * The default date format string which can be overriden for localization support. The format must be
18434 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18438 * @cfg {String} altFormats
18439 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18440 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18442 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18450 todayHighlight : false,
18456 keyboardNavigation: true,
18458 calendarWeeks: false,
18460 startDate: -Infinity,
18464 daysOfWeekDisabled: [],
18468 singleMode : false,
18470 UTCDate: function()
18472 return new Date(Date.UTC.apply(Date, arguments));
18475 UTCToday: function()
18477 var today = new Date();
18478 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18481 getDate: function() {
18482 var d = this.getUTCDate();
18483 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18486 getUTCDate: function() {
18490 setDate: function(d) {
18491 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18494 setUTCDate: function(d) {
18496 this.setValue(this.formatDate(this.date));
18499 onRender: function(ct, position)
18502 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18504 this.language = this.language || 'en';
18505 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18506 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18508 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18509 this.format = this.format || 'm/d/y';
18510 this.isInline = false;
18511 this.isInput = true;
18512 this.component = this.el.select('.add-on', true).first() || false;
18513 this.component = (this.component && this.component.length === 0) ? false : this.component;
18514 this.hasInput = this.component && this.inputEl().length;
18516 if (typeof(this.minViewMode === 'string')) {
18517 switch (this.minViewMode) {
18519 this.minViewMode = 1;
18522 this.minViewMode = 2;
18525 this.minViewMode = 0;
18530 if (typeof(this.viewMode === 'string')) {
18531 switch (this.viewMode) {
18544 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18546 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18548 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18550 this.picker().on('mousedown', this.onMousedown, this);
18551 this.picker().on('click', this.onClick, this);
18553 this.picker().addClass('datepicker-dropdown');
18555 this.startViewMode = this.viewMode;
18557 if(this.singleMode){
18558 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18559 v.setVisibilityMode(Roo.Element.DISPLAY);
18563 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18564 v.setStyle('width', '189px');
18568 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18569 if(!this.calendarWeeks){
18574 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18575 v.attr('colspan', function(i, val){
18576 return parseInt(val) + 1;
18581 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18583 this.setStartDate(this.startDate);
18584 this.setEndDate(this.endDate);
18586 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18593 if(this.isInline) {
18598 picker : function()
18600 return this.pickerEl;
18601 // return this.el.select('.datepicker', true).first();
18604 fillDow: function()
18606 var dowCnt = this.weekStart;
18615 if(this.calendarWeeks){
18623 while (dowCnt < this.weekStart + 7) {
18627 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18631 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18634 fillMonths: function()
18637 var months = this.picker().select('>.datepicker-months td', true).first();
18639 months.dom.innerHTML = '';
18645 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18648 months.createChild(month);
18655 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;
18657 if (this.date < this.startDate) {
18658 this.viewDate = new Date(this.startDate);
18659 } else if (this.date > this.endDate) {
18660 this.viewDate = new Date(this.endDate);
18662 this.viewDate = new Date(this.date);
18670 var d = new Date(this.viewDate),
18671 year = d.getUTCFullYear(),
18672 month = d.getUTCMonth(),
18673 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18674 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18675 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18676 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18677 currentDate = this.date && this.date.valueOf(),
18678 today = this.UTCToday();
18680 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18682 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18684 // this.picker.select('>tfoot th.today').
18685 // .text(dates[this.language].today)
18686 // .toggle(this.todayBtn !== false);
18688 this.updateNavArrows();
18691 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18693 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18695 prevMonth.setUTCDate(day);
18697 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18699 var nextMonth = new Date(prevMonth);
18701 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18703 nextMonth = nextMonth.valueOf();
18705 var fillMonths = false;
18707 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18709 while(prevMonth.valueOf() <= nextMonth) {
18712 if (prevMonth.getUTCDay() === this.weekStart) {
18714 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18722 if(this.calendarWeeks){
18723 // ISO 8601: First week contains first thursday.
18724 // ISO also states week starts on Monday, but we can be more abstract here.
18726 // Start of current week: based on weekstart/current date
18727 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18728 // Thursday of this week
18729 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18730 // First Thursday of year, year from thursday
18731 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18732 // Calendar week: ms between thursdays, div ms per day, div 7 days
18733 calWeek = (th - yth) / 864e5 / 7 + 1;
18735 fillMonths.cn.push({
18743 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18745 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18748 if (this.todayHighlight &&
18749 prevMonth.getUTCFullYear() == today.getFullYear() &&
18750 prevMonth.getUTCMonth() == today.getMonth() &&
18751 prevMonth.getUTCDate() == today.getDate()) {
18752 clsName += ' today';
18755 if (currentDate && prevMonth.valueOf() === currentDate) {
18756 clsName += ' active';
18759 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18760 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18761 clsName += ' disabled';
18764 fillMonths.cn.push({
18766 cls: 'day ' + clsName,
18767 html: prevMonth.getDate()
18770 prevMonth.setDate(prevMonth.getDate()+1);
18773 var currentYear = this.date && this.date.getUTCFullYear();
18774 var currentMonth = this.date && this.date.getUTCMonth();
18776 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18778 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18779 v.removeClass('active');
18781 if(currentYear === year && k === currentMonth){
18782 v.addClass('active');
18785 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18786 v.addClass('disabled');
18792 year = parseInt(year/10, 10) * 10;
18794 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18796 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18799 for (var i = -1; i < 11; i++) {
18800 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18802 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18810 showMode: function(dir)
18813 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18816 Roo.each(this.picker().select('>div',true).elements, function(v){
18817 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18820 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18825 if(this.isInline) {
18829 this.picker().removeClass(['bottom', 'top']);
18831 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18833 * place to the top of element!
18837 this.picker().addClass('top');
18838 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18843 this.picker().addClass('bottom');
18845 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18848 parseDate : function(value)
18850 if(!value || value instanceof Date){
18853 var v = Date.parseDate(value, this.format);
18854 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18855 v = Date.parseDate(value, 'Y-m-d');
18857 if(!v && this.altFormats){
18858 if(!this.altFormatsArray){
18859 this.altFormatsArray = this.altFormats.split("|");
18861 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18862 v = Date.parseDate(value, this.altFormatsArray[i]);
18868 formatDate : function(date, fmt)
18870 return (!date || !(date instanceof Date)) ?
18871 date : date.dateFormat(fmt || this.format);
18874 onFocus : function()
18876 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18880 onBlur : function()
18882 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18884 var d = this.inputEl().getValue();
18891 showPopup : function()
18893 this.picker().show();
18897 this.fireEvent('showpopup', this, this.date);
18900 hidePopup : function()
18902 if(this.isInline) {
18905 this.picker().hide();
18906 this.viewMode = this.startViewMode;
18909 this.fireEvent('hidepopup', this, this.date);
18913 onMousedown: function(e)
18915 e.stopPropagation();
18916 e.preventDefault();
18921 Roo.bootstrap.DateField.superclass.keyup.call(this);
18925 setValue: function(v)
18927 if(this.fireEvent('beforeselect', this, v) !== false){
18928 var d = new Date(this.parseDate(v) ).clearTime();
18930 if(isNaN(d.getTime())){
18931 this.date = this.viewDate = '';
18932 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18936 v = this.formatDate(d);
18938 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18940 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18944 this.fireEvent('select', this, this.date);
18948 getValue: function()
18950 return this.formatDate(this.date);
18953 fireKey: function(e)
18955 if (!this.picker().isVisible()){
18956 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18962 var dateChanged = false,
18964 newDate, newViewDate;
18969 e.preventDefault();
18973 if (!this.keyboardNavigation) {
18976 dir = e.keyCode == 37 ? -1 : 1;
18979 newDate = this.moveYear(this.date, dir);
18980 newViewDate = this.moveYear(this.viewDate, dir);
18981 } else if (e.shiftKey){
18982 newDate = this.moveMonth(this.date, dir);
18983 newViewDate = this.moveMonth(this.viewDate, dir);
18985 newDate = new Date(this.date);
18986 newDate.setUTCDate(this.date.getUTCDate() + dir);
18987 newViewDate = new Date(this.viewDate);
18988 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18990 if (this.dateWithinRange(newDate)){
18991 this.date = newDate;
18992 this.viewDate = newViewDate;
18993 this.setValue(this.formatDate(this.date));
18995 e.preventDefault();
18996 dateChanged = true;
19001 if (!this.keyboardNavigation) {
19004 dir = e.keyCode == 38 ? -1 : 1;
19006 newDate = this.moveYear(this.date, dir);
19007 newViewDate = this.moveYear(this.viewDate, dir);
19008 } else if (e.shiftKey){
19009 newDate = this.moveMonth(this.date, dir);
19010 newViewDate = this.moveMonth(this.viewDate, dir);
19012 newDate = new Date(this.date);
19013 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19014 newViewDate = new Date(this.viewDate);
19015 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19017 if (this.dateWithinRange(newDate)){
19018 this.date = newDate;
19019 this.viewDate = newViewDate;
19020 this.setValue(this.formatDate(this.date));
19022 e.preventDefault();
19023 dateChanged = true;
19027 this.setValue(this.formatDate(this.date));
19029 e.preventDefault();
19032 this.setValue(this.formatDate(this.date));
19046 onClick: function(e)
19048 e.stopPropagation();
19049 e.preventDefault();
19051 var target = e.getTarget();
19053 if(target.nodeName.toLowerCase() === 'i'){
19054 target = Roo.get(target).dom.parentNode;
19057 var nodeName = target.nodeName;
19058 var className = target.className;
19059 var html = target.innerHTML;
19060 //Roo.log(nodeName);
19062 switch(nodeName.toLowerCase()) {
19064 switch(className) {
19070 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19071 switch(this.viewMode){
19073 this.viewDate = this.moveMonth(this.viewDate, dir);
19077 this.viewDate = this.moveYear(this.viewDate, dir);
19083 var date = new Date();
19084 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19086 this.setValue(this.formatDate(this.date));
19093 if (className.indexOf('disabled') < 0) {
19094 this.viewDate.setUTCDate(1);
19095 if (className.indexOf('month') > -1) {
19096 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19098 var year = parseInt(html, 10) || 0;
19099 this.viewDate.setUTCFullYear(year);
19103 if(this.singleMode){
19104 this.setValue(this.formatDate(this.viewDate));
19115 //Roo.log(className);
19116 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19117 var day = parseInt(html, 10) || 1;
19118 var year = this.viewDate.getUTCFullYear(),
19119 month = this.viewDate.getUTCMonth();
19121 if (className.indexOf('old') > -1) {
19128 } else if (className.indexOf('new') > -1) {
19136 //Roo.log([year,month,day]);
19137 this.date = this.UTCDate(year, month, day,0,0,0,0);
19138 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19140 //Roo.log(this.formatDate(this.date));
19141 this.setValue(this.formatDate(this.date));
19148 setStartDate: function(startDate)
19150 this.startDate = startDate || -Infinity;
19151 if (this.startDate !== -Infinity) {
19152 this.startDate = this.parseDate(this.startDate);
19155 this.updateNavArrows();
19158 setEndDate: function(endDate)
19160 this.endDate = endDate || Infinity;
19161 if (this.endDate !== Infinity) {
19162 this.endDate = this.parseDate(this.endDate);
19165 this.updateNavArrows();
19168 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19170 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19171 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19172 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19174 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19175 return parseInt(d, 10);
19178 this.updateNavArrows();
19181 updateNavArrows: function()
19183 if(this.singleMode){
19187 var d = new Date(this.viewDate),
19188 year = d.getUTCFullYear(),
19189 month = d.getUTCMonth();
19191 Roo.each(this.picker().select('.prev', true).elements, function(v){
19193 switch (this.viewMode) {
19196 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19202 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19209 Roo.each(this.picker().select('.next', true).elements, function(v){
19211 switch (this.viewMode) {
19214 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19220 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19228 moveMonth: function(date, dir)
19233 var new_date = new Date(date.valueOf()),
19234 day = new_date.getUTCDate(),
19235 month = new_date.getUTCMonth(),
19236 mag = Math.abs(dir),
19238 dir = dir > 0 ? 1 : -1;
19241 // If going back one month, make sure month is not current month
19242 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19244 return new_date.getUTCMonth() == month;
19246 // If going forward one month, make sure month is as expected
19247 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19249 return new_date.getUTCMonth() != new_month;
19251 new_month = month + dir;
19252 new_date.setUTCMonth(new_month);
19253 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19254 if (new_month < 0 || new_month > 11) {
19255 new_month = (new_month + 12) % 12;
19258 // For magnitudes >1, move one month at a time...
19259 for (var i=0; i<mag; i++) {
19260 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19261 new_date = this.moveMonth(new_date, dir);
19263 // ...then reset the day, keeping it in the new month
19264 new_month = new_date.getUTCMonth();
19265 new_date.setUTCDate(day);
19267 return new_month != new_date.getUTCMonth();
19270 // Common date-resetting loop -- if date is beyond end of month, make it
19273 new_date.setUTCDate(--day);
19274 new_date.setUTCMonth(new_month);
19279 moveYear: function(date, dir)
19281 return this.moveMonth(date, dir*12);
19284 dateWithinRange: function(date)
19286 return date >= this.startDate && date <= this.endDate;
19292 this.picker().remove();
19295 validateValue : function(value)
19297 if(this.getVisibilityEl().hasClass('hidden')){
19301 if(value.length < 1) {
19302 if(this.allowBlank){
19308 if(value.length < this.minLength){
19311 if(value.length > this.maxLength){
19315 var vt = Roo.form.VTypes;
19316 if(!vt[this.vtype](value, this)){
19320 if(typeof this.validator == "function"){
19321 var msg = this.validator(value);
19327 if(this.regex && !this.regex.test(value)){
19331 if(typeof(this.parseDate(value)) == 'undefined'){
19335 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19339 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19349 this.date = this.viewDate = '';
19351 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19356 Roo.apply(Roo.bootstrap.DateField, {
19367 html: '<i class="fa fa-arrow-left"/>'
19377 html: '<i class="fa fa-arrow-right"/>'
19419 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19420 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19421 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19422 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19423 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19436 navFnc: 'FullYear',
19441 navFnc: 'FullYear',
19446 Roo.apply(Roo.bootstrap.DateField, {
19450 cls: 'datepicker dropdown-menu roo-dynamic',
19454 cls: 'datepicker-days',
19458 cls: 'table-condensed',
19460 Roo.bootstrap.DateField.head,
19464 Roo.bootstrap.DateField.footer
19471 cls: 'datepicker-months',
19475 cls: 'table-condensed',
19477 Roo.bootstrap.DateField.head,
19478 Roo.bootstrap.DateField.content,
19479 Roo.bootstrap.DateField.footer
19486 cls: 'datepicker-years',
19490 cls: 'table-condensed',
19492 Roo.bootstrap.DateField.head,
19493 Roo.bootstrap.DateField.content,
19494 Roo.bootstrap.DateField.footer
19513 * @class Roo.bootstrap.TimeField
19514 * @extends Roo.bootstrap.Input
19515 * Bootstrap DateField class
19519 * Create a new TimeField
19520 * @param {Object} config The config object
19523 Roo.bootstrap.TimeField = function(config){
19524 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19528 * Fires when this field show.
19529 * @param {Roo.bootstrap.DateField} thisthis
19530 * @param {Mixed} date The date value
19535 * Fires when this field hide.
19536 * @param {Roo.bootstrap.DateField} this
19537 * @param {Mixed} date The date value
19542 * Fires when select a date.
19543 * @param {Roo.bootstrap.DateField} this
19544 * @param {Mixed} date The date value
19550 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19553 * @cfg {String} format
19554 * The default time format string which can be overriden for localization support. The format must be
19555 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19559 onRender: function(ct, position)
19562 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19564 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19566 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19568 this.pop = this.picker().select('>.datepicker-time',true).first();
19569 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19571 this.picker().on('mousedown', this.onMousedown, this);
19572 this.picker().on('click', this.onClick, this);
19574 this.picker().addClass('datepicker-dropdown');
19579 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19580 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19581 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19582 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19583 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19584 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19588 fireKey: function(e){
19589 if (!this.picker().isVisible()){
19590 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19596 e.preventDefault();
19604 this.onTogglePeriod();
19607 this.onIncrementMinutes();
19610 this.onDecrementMinutes();
19619 onClick: function(e) {
19620 e.stopPropagation();
19621 e.preventDefault();
19624 picker : function()
19626 return this.el.select('.datepicker', true).first();
19629 fillTime: function()
19631 var time = this.pop.select('tbody', true).first();
19633 time.dom.innerHTML = '';
19648 cls: 'hours-up glyphicon glyphicon-chevron-up'
19668 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19689 cls: 'timepicker-hour',
19704 cls: 'timepicker-minute',
19719 cls: 'btn btn-primary period',
19741 cls: 'hours-down glyphicon glyphicon-chevron-down'
19761 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19779 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19786 var hours = this.time.getHours();
19787 var minutes = this.time.getMinutes();
19800 hours = hours - 12;
19804 hours = '0' + hours;
19808 minutes = '0' + minutes;
19811 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19812 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19813 this.pop.select('button', true).first().dom.innerHTML = period;
19819 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19821 var cls = ['bottom'];
19823 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19830 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19835 this.picker().addClass(cls.join('-'));
19839 Roo.each(cls, function(c){
19841 _this.picker().setTop(_this.inputEl().getHeight());
19845 _this.picker().setTop(0 - _this.picker().getHeight());
19850 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19854 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19861 onFocus : function()
19863 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19867 onBlur : function()
19869 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19875 this.picker().show();
19880 this.fireEvent('show', this, this.date);
19885 this.picker().hide();
19888 this.fireEvent('hide', this, this.date);
19891 setTime : function()
19894 this.setValue(this.time.format(this.format));
19896 this.fireEvent('select', this, this.date);
19901 onMousedown: function(e){
19902 e.stopPropagation();
19903 e.preventDefault();
19906 onIncrementHours: function()
19908 Roo.log('onIncrementHours');
19909 this.time = this.time.add(Date.HOUR, 1);
19914 onDecrementHours: function()
19916 Roo.log('onDecrementHours');
19917 this.time = this.time.add(Date.HOUR, -1);
19921 onIncrementMinutes: function()
19923 Roo.log('onIncrementMinutes');
19924 this.time = this.time.add(Date.MINUTE, 1);
19928 onDecrementMinutes: function()
19930 Roo.log('onDecrementMinutes');
19931 this.time = this.time.add(Date.MINUTE, -1);
19935 onTogglePeriod: function()
19937 Roo.log('onTogglePeriod');
19938 this.time = this.time.add(Date.HOUR, 12);
19945 Roo.apply(Roo.bootstrap.TimeField, {
19975 cls: 'btn btn-info ok',
19987 Roo.apply(Roo.bootstrap.TimeField, {
19991 cls: 'datepicker dropdown-menu',
19995 cls: 'datepicker-time',
19999 cls: 'table-condensed',
20001 Roo.bootstrap.TimeField.content,
20002 Roo.bootstrap.TimeField.footer
20021 * @class Roo.bootstrap.MonthField
20022 * @extends Roo.bootstrap.Input
20023 * Bootstrap MonthField class
20025 * @cfg {String} language default en
20028 * Create a new MonthField
20029 * @param {Object} config The config object
20032 Roo.bootstrap.MonthField = function(config){
20033 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20038 * Fires when this field show.
20039 * @param {Roo.bootstrap.MonthField} this
20040 * @param {Mixed} date The date value
20045 * Fires when this field hide.
20046 * @param {Roo.bootstrap.MonthField} this
20047 * @param {Mixed} date The date value
20052 * Fires when select a date.
20053 * @param {Roo.bootstrap.MonthField} this
20054 * @param {String} oldvalue The old value
20055 * @param {String} newvalue The new value
20061 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20063 onRender: function(ct, position)
20066 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20068 this.language = this.language || 'en';
20069 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20070 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20072 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20073 this.isInline = false;
20074 this.isInput = true;
20075 this.component = this.el.select('.add-on', true).first() || false;
20076 this.component = (this.component && this.component.length === 0) ? false : this.component;
20077 this.hasInput = this.component && this.inputEL().length;
20079 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20081 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20083 this.picker().on('mousedown', this.onMousedown, this);
20084 this.picker().on('click', this.onClick, this);
20086 this.picker().addClass('datepicker-dropdown');
20088 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20089 v.setStyle('width', '189px');
20096 if(this.isInline) {
20102 setValue: function(v, suppressEvent)
20104 var o = this.getValue();
20106 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20110 if(suppressEvent !== true){
20111 this.fireEvent('select', this, o, v);
20116 getValue: function()
20121 onClick: function(e)
20123 e.stopPropagation();
20124 e.preventDefault();
20126 var target = e.getTarget();
20128 if(target.nodeName.toLowerCase() === 'i'){
20129 target = Roo.get(target).dom.parentNode;
20132 var nodeName = target.nodeName;
20133 var className = target.className;
20134 var html = target.innerHTML;
20136 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20140 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20142 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20148 picker : function()
20150 return this.pickerEl;
20153 fillMonths: function()
20156 var months = this.picker().select('>.datepicker-months td', true).first();
20158 months.dom.innerHTML = '';
20164 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20167 months.createChild(month);
20176 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20177 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20180 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20181 e.removeClass('active');
20183 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20184 e.addClass('active');
20191 if(this.isInline) {
20195 this.picker().removeClass(['bottom', 'top']);
20197 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20199 * place to the top of element!
20203 this.picker().addClass('top');
20204 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20209 this.picker().addClass('bottom');
20211 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20214 onFocus : function()
20216 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20220 onBlur : function()
20222 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20224 var d = this.inputEl().getValue();
20233 this.picker().show();
20234 this.picker().select('>.datepicker-months', true).first().show();
20238 this.fireEvent('show', this, this.date);
20243 if(this.isInline) {
20246 this.picker().hide();
20247 this.fireEvent('hide', this, this.date);
20251 onMousedown: function(e)
20253 e.stopPropagation();
20254 e.preventDefault();
20259 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20263 fireKey: function(e)
20265 if (!this.picker().isVisible()){
20266 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20277 e.preventDefault();
20281 dir = e.keyCode == 37 ? -1 : 1;
20283 this.vIndex = this.vIndex + dir;
20285 if(this.vIndex < 0){
20289 if(this.vIndex > 11){
20293 if(isNaN(this.vIndex)){
20297 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20303 dir = e.keyCode == 38 ? -1 : 1;
20305 this.vIndex = this.vIndex + dir * 4;
20307 if(this.vIndex < 0){
20311 if(this.vIndex > 11){
20315 if(isNaN(this.vIndex)){
20319 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20324 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20325 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20329 e.preventDefault();
20332 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20333 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20349 this.picker().remove();
20354 Roo.apply(Roo.bootstrap.MonthField, {
20373 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20374 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20379 Roo.apply(Roo.bootstrap.MonthField, {
20383 cls: 'datepicker dropdown-menu roo-dynamic',
20387 cls: 'datepicker-months',
20391 cls: 'table-condensed',
20393 Roo.bootstrap.DateField.content
20413 * @class Roo.bootstrap.CheckBox
20414 * @extends Roo.bootstrap.Input
20415 * Bootstrap CheckBox class
20417 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20418 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20419 * @cfg {String} boxLabel The text that appears beside the checkbox
20420 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20421 * @cfg {Boolean} checked initnal the element
20422 * @cfg {Boolean} inline inline the element (default false)
20423 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20424 * @cfg {String} tooltip label tooltip
20427 * Create a new CheckBox
20428 * @param {Object} config The config object
20431 Roo.bootstrap.CheckBox = function(config){
20432 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20437 * Fires when the element is checked or unchecked.
20438 * @param {Roo.bootstrap.CheckBox} this This input
20439 * @param {Boolean} checked The new checked value
20444 * Fires when the element is click.
20445 * @param {Roo.bootstrap.CheckBox} this This input
20452 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20454 inputType: 'checkbox',
20463 getAutoCreate : function()
20465 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20471 cfg.cls = 'form-group ' + this.inputType; //input-group
20474 cfg.cls += ' ' + this.inputType + '-inline';
20480 type : this.inputType,
20481 value : this.inputValue,
20482 cls : 'roo-' + this.inputType, //'form-box',
20483 placeholder : this.placeholder || ''
20487 if(this.inputType != 'radio'){
20491 cls : 'roo-hidden-value',
20492 value : this.checked ? this.inputValue : this.valueOff
20497 if (this.weight) { // Validity check?
20498 cfg.cls += " " + this.inputType + "-" + this.weight;
20501 if (this.disabled) {
20502 input.disabled=true;
20506 input.checked = this.checked;
20511 input.name = this.name;
20513 if(this.inputType != 'radio'){
20514 hidden.name = this.name;
20515 input.name = '_hidden_' + this.name;
20520 input.cls += ' input-' + this.size;
20525 ['xs','sm','md','lg'].map(function(size){
20526 if (settings[size]) {
20527 cfg.cls += ' col-' + size + '-' + settings[size];
20531 var inputblock = input;
20533 if (this.before || this.after) {
20536 cls : 'input-group',
20541 inputblock.cn.push({
20543 cls : 'input-group-addon',
20548 inputblock.cn.push(input);
20550 if(this.inputType != 'radio'){
20551 inputblock.cn.push(hidden);
20555 inputblock.cn.push({
20557 cls : 'input-group-addon',
20564 if (align ==='left' && this.fieldLabel.length) {
20565 // Roo.log("left and has label");
20570 cls : 'control-label',
20571 html : this.fieldLabel
20581 if(this.labelWidth > 12){
20582 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20585 if(this.labelWidth < 13 && this.labelmd == 0){
20586 this.labelmd = this.labelWidth;
20589 if(this.labellg > 0){
20590 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20591 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20594 if(this.labelmd > 0){
20595 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20596 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20599 if(this.labelsm > 0){
20600 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20601 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20604 if(this.labelxs > 0){
20605 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20606 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20609 } else if ( this.fieldLabel.length) {
20610 // Roo.log(" label");
20614 tag: this.boxLabel ? 'span' : 'label',
20616 cls: 'control-label box-input-label',
20617 //cls : 'input-group-addon',
20618 html : this.fieldLabel
20627 // Roo.log(" no label && no align");
20628 cfg.cn = [ inputblock ] ;
20634 var boxLabelCfg = {
20636 //'for': id, // box label is handled by onclick - so no for...
20638 html: this.boxLabel
20642 boxLabelCfg.tooltip = this.tooltip;
20645 cfg.cn.push(boxLabelCfg);
20648 if(this.inputType != 'radio'){
20649 cfg.cn.push(hidden);
20657 * return the real input element.
20659 inputEl: function ()
20661 return this.el.select('input.roo-' + this.inputType,true).first();
20663 hiddenEl: function ()
20665 return this.el.select('input.roo-hidden-value',true).first();
20668 labelEl: function()
20670 return this.el.select('label.control-label',true).first();
20672 /* depricated... */
20676 return this.labelEl();
20679 boxLabelEl: function()
20681 return this.el.select('label.box-label',true).first();
20684 initEvents : function()
20686 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20688 this.inputEl().on('click', this.onClick, this);
20690 if (this.boxLabel) {
20691 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20694 this.startValue = this.getValue();
20697 Roo.bootstrap.CheckBox.register(this);
20701 onClick : function(e)
20703 if(this.fireEvent('click', this, e) !== false){
20704 this.setChecked(!this.checked);
20709 setChecked : function(state,suppressEvent)
20711 this.startValue = this.getValue();
20713 if(this.inputType == 'radio'){
20715 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20716 e.dom.checked = false;
20719 this.inputEl().dom.checked = true;
20721 this.inputEl().dom.value = this.inputValue;
20723 if(suppressEvent !== true){
20724 this.fireEvent('check', this, true);
20732 this.checked = state;
20734 this.inputEl().dom.checked = state;
20737 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20739 if(suppressEvent !== true){
20740 this.fireEvent('check', this, state);
20746 getValue : function()
20748 if(this.inputType == 'radio'){
20749 return this.getGroupValue();
20752 return this.hiddenEl().dom.value;
20756 getGroupValue : function()
20758 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20762 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20765 setValue : function(v,suppressEvent)
20767 if(this.inputType == 'radio'){
20768 this.setGroupValue(v, suppressEvent);
20772 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20777 setGroupValue : function(v, suppressEvent)
20779 this.startValue = this.getValue();
20781 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20782 e.dom.checked = false;
20784 if(e.dom.value == v){
20785 e.dom.checked = true;
20789 if(suppressEvent !== true){
20790 this.fireEvent('check', this, true);
20798 validate : function()
20800 if(this.getVisibilityEl().hasClass('hidden')){
20806 (this.inputType == 'radio' && this.validateRadio()) ||
20807 (this.inputType == 'checkbox' && this.validateCheckbox())
20813 this.markInvalid();
20817 validateRadio : function()
20819 if(this.getVisibilityEl().hasClass('hidden')){
20823 if(this.allowBlank){
20829 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20830 if(!e.dom.checked){
20842 validateCheckbox : function()
20845 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20846 //return (this.getValue() == this.inputValue) ? true : false;
20849 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20857 for(var i in group){
20858 if(group[i].el.isVisible(true)){
20866 for(var i in group){
20871 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20878 * Mark this field as valid
20880 markValid : function()
20884 this.fireEvent('valid', this);
20886 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20889 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20896 if(this.inputType == 'radio'){
20897 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20898 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20899 e.findParent('.form-group', false, true).addClass(_this.validClass);
20906 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20907 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20911 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20917 for(var i in group){
20918 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20919 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20924 * Mark this field as invalid
20925 * @param {String} msg The validation message
20927 markInvalid : function(msg)
20929 if(this.allowBlank){
20935 this.fireEvent('invalid', this, msg);
20937 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20940 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20944 label.markInvalid();
20947 if(this.inputType == 'radio'){
20948 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20949 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20950 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20957 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20958 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20962 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20968 for(var i in group){
20969 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20970 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20975 clearInvalid : function()
20977 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20979 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20981 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20983 if (label && label.iconEl) {
20984 label.iconEl.removeClass(label.validClass);
20985 label.iconEl.removeClass(label.invalidClass);
20989 disable : function()
20991 if(this.inputType != 'radio'){
20992 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20999 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21000 _this.getActionEl().addClass(this.disabledClass);
21001 e.dom.disabled = true;
21005 this.disabled = true;
21006 this.fireEvent("disable", this);
21010 enable : function()
21012 if(this.inputType != 'radio'){
21013 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21020 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21021 _this.getActionEl().removeClass(this.disabledClass);
21022 e.dom.disabled = false;
21026 this.disabled = false;
21027 this.fireEvent("enable", this);
21031 setBoxLabel : function(v)
21036 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21042 Roo.apply(Roo.bootstrap.CheckBox, {
21047 * register a CheckBox Group
21048 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21050 register : function(checkbox)
21052 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21053 this.groups[checkbox.groupId] = {};
21056 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21060 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21064 * fetch a CheckBox Group based on the group ID
21065 * @param {string} the group ID
21066 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21068 get: function(groupId) {
21069 if (typeof(this.groups[groupId]) == 'undefined') {
21073 return this.groups[groupId] ;
21086 * @class Roo.bootstrap.Radio
21087 * @extends Roo.bootstrap.Component
21088 * Bootstrap Radio class
21089 * @cfg {String} boxLabel - the label associated
21090 * @cfg {String} value - the value of radio
21093 * Create a new Radio
21094 * @param {Object} config The config object
21096 Roo.bootstrap.Radio = function(config){
21097 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21101 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21107 getAutoCreate : function()
21111 cls : 'form-group radio',
21116 html : this.boxLabel
21124 initEvents : function()
21126 this.parent().register(this);
21128 this.el.on('click', this.onClick, this);
21132 onClick : function(e)
21134 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21135 this.setChecked(true);
21139 setChecked : function(state, suppressEvent)
21141 this.parent().setValue(this.value, suppressEvent);
21145 setBoxLabel : function(v)
21150 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21165 * @class Roo.bootstrap.SecurePass
21166 * @extends Roo.bootstrap.Input
21167 * Bootstrap SecurePass class
21171 * Create a new SecurePass
21172 * @param {Object} config The config object
21175 Roo.bootstrap.SecurePass = function (config) {
21176 // these go here, so the translation tool can replace them..
21178 PwdEmpty: "Please type a password, and then retype it to confirm.",
21179 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21180 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21181 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21182 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21183 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21184 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21185 TooWeak: "Your password is Too Weak."
21187 this.meterLabel = "Password strength:";
21188 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21189 this.meterClass = [
21190 "roo-password-meter-tooweak",
21191 "roo-password-meter-weak",
21192 "roo-password-meter-medium",
21193 "roo-password-meter-strong",
21194 "roo-password-meter-grey"
21199 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21202 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21204 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21206 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21207 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21208 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21209 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21210 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21211 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21212 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21222 * @cfg {String/Object} Label for the strength meter (defaults to
21223 * 'Password strength:')
21228 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21229 * ['Weak', 'Medium', 'Strong'])
21232 pwdStrengths: false,
21245 initEvents: function ()
21247 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21249 if (this.el.is('input[type=password]') && Roo.isSafari) {
21250 this.el.on('keydown', this.SafariOnKeyDown, this);
21253 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21256 onRender: function (ct, position)
21258 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21259 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21260 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21262 this.trigger.createChild({
21267 cls: 'roo-password-meter-grey col-xs-12',
21270 //width: this.meterWidth + 'px'
21274 cls: 'roo-password-meter-text'
21280 if (this.hideTrigger) {
21281 this.trigger.setDisplayed(false);
21283 this.setSize(this.width || '', this.height || '');
21286 onDestroy: function ()
21288 if (this.trigger) {
21289 this.trigger.removeAllListeners();
21290 this.trigger.remove();
21293 this.wrap.remove();
21295 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21298 checkStrength: function ()
21300 var pwd = this.inputEl().getValue();
21301 if (pwd == this._lastPwd) {
21306 if (this.ClientSideStrongPassword(pwd)) {
21308 } else if (this.ClientSideMediumPassword(pwd)) {
21310 } else if (this.ClientSideWeakPassword(pwd)) {
21316 Roo.log('strength1: ' + strength);
21318 //var pm = this.trigger.child('div/div/div').dom;
21319 var pm = this.trigger.child('div/div');
21320 pm.removeClass(this.meterClass);
21321 pm.addClass(this.meterClass[strength]);
21324 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21326 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21328 this._lastPwd = pwd;
21332 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21334 this._lastPwd = '';
21336 var pm = this.trigger.child('div/div');
21337 pm.removeClass(this.meterClass);
21338 pm.addClass('roo-password-meter-grey');
21341 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21344 this.inputEl().dom.type='password';
21347 validateValue: function (value)
21350 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21353 if (value.length == 0) {
21354 if (this.allowBlank) {
21355 this.clearInvalid();
21359 this.markInvalid(this.errors.PwdEmpty);
21360 this.errorMsg = this.errors.PwdEmpty;
21368 if ('[\x21-\x7e]*'.match(value)) {
21369 this.markInvalid(this.errors.PwdBadChar);
21370 this.errorMsg = this.errors.PwdBadChar;
21373 if (value.length < 6) {
21374 this.markInvalid(this.errors.PwdShort);
21375 this.errorMsg = this.errors.PwdShort;
21378 if (value.length > 16) {
21379 this.markInvalid(this.errors.PwdLong);
21380 this.errorMsg = this.errors.PwdLong;
21384 if (this.ClientSideStrongPassword(value)) {
21386 } else if (this.ClientSideMediumPassword(value)) {
21388 } else if (this.ClientSideWeakPassword(value)) {
21395 if (strength < 2) {
21396 //this.markInvalid(this.errors.TooWeak);
21397 this.errorMsg = this.errors.TooWeak;
21402 console.log('strength2: ' + strength);
21404 //var pm = this.trigger.child('div/div/div').dom;
21406 var pm = this.trigger.child('div/div');
21407 pm.removeClass(this.meterClass);
21408 pm.addClass(this.meterClass[strength]);
21410 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21412 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21414 this.errorMsg = '';
21418 CharacterSetChecks: function (type)
21421 this.fResult = false;
21424 isctype: function (character, type)
21427 case this.kCapitalLetter:
21428 if (character >= 'A' && character <= 'Z') {
21433 case this.kSmallLetter:
21434 if (character >= 'a' && character <= 'z') {
21440 if (character >= '0' && character <= '9') {
21445 case this.kPunctuation:
21446 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21457 IsLongEnough: function (pwd, size)
21459 return !(pwd == null || isNaN(size) || pwd.length < size);
21462 SpansEnoughCharacterSets: function (word, nb)
21464 if (!this.IsLongEnough(word, nb))
21469 var characterSetChecks = new Array(
21470 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21471 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21474 for (var index = 0; index < word.length; ++index) {
21475 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21476 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21477 characterSetChecks[nCharSet].fResult = true;
21484 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21485 if (characterSetChecks[nCharSet].fResult) {
21490 if (nCharSets < nb) {
21496 ClientSideStrongPassword: function (pwd)
21498 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21501 ClientSideMediumPassword: function (pwd)
21503 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21506 ClientSideWeakPassword: function (pwd)
21508 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21511 })//<script type="text/javascript">
21514 * Based Ext JS Library 1.1.1
21515 * Copyright(c) 2006-2007, Ext JS, LLC.
21521 * @class Roo.HtmlEditorCore
21522 * @extends Roo.Component
21523 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21525 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21528 Roo.HtmlEditorCore = function(config){
21531 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21536 * @event initialize
21537 * Fires when the editor is fully initialized (including the iframe)
21538 * @param {Roo.HtmlEditorCore} this
21543 * Fires when the editor is first receives the focus. Any insertion must wait
21544 * until after this event.
21545 * @param {Roo.HtmlEditorCore} this
21549 * @event beforesync
21550 * Fires before the textarea is updated with content from the editor iframe. Return false
21551 * to cancel the sync.
21552 * @param {Roo.HtmlEditorCore} this
21553 * @param {String} html
21557 * @event beforepush
21558 * Fires before the iframe editor is updated with content from the textarea. Return false
21559 * to cancel the push.
21560 * @param {Roo.HtmlEditorCore} this
21561 * @param {String} html
21566 * Fires when the textarea is updated with content from the editor iframe.
21567 * @param {Roo.HtmlEditorCore} this
21568 * @param {String} html
21573 * Fires when the iframe editor is updated with content from the textarea.
21574 * @param {Roo.HtmlEditorCore} this
21575 * @param {String} html
21580 * @event editorevent
21581 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21582 * @param {Roo.HtmlEditorCore} this
21588 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21590 // defaults : white / black...
21591 this.applyBlacklists();
21598 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21602 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21608 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21613 * @cfg {Number} height (in pixels)
21617 * @cfg {Number} width (in pixels)
21622 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21625 stylesheets: false,
21630 // private properties
21631 validationEvent : false,
21633 initialized : false,
21635 sourceEditMode : false,
21636 onFocus : Roo.emptyFn,
21638 hideMode:'offsets',
21642 // blacklist + whitelisted elements..
21649 * Protected method that will not generally be called directly. It
21650 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21651 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21653 getDocMarkup : function(){
21657 // inherit styels from page...??
21658 if (this.stylesheets === false) {
21660 Roo.get(document.head).select('style').each(function(node) {
21661 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21664 Roo.get(document.head).select('link').each(function(node) {
21665 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21668 } else if (!this.stylesheets.length) {
21670 st = '<style type="text/css">' +
21671 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21674 st = '<style type="text/css">' +
21679 st += '<style type="text/css">' +
21680 'IMG { cursor: pointer } ' +
21683 var cls = 'roo-htmleditor-body';
21685 if(this.bodyCls.length){
21686 cls += ' ' + this.bodyCls;
21689 return '<html><head>' + st +
21690 //<style type="text/css">' +
21691 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21693 ' </head><body class="' + cls + '"></body></html>';
21697 onRender : function(ct, position)
21700 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21701 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21704 this.el.dom.style.border = '0 none';
21705 this.el.dom.setAttribute('tabIndex', -1);
21706 this.el.addClass('x-hidden hide');
21710 if(Roo.isIE){ // fix IE 1px bogus margin
21711 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21715 this.frameId = Roo.id();
21719 var iframe = this.owner.wrap.createChild({
21721 cls: 'form-control', // bootstrap..
21723 name: this.frameId,
21724 frameBorder : 'no',
21725 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21730 this.iframe = iframe.dom;
21732 this.assignDocWin();
21734 this.doc.designMode = 'on';
21737 this.doc.write(this.getDocMarkup());
21741 var task = { // must defer to wait for browser to be ready
21743 //console.log("run task?" + this.doc.readyState);
21744 this.assignDocWin();
21745 if(this.doc.body || this.doc.readyState == 'complete'){
21747 this.doc.designMode="on";
21751 Roo.TaskMgr.stop(task);
21752 this.initEditor.defer(10, this);
21759 Roo.TaskMgr.start(task);
21764 onResize : function(w, h)
21766 Roo.log('resize: ' +w + ',' + h );
21767 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21771 if(typeof w == 'number'){
21773 this.iframe.style.width = w + 'px';
21775 if(typeof h == 'number'){
21777 this.iframe.style.height = h + 'px';
21779 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21786 * Toggles the editor between standard and source edit mode.
21787 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21789 toggleSourceEdit : function(sourceEditMode){
21791 this.sourceEditMode = sourceEditMode === true;
21793 if(this.sourceEditMode){
21795 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21798 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21799 //this.iframe.className = '';
21802 //this.setSize(this.owner.wrap.getSize());
21803 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21810 * Protected method that will not generally be called directly. If you need/want
21811 * custom HTML cleanup, this is the method you should override.
21812 * @param {String} html The HTML to be cleaned
21813 * return {String} The cleaned HTML
21815 cleanHtml : function(html){
21816 html = String(html);
21817 if(html.length > 5){
21818 if(Roo.isSafari){ // strip safari nonsense
21819 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21822 if(html == ' '){
21829 * HTML Editor -> Textarea
21830 * Protected method that will not generally be called directly. Syncs the contents
21831 * of the editor iframe with the textarea.
21833 syncValue : function(){
21834 if(this.initialized){
21835 var bd = (this.doc.body || this.doc.documentElement);
21836 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21837 var html = bd.innerHTML;
21839 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21840 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21842 html = '<div style="'+m[0]+'">' + html + '</div>';
21845 html = this.cleanHtml(html);
21846 // fix up the special chars.. normaly like back quotes in word...
21847 // however we do not want to do this with chinese..
21848 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21849 var cc = b.charCodeAt();
21851 (cc >= 0x4E00 && cc < 0xA000 ) ||
21852 (cc >= 0x3400 && cc < 0x4E00 ) ||
21853 (cc >= 0xf900 && cc < 0xfb00 )
21859 if(this.owner.fireEvent('beforesync', this, html) !== false){
21860 this.el.dom.value = html;
21861 this.owner.fireEvent('sync', this, html);
21867 * Protected method that will not generally be called directly. Pushes the value of the textarea
21868 * into the iframe editor.
21870 pushValue : function(){
21871 if(this.initialized){
21872 var v = this.el.dom.value.trim();
21874 // if(v.length < 1){
21878 if(this.owner.fireEvent('beforepush', this, v) !== false){
21879 var d = (this.doc.body || this.doc.documentElement);
21881 this.cleanUpPaste();
21882 this.el.dom.value = d.innerHTML;
21883 this.owner.fireEvent('push', this, v);
21889 deferFocus : function(){
21890 this.focus.defer(10, this);
21894 focus : function(){
21895 if(this.win && !this.sourceEditMode){
21902 assignDocWin: function()
21904 var iframe = this.iframe;
21907 this.doc = iframe.contentWindow.document;
21908 this.win = iframe.contentWindow;
21910 // if (!Roo.get(this.frameId)) {
21913 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21914 // this.win = Roo.get(this.frameId).dom.contentWindow;
21916 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21920 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21921 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21926 initEditor : function(){
21927 //console.log("INIT EDITOR");
21928 this.assignDocWin();
21932 this.doc.designMode="on";
21934 this.doc.write(this.getDocMarkup());
21937 var dbody = (this.doc.body || this.doc.documentElement);
21938 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21939 // this copies styles from the containing element into thsi one..
21940 // not sure why we need all of this..
21941 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21943 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21944 //ss['background-attachment'] = 'fixed'; // w3c
21945 dbody.bgProperties = 'fixed'; // ie
21946 //Roo.DomHelper.applyStyles(dbody, ss);
21947 Roo.EventManager.on(this.doc, {
21948 //'mousedown': this.onEditorEvent,
21949 'mouseup': this.onEditorEvent,
21950 'dblclick': this.onEditorEvent,
21951 'click': this.onEditorEvent,
21952 'keyup': this.onEditorEvent,
21957 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21959 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21960 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21962 this.initialized = true;
21964 this.owner.fireEvent('initialize', this);
21969 onDestroy : function(){
21975 //for (var i =0; i < this.toolbars.length;i++) {
21976 // // fixme - ask toolbars for heights?
21977 // this.toolbars[i].onDestroy();
21980 //this.wrap.dom.innerHTML = '';
21981 //this.wrap.remove();
21986 onFirstFocus : function(){
21988 this.assignDocWin();
21991 this.activated = true;
21994 if(Roo.isGecko){ // prevent silly gecko errors
21996 var s = this.win.getSelection();
21997 if(!s.focusNode || s.focusNode.nodeType != 3){
21998 var r = s.getRangeAt(0);
21999 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22004 this.execCmd('useCSS', true);
22005 this.execCmd('styleWithCSS', false);
22008 this.owner.fireEvent('activate', this);
22012 adjustFont: function(btn){
22013 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22014 //if(Roo.isSafari){ // safari
22017 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22018 if(Roo.isSafari){ // safari
22019 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22020 v = (v < 10) ? 10 : v;
22021 v = (v > 48) ? 48 : v;
22022 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22027 v = Math.max(1, v+adjust);
22029 this.execCmd('FontSize', v );
22032 onEditorEvent : function(e)
22034 this.owner.fireEvent('editorevent', this, e);
22035 // this.updateToolbar();
22036 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22039 insertTag : function(tg)
22041 // could be a bit smarter... -> wrap the current selected tRoo..
22042 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22044 range = this.createRange(this.getSelection());
22045 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22046 wrappingNode.appendChild(range.extractContents());
22047 range.insertNode(wrappingNode);
22054 this.execCmd("formatblock", tg);
22058 insertText : function(txt)
22062 var range = this.createRange();
22063 range.deleteContents();
22064 //alert(Sender.getAttribute('label'));
22066 range.insertNode(this.doc.createTextNode(txt));
22072 * Executes a Midas editor command on the editor document and performs necessary focus and
22073 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22074 * @param {String} cmd The Midas command
22075 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22077 relayCmd : function(cmd, value){
22079 this.execCmd(cmd, value);
22080 this.owner.fireEvent('editorevent', this);
22081 //this.updateToolbar();
22082 this.owner.deferFocus();
22086 * Executes a Midas editor command directly on the editor document.
22087 * For visual commands, you should use {@link #relayCmd} instead.
22088 * <b>This should only be called after the editor is initialized.</b>
22089 * @param {String} cmd The Midas command
22090 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22092 execCmd : function(cmd, value){
22093 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22100 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22102 * @param {String} text | dom node..
22104 insertAtCursor : function(text)
22107 if(!this.activated){
22113 var r = this.doc.selection.createRange();
22124 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22128 // from jquery ui (MIT licenced)
22130 var win = this.win;
22132 if (win.getSelection && win.getSelection().getRangeAt) {
22133 range = win.getSelection().getRangeAt(0);
22134 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22135 range.insertNode(node);
22136 } else if (win.document.selection && win.document.selection.createRange) {
22137 // no firefox support
22138 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22139 win.document.selection.createRange().pasteHTML(txt);
22141 // no firefox support
22142 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22143 this.execCmd('InsertHTML', txt);
22152 mozKeyPress : function(e){
22154 var c = e.getCharCode(), cmd;
22157 c = String.fromCharCode(c).toLowerCase();
22171 this.cleanUpPaste.defer(100, this);
22179 e.preventDefault();
22187 fixKeys : function(){ // load time branching for fastest keydown performance
22189 return function(e){
22190 var k = e.getKey(), r;
22193 r = this.doc.selection.createRange();
22196 r.pasteHTML('    ');
22203 r = this.doc.selection.createRange();
22205 var target = r.parentElement();
22206 if(!target || target.tagName.toLowerCase() != 'li'){
22208 r.pasteHTML('<br />');
22214 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22215 this.cleanUpPaste.defer(100, this);
22221 }else if(Roo.isOpera){
22222 return function(e){
22223 var k = e.getKey();
22227 this.execCmd('InsertHTML','    ');
22230 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22231 this.cleanUpPaste.defer(100, this);
22236 }else if(Roo.isSafari){
22237 return function(e){
22238 var k = e.getKey();
22242 this.execCmd('InsertText','\t');
22246 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22247 this.cleanUpPaste.defer(100, this);
22255 getAllAncestors: function()
22257 var p = this.getSelectedNode();
22260 a.push(p); // push blank onto stack..
22261 p = this.getParentElement();
22265 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22269 a.push(this.doc.body);
22273 lastSelNode : false,
22276 getSelection : function()
22278 this.assignDocWin();
22279 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22282 getSelectedNode: function()
22284 // this may only work on Gecko!!!
22286 // should we cache this!!!!
22291 var range = this.createRange(this.getSelection()).cloneRange();
22294 var parent = range.parentElement();
22296 var testRange = range.duplicate();
22297 testRange.moveToElementText(parent);
22298 if (testRange.inRange(range)) {
22301 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22304 parent = parent.parentElement;
22309 // is ancestor a text element.
22310 var ac = range.commonAncestorContainer;
22311 if (ac.nodeType == 3) {
22312 ac = ac.parentNode;
22315 var ar = ac.childNodes;
22318 var other_nodes = [];
22319 var has_other_nodes = false;
22320 for (var i=0;i<ar.length;i++) {
22321 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22324 // fullly contained node.
22326 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22331 // probably selected..
22332 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22333 other_nodes.push(ar[i]);
22337 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22342 has_other_nodes = true;
22344 if (!nodes.length && other_nodes.length) {
22345 nodes= other_nodes;
22347 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22353 createRange: function(sel)
22355 // this has strange effects when using with
22356 // top toolbar - not sure if it's a great idea.
22357 //this.editor.contentWindow.focus();
22358 if (typeof sel != "undefined") {
22360 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22362 return this.doc.createRange();
22365 return this.doc.createRange();
22368 getParentElement: function()
22371 this.assignDocWin();
22372 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22374 var range = this.createRange(sel);
22377 var p = range.commonAncestorContainer;
22378 while (p.nodeType == 3) { // text node
22389 * Range intersection.. the hard stuff...
22393 * [ -- selected range --- ]
22397 * if end is before start or hits it. fail.
22398 * if start is after end or hits it fail.
22400 * if either hits (but other is outside. - then it's not
22406 // @see http://www.thismuchiknow.co.uk/?p=64.
22407 rangeIntersectsNode : function(range, node)
22409 var nodeRange = node.ownerDocument.createRange();
22411 nodeRange.selectNode(node);
22413 nodeRange.selectNodeContents(node);
22416 var rangeStartRange = range.cloneRange();
22417 rangeStartRange.collapse(true);
22419 var rangeEndRange = range.cloneRange();
22420 rangeEndRange.collapse(false);
22422 var nodeStartRange = nodeRange.cloneRange();
22423 nodeStartRange.collapse(true);
22425 var nodeEndRange = nodeRange.cloneRange();
22426 nodeEndRange.collapse(false);
22428 return rangeStartRange.compareBoundaryPoints(
22429 Range.START_TO_START, nodeEndRange) == -1 &&
22430 rangeEndRange.compareBoundaryPoints(
22431 Range.START_TO_START, nodeStartRange) == 1;
22435 rangeCompareNode : function(range, node)
22437 var nodeRange = node.ownerDocument.createRange();
22439 nodeRange.selectNode(node);
22441 nodeRange.selectNodeContents(node);
22445 range.collapse(true);
22447 nodeRange.collapse(true);
22449 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22450 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22452 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22454 var nodeIsBefore = ss == 1;
22455 var nodeIsAfter = ee == -1;
22457 if (nodeIsBefore && nodeIsAfter) {
22460 if (!nodeIsBefore && nodeIsAfter) {
22461 return 1; //right trailed.
22464 if (nodeIsBefore && !nodeIsAfter) {
22465 return 2; // left trailed.
22471 // private? - in a new class?
22472 cleanUpPaste : function()
22474 // cleans up the whole document..
22475 Roo.log('cleanuppaste');
22477 this.cleanUpChildren(this.doc.body);
22478 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22479 if (clean != this.doc.body.innerHTML) {
22480 this.doc.body.innerHTML = clean;
22485 cleanWordChars : function(input) {// change the chars to hex code
22486 var he = Roo.HtmlEditorCore;
22488 var output = input;
22489 Roo.each(he.swapCodes, function(sw) {
22490 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22492 output = output.replace(swapper, sw[1]);
22499 cleanUpChildren : function (n)
22501 if (!n.childNodes.length) {
22504 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22505 this.cleanUpChild(n.childNodes[i]);
22512 cleanUpChild : function (node)
22515 //console.log(node);
22516 if (node.nodeName == "#text") {
22517 // clean up silly Windows -- stuff?
22520 if (node.nodeName == "#comment") {
22521 node.parentNode.removeChild(node);
22522 // clean up silly Windows -- stuff?
22525 var lcname = node.tagName.toLowerCase();
22526 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22527 // whitelist of tags..
22529 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22531 node.parentNode.removeChild(node);
22536 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22538 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22539 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22541 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22542 // remove_keep_children = true;
22545 if (remove_keep_children) {
22546 this.cleanUpChildren(node);
22547 // inserts everything just before this node...
22548 while (node.childNodes.length) {
22549 var cn = node.childNodes[0];
22550 node.removeChild(cn);
22551 node.parentNode.insertBefore(cn, node);
22553 node.parentNode.removeChild(node);
22557 if (!node.attributes || !node.attributes.length) {
22558 this.cleanUpChildren(node);
22562 function cleanAttr(n,v)
22565 if (v.match(/^\./) || v.match(/^\//)) {
22568 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22571 if (v.match(/^#/)) {
22574 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22575 node.removeAttribute(n);
22579 var cwhite = this.cwhite;
22580 var cblack = this.cblack;
22582 function cleanStyle(n,v)
22584 if (v.match(/expression/)) { //XSS?? should we even bother..
22585 node.removeAttribute(n);
22589 var parts = v.split(/;/);
22592 Roo.each(parts, function(p) {
22593 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22597 var l = p.split(':').shift().replace(/\s+/g,'');
22598 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22600 if ( cwhite.length && cblack.indexOf(l) > -1) {
22601 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22602 //node.removeAttribute(n);
22606 // only allow 'c whitelisted system attributes'
22607 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22608 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22609 //node.removeAttribute(n);
22619 if (clean.length) {
22620 node.setAttribute(n, clean.join(';'));
22622 node.removeAttribute(n);
22628 for (var i = node.attributes.length-1; i > -1 ; i--) {
22629 var a = node.attributes[i];
22632 if (a.name.toLowerCase().substr(0,2)=='on') {
22633 node.removeAttribute(a.name);
22636 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22637 node.removeAttribute(a.name);
22640 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22641 cleanAttr(a.name,a.value); // fixme..
22644 if (a.name == 'style') {
22645 cleanStyle(a.name,a.value);
22648 /// clean up MS crap..
22649 // tecnically this should be a list of valid class'es..
22652 if (a.name == 'class') {
22653 if (a.value.match(/^Mso/)) {
22654 node.className = '';
22657 if (a.value.match(/^body$/)) {
22658 node.className = '';
22669 this.cleanUpChildren(node);
22675 * Clean up MS wordisms...
22677 cleanWord : function(node)
22682 this.cleanWord(this.doc.body);
22685 if (node.nodeName == "#text") {
22686 // clean up silly Windows -- stuff?
22689 if (node.nodeName == "#comment") {
22690 node.parentNode.removeChild(node);
22691 // clean up silly Windows -- stuff?
22695 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22696 node.parentNode.removeChild(node);
22700 // remove - but keep children..
22701 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22702 while (node.childNodes.length) {
22703 var cn = node.childNodes[0];
22704 node.removeChild(cn);
22705 node.parentNode.insertBefore(cn, node);
22707 node.parentNode.removeChild(node);
22708 this.iterateChildren(node, this.cleanWord);
22712 if (node.className.length) {
22714 var cn = node.className.split(/\W+/);
22716 Roo.each(cn, function(cls) {
22717 if (cls.match(/Mso[a-zA-Z]+/)) {
22722 node.className = cna.length ? cna.join(' ') : '';
22724 node.removeAttribute("class");
22728 if (node.hasAttribute("lang")) {
22729 node.removeAttribute("lang");
22732 if (node.hasAttribute("style")) {
22734 var styles = node.getAttribute("style").split(";");
22736 Roo.each(styles, function(s) {
22737 if (!s.match(/:/)) {
22740 var kv = s.split(":");
22741 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22744 // what ever is left... we allow.
22747 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22748 if (!nstyle.length) {
22749 node.removeAttribute('style');
22752 this.iterateChildren(node, this.cleanWord);
22758 * iterateChildren of a Node, calling fn each time, using this as the scole..
22759 * @param {DomNode} node node to iterate children of.
22760 * @param {Function} fn method of this class to call on each item.
22762 iterateChildren : function(node, fn)
22764 if (!node.childNodes.length) {
22767 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22768 fn.call(this, node.childNodes[i])
22774 * cleanTableWidths.
22776 * Quite often pasting from word etc.. results in tables with column and widths.
22777 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22780 cleanTableWidths : function(node)
22785 this.cleanTableWidths(this.doc.body);
22790 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22793 Roo.log(node.tagName);
22794 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22795 this.iterateChildren(node, this.cleanTableWidths);
22798 if (node.hasAttribute('width')) {
22799 node.removeAttribute('width');
22803 if (node.hasAttribute("style")) {
22806 var styles = node.getAttribute("style").split(";");
22808 Roo.each(styles, function(s) {
22809 if (!s.match(/:/)) {
22812 var kv = s.split(":");
22813 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22816 // what ever is left... we allow.
22819 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22820 if (!nstyle.length) {
22821 node.removeAttribute('style');
22825 this.iterateChildren(node, this.cleanTableWidths);
22833 domToHTML : function(currentElement, depth, nopadtext) {
22835 depth = depth || 0;
22836 nopadtext = nopadtext || false;
22838 if (!currentElement) {
22839 return this.domToHTML(this.doc.body);
22842 //Roo.log(currentElement);
22844 var allText = false;
22845 var nodeName = currentElement.nodeName;
22846 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22848 if (nodeName == '#text') {
22850 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22855 if (nodeName != 'BODY') {
22858 // Prints the node tagName, such as <A>, <IMG>, etc
22861 for(i = 0; i < currentElement.attributes.length;i++) {
22863 var aname = currentElement.attributes.item(i).name;
22864 if (!currentElement.attributes.item(i).value.length) {
22867 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22870 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22879 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22882 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22887 // Traverse the tree
22889 var currentElementChild = currentElement.childNodes.item(i);
22890 var allText = true;
22891 var innerHTML = '';
22893 while (currentElementChild) {
22894 // Formatting code (indent the tree so it looks nice on the screen)
22895 var nopad = nopadtext;
22896 if (lastnode == 'SPAN') {
22900 if (currentElementChild.nodeName == '#text') {
22901 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22902 toadd = nopadtext ? toadd : toadd.trim();
22903 if (!nopad && toadd.length > 80) {
22904 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22906 innerHTML += toadd;
22909 currentElementChild = currentElement.childNodes.item(i);
22915 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22917 // Recursively traverse the tree structure of the child node
22918 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22919 lastnode = currentElementChild.nodeName;
22921 currentElementChild=currentElement.childNodes.item(i);
22927 // The remaining code is mostly for formatting the tree
22928 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22933 ret+= "</"+tagName+">";
22939 applyBlacklists : function()
22941 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22942 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22946 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22947 if (b.indexOf(tag) > -1) {
22950 this.white.push(tag);
22954 Roo.each(w, function(tag) {
22955 if (b.indexOf(tag) > -1) {
22958 if (this.white.indexOf(tag) > -1) {
22961 this.white.push(tag);
22966 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22967 if (w.indexOf(tag) > -1) {
22970 this.black.push(tag);
22974 Roo.each(b, function(tag) {
22975 if (w.indexOf(tag) > -1) {
22978 if (this.black.indexOf(tag) > -1) {
22981 this.black.push(tag);
22986 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22987 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22991 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22992 if (b.indexOf(tag) > -1) {
22995 this.cwhite.push(tag);
22999 Roo.each(w, function(tag) {
23000 if (b.indexOf(tag) > -1) {
23003 if (this.cwhite.indexOf(tag) > -1) {
23006 this.cwhite.push(tag);
23011 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23012 if (w.indexOf(tag) > -1) {
23015 this.cblack.push(tag);
23019 Roo.each(b, function(tag) {
23020 if (w.indexOf(tag) > -1) {
23023 if (this.cblack.indexOf(tag) > -1) {
23026 this.cblack.push(tag);
23031 setStylesheets : function(stylesheets)
23033 if(typeof(stylesheets) == 'string'){
23034 Roo.get(this.iframe.contentDocument.head).createChild({
23036 rel : 'stylesheet',
23045 Roo.each(stylesheets, function(s) {
23050 Roo.get(_this.iframe.contentDocument.head).createChild({
23052 rel : 'stylesheet',
23061 removeStylesheets : function()
23065 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23070 setStyle : function(style)
23072 Roo.get(this.iframe.contentDocument.head).createChild({
23081 // hide stuff that is not compatible
23095 * @event specialkey
23099 * @cfg {String} fieldClass @hide
23102 * @cfg {String} focusClass @hide
23105 * @cfg {String} autoCreate @hide
23108 * @cfg {String} inputType @hide
23111 * @cfg {String} invalidClass @hide
23114 * @cfg {String} invalidText @hide
23117 * @cfg {String} msgFx @hide
23120 * @cfg {String} validateOnBlur @hide
23124 Roo.HtmlEditorCore.white = [
23125 'area', 'br', 'img', 'input', 'hr', 'wbr',
23127 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23128 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23129 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23130 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23131 'table', 'ul', 'xmp',
23133 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23136 'dir', 'menu', 'ol', 'ul', 'dl',
23142 Roo.HtmlEditorCore.black = [
23143 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23145 'base', 'basefont', 'bgsound', 'blink', 'body',
23146 'frame', 'frameset', 'head', 'html', 'ilayer',
23147 'iframe', 'layer', 'link', 'meta', 'object',
23148 'script', 'style' ,'title', 'xml' // clean later..
23150 Roo.HtmlEditorCore.clean = [
23151 'script', 'style', 'title', 'xml'
23153 Roo.HtmlEditorCore.remove = [
23158 Roo.HtmlEditorCore.ablack = [
23162 Roo.HtmlEditorCore.aclean = [
23163 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23167 Roo.HtmlEditorCore.pwhite= [
23168 'http', 'https', 'mailto'
23171 // white listed style attributes.
23172 Roo.HtmlEditorCore.cwhite= [
23173 // 'text-align', /// default is to allow most things..
23179 // black listed style attributes.
23180 Roo.HtmlEditorCore.cblack= [
23181 // 'font-size' -- this can be set by the project
23185 Roo.HtmlEditorCore.swapCodes =[
23204 * @class Roo.bootstrap.HtmlEditor
23205 * @extends Roo.bootstrap.TextArea
23206 * Bootstrap HtmlEditor class
23209 * Create a new HtmlEditor
23210 * @param {Object} config The config object
23213 Roo.bootstrap.HtmlEditor = function(config){
23214 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23215 if (!this.toolbars) {
23216 this.toolbars = [];
23219 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23222 * @event initialize
23223 * Fires when the editor is fully initialized (including the iframe)
23224 * @param {HtmlEditor} this
23229 * Fires when the editor is first receives the focus. Any insertion must wait
23230 * until after this event.
23231 * @param {HtmlEditor} this
23235 * @event beforesync
23236 * Fires before the textarea is updated with content from the editor iframe. Return false
23237 * to cancel the sync.
23238 * @param {HtmlEditor} this
23239 * @param {String} html
23243 * @event beforepush
23244 * Fires before the iframe editor is updated with content from the textarea. Return false
23245 * to cancel the push.
23246 * @param {HtmlEditor} this
23247 * @param {String} html
23252 * Fires when the textarea is updated with content from the editor iframe.
23253 * @param {HtmlEditor} this
23254 * @param {String} html
23259 * Fires when the iframe editor is updated with content from the textarea.
23260 * @param {HtmlEditor} this
23261 * @param {String} html
23265 * @event editmodechange
23266 * Fires when the editor switches edit modes
23267 * @param {HtmlEditor} this
23268 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23270 editmodechange: true,
23272 * @event editorevent
23273 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23274 * @param {HtmlEditor} this
23278 * @event firstfocus
23279 * Fires when on first focus - needed by toolbars..
23280 * @param {HtmlEditor} this
23285 * Auto save the htmlEditor value as a file into Events
23286 * @param {HtmlEditor} this
23290 * @event savedpreview
23291 * preview the saved version of htmlEditor
23292 * @param {HtmlEditor} this
23299 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23303 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23308 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23313 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23318 * @cfg {Number} height (in pixels)
23322 * @cfg {Number} width (in pixels)
23327 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23330 stylesheets: false,
23335 // private properties
23336 validationEvent : false,
23338 initialized : false,
23341 onFocus : Roo.emptyFn,
23343 hideMode:'offsets',
23345 tbContainer : false,
23349 toolbarContainer :function() {
23350 return this.wrap.select('.x-html-editor-tb',true).first();
23354 * Protected method that will not generally be called directly. It
23355 * is called when the editor creates its toolbar. Override this method if you need to
23356 * add custom toolbar buttons.
23357 * @param {HtmlEditor} editor
23359 createToolbar : function(){
23360 Roo.log('renewing');
23361 Roo.log("create toolbars");
23363 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23364 this.toolbars[0].render(this.toolbarContainer());
23368 // if (!editor.toolbars || !editor.toolbars.length) {
23369 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23372 // for (var i =0 ; i < editor.toolbars.length;i++) {
23373 // editor.toolbars[i] = Roo.factory(
23374 // typeof(editor.toolbars[i]) == 'string' ?
23375 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23376 // Roo.bootstrap.HtmlEditor);
23377 // editor.toolbars[i].init(editor);
23383 onRender : function(ct, position)
23385 // Roo.log("Call onRender: " + this.xtype);
23387 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23389 this.wrap = this.inputEl().wrap({
23390 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23393 this.editorcore.onRender(ct, position);
23395 if (this.resizable) {
23396 this.resizeEl = new Roo.Resizable(this.wrap, {
23400 minHeight : this.height,
23401 height: this.height,
23402 handles : this.resizable,
23405 resize : function(r, w, h) {
23406 _t.onResize(w,h); // -something
23412 this.createToolbar(this);
23415 if(!this.width && this.resizable){
23416 this.setSize(this.wrap.getSize());
23418 if (this.resizeEl) {
23419 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23420 // should trigger onReize..
23426 onResize : function(w, h)
23428 Roo.log('resize: ' +w + ',' + h );
23429 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23433 if(this.inputEl() ){
23434 if(typeof w == 'number'){
23435 var aw = w - this.wrap.getFrameWidth('lr');
23436 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23439 if(typeof h == 'number'){
23440 var tbh = -11; // fixme it needs to tool bar size!
23441 for (var i =0; i < this.toolbars.length;i++) {
23442 // fixme - ask toolbars for heights?
23443 tbh += this.toolbars[i].el.getHeight();
23444 //if (this.toolbars[i].footer) {
23445 // tbh += this.toolbars[i].footer.el.getHeight();
23453 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23454 ah -= 5; // knock a few pixes off for look..
23455 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23459 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23460 this.editorcore.onResize(ew,eh);
23465 * Toggles the editor between standard and source edit mode.
23466 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23468 toggleSourceEdit : function(sourceEditMode)
23470 this.editorcore.toggleSourceEdit(sourceEditMode);
23472 if(this.editorcore.sourceEditMode){
23473 Roo.log('editor - showing textarea');
23476 // Roo.log(this.syncValue());
23478 this.inputEl().removeClass(['hide', 'x-hidden']);
23479 this.inputEl().dom.removeAttribute('tabIndex');
23480 this.inputEl().focus();
23482 Roo.log('editor - hiding textarea');
23484 // Roo.log(this.pushValue());
23487 this.inputEl().addClass(['hide', 'x-hidden']);
23488 this.inputEl().dom.setAttribute('tabIndex', -1);
23489 //this.deferFocus();
23492 if(this.resizable){
23493 this.setSize(this.wrap.getSize());
23496 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23499 // private (for BoxComponent)
23500 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23502 // private (for BoxComponent)
23503 getResizeEl : function(){
23507 // private (for BoxComponent)
23508 getPositionEl : function(){
23513 initEvents : function(){
23514 this.originalValue = this.getValue();
23518 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23521 // markInvalid : Roo.emptyFn,
23523 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23526 // clearInvalid : Roo.emptyFn,
23528 setValue : function(v){
23529 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23530 this.editorcore.pushValue();
23535 deferFocus : function(){
23536 this.focus.defer(10, this);
23540 focus : function(){
23541 this.editorcore.focus();
23547 onDestroy : function(){
23553 for (var i =0; i < this.toolbars.length;i++) {
23554 // fixme - ask toolbars for heights?
23555 this.toolbars[i].onDestroy();
23558 this.wrap.dom.innerHTML = '';
23559 this.wrap.remove();
23564 onFirstFocus : function(){
23565 //Roo.log("onFirstFocus");
23566 this.editorcore.onFirstFocus();
23567 for (var i =0; i < this.toolbars.length;i++) {
23568 this.toolbars[i].onFirstFocus();
23574 syncValue : function()
23576 this.editorcore.syncValue();
23579 pushValue : function()
23581 this.editorcore.pushValue();
23585 // hide stuff that is not compatible
23599 * @event specialkey
23603 * @cfg {String} fieldClass @hide
23606 * @cfg {String} focusClass @hide
23609 * @cfg {String} autoCreate @hide
23612 * @cfg {String} inputType @hide
23615 * @cfg {String} invalidClass @hide
23618 * @cfg {String} invalidText @hide
23621 * @cfg {String} msgFx @hide
23624 * @cfg {String} validateOnBlur @hide
23633 Roo.namespace('Roo.bootstrap.htmleditor');
23635 * @class Roo.bootstrap.HtmlEditorToolbar1
23640 new Roo.bootstrap.HtmlEditor({
23643 new Roo.bootstrap.HtmlEditorToolbar1({
23644 disable : { fonts: 1 , format: 1, ..., ... , ...],
23650 * @cfg {Object} disable List of elements to disable..
23651 * @cfg {Array} btns List of additional buttons.
23655 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23658 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23661 Roo.apply(this, config);
23663 // default disabled, based on 'good practice'..
23664 this.disable = this.disable || {};
23665 Roo.applyIf(this.disable, {
23668 specialElements : true
23670 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23672 this.editor = config.editor;
23673 this.editorcore = config.editor.editorcore;
23675 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23677 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23678 // dont call parent... till later.
23680 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23685 editorcore : false,
23690 "h1","h2","h3","h4","h5","h6",
23692 "abbr", "acronym", "address", "cite", "samp", "var",
23696 onRender : function(ct, position)
23698 // Roo.log("Call onRender: " + this.xtype);
23700 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23702 this.el.dom.style.marginBottom = '0';
23704 var editorcore = this.editorcore;
23705 var editor= this.editor;
23708 var btn = function(id,cmd , toggle, handler, html){
23710 var event = toggle ? 'toggle' : 'click';
23715 xns: Roo.bootstrap,
23718 enableToggle:toggle !== false,
23720 pressed : toggle ? false : null,
23723 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23724 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23730 // var cb_box = function...
23735 xns: Roo.bootstrap,
23736 glyphicon : 'font',
23740 xns: Roo.bootstrap,
23744 Roo.each(this.formats, function(f) {
23745 style.menu.items.push({
23747 xns: Roo.bootstrap,
23748 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23753 editorcore.insertTag(this.tagname);
23760 children.push(style);
23762 btn('bold',false,true);
23763 btn('italic',false,true);
23764 btn('align-left', 'justifyleft',true);
23765 btn('align-center', 'justifycenter',true);
23766 btn('align-right' , 'justifyright',true);
23767 btn('link', false, false, function(btn) {
23768 //Roo.log("create link?");
23769 var url = prompt(this.createLinkText, this.defaultLinkValue);
23770 if(url && url != 'http:/'+'/'){
23771 this.editorcore.relayCmd('createlink', url);
23774 btn('list','insertunorderedlist',true);
23775 btn('pencil', false,true, function(btn){
23777 this.toggleSourceEdit(btn.pressed);
23780 if (this.editor.btns.length > 0) {
23781 for (var i = 0; i<this.editor.btns.length; i++) {
23782 children.push(this.editor.btns[i]);
23790 xns: Roo.bootstrap,
23795 xns: Roo.bootstrap,
23800 cog.menu.items.push({
23802 xns: Roo.bootstrap,
23803 html : Clean styles,
23808 editorcore.insertTag(this.tagname);
23817 this.xtype = 'NavSimplebar';
23819 for(var i=0;i< children.length;i++) {
23821 this.buttons.add(this.addxtypeChild(children[i]));
23825 editor.on('editorevent', this.updateToolbar, this);
23827 onBtnClick : function(id)
23829 this.editorcore.relayCmd(id);
23830 this.editorcore.focus();
23834 * Protected method that will not generally be called directly. It triggers
23835 * a toolbar update by reading the markup state of the current selection in the editor.
23837 updateToolbar: function(){
23839 if(!this.editorcore.activated){
23840 this.editor.onFirstFocus(); // is this neeed?
23844 var btns = this.buttons;
23845 var doc = this.editorcore.doc;
23846 btns.get('bold').setActive(doc.queryCommandState('bold'));
23847 btns.get('italic').setActive(doc.queryCommandState('italic'));
23848 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23850 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23851 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23852 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23854 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23855 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23858 var ans = this.editorcore.getAllAncestors();
23859 if (this.formatCombo) {
23862 var store = this.formatCombo.store;
23863 this.formatCombo.setValue("");
23864 for (var i =0; i < ans.length;i++) {
23865 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23867 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23875 // hides menus... - so this cant be on a menu...
23876 Roo.bootstrap.MenuMgr.hideAll();
23878 Roo.bootstrap.MenuMgr.hideAll();
23879 //this.editorsyncValue();
23881 onFirstFocus: function() {
23882 this.buttons.each(function(item){
23886 toggleSourceEdit : function(sourceEditMode){
23889 if(sourceEditMode){
23890 Roo.log("disabling buttons");
23891 this.buttons.each( function(item){
23892 if(item.cmd != 'pencil'){
23898 Roo.log("enabling buttons");
23899 if(this.editorcore.initialized){
23900 this.buttons.each( function(item){
23906 Roo.log("calling toggole on editor");
23907 // tell the editor that it's been pressed..
23908 this.editor.toggleSourceEdit(sourceEditMode);
23918 * @class Roo.bootstrap.Table.AbstractSelectionModel
23919 * @extends Roo.util.Observable
23920 * Abstract base class for grid SelectionModels. It provides the interface that should be
23921 * implemented by descendant classes. This class should not be directly instantiated.
23924 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23925 this.locked = false;
23926 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23930 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23931 /** @ignore Called by the grid automatically. Do not call directly. */
23932 init : function(grid){
23938 * Locks the selections.
23941 this.locked = true;
23945 * Unlocks the selections.
23947 unlock : function(){
23948 this.locked = false;
23952 * Returns true if the selections are locked.
23953 * @return {Boolean}
23955 isLocked : function(){
23956 return this.locked;
23960 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23961 * @class Roo.bootstrap.Table.RowSelectionModel
23962 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23963 * It supports multiple selections and keyboard selection/navigation.
23965 * @param {Object} config
23968 Roo.bootstrap.Table.RowSelectionModel = function(config){
23969 Roo.apply(this, config);
23970 this.selections = new Roo.util.MixedCollection(false, function(o){
23975 this.lastActive = false;
23979 * @event selectionchange
23980 * Fires when the selection changes
23981 * @param {SelectionModel} this
23983 "selectionchange" : true,
23985 * @event afterselectionchange
23986 * Fires after the selection changes (eg. by key press or clicking)
23987 * @param {SelectionModel} this
23989 "afterselectionchange" : true,
23991 * @event beforerowselect
23992 * Fires when a row is selected being selected, return false to cancel.
23993 * @param {SelectionModel} this
23994 * @param {Number} rowIndex The selected index
23995 * @param {Boolean} keepExisting False if other selections will be cleared
23997 "beforerowselect" : true,
24000 * Fires when a row is selected.
24001 * @param {SelectionModel} this
24002 * @param {Number} rowIndex The selected index
24003 * @param {Roo.data.Record} r The record
24005 "rowselect" : true,
24007 * @event rowdeselect
24008 * Fires when a row is deselected.
24009 * @param {SelectionModel} this
24010 * @param {Number} rowIndex The selected index
24012 "rowdeselect" : true
24014 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24015 this.locked = false;
24018 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24020 * @cfg {Boolean} singleSelect
24021 * True to allow selection of only one row at a time (defaults to false)
24023 singleSelect : false,
24026 initEvents : function()
24029 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24030 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24031 //}else{ // allow click to work like normal
24032 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24034 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24035 this.grid.on("rowclick", this.handleMouseDown, this);
24037 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24038 "up" : function(e){
24040 this.selectPrevious(e.shiftKey);
24041 }else if(this.last !== false && this.lastActive !== false){
24042 var last = this.last;
24043 this.selectRange(this.last, this.lastActive-1);
24044 this.grid.getView().focusRow(this.lastActive);
24045 if(last !== false){
24049 this.selectFirstRow();
24051 this.fireEvent("afterselectionchange", this);
24053 "down" : function(e){
24055 this.selectNext(e.shiftKey);
24056 }else if(this.last !== false && this.lastActive !== false){
24057 var last = this.last;
24058 this.selectRange(this.last, this.lastActive+1);
24059 this.grid.getView().focusRow(this.lastActive);
24060 if(last !== false){
24064 this.selectFirstRow();
24066 this.fireEvent("afterselectionchange", this);
24070 this.grid.store.on('load', function(){
24071 this.selections.clear();
24074 var view = this.grid.view;
24075 view.on("refresh", this.onRefresh, this);
24076 view.on("rowupdated", this.onRowUpdated, this);
24077 view.on("rowremoved", this.onRemove, this);
24082 onRefresh : function()
24084 var ds = this.grid.store, i, v = this.grid.view;
24085 var s = this.selections;
24086 s.each(function(r){
24087 if((i = ds.indexOfId(r.id)) != -1){
24096 onRemove : function(v, index, r){
24097 this.selections.remove(r);
24101 onRowUpdated : function(v, index, r){
24102 if(this.isSelected(r)){
24103 v.onRowSelect(index);
24109 * @param {Array} records The records to select
24110 * @param {Boolean} keepExisting (optional) True to keep existing selections
24112 selectRecords : function(records, keepExisting)
24115 this.clearSelections();
24117 var ds = this.grid.store;
24118 for(var i = 0, len = records.length; i < len; i++){
24119 this.selectRow(ds.indexOf(records[i]), true);
24124 * Gets the number of selected rows.
24127 getCount : function(){
24128 return this.selections.length;
24132 * Selects the first row in the grid.
24134 selectFirstRow : function(){
24139 * Select the last row.
24140 * @param {Boolean} keepExisting (optional) True to keep existing selections
24142 selectLastRow : function(keepExisting){
24143 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24144 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24148 * Selects the row immediately following the last selected row.
24149 * @param {Boolean} keepExisting (optional) True to keep existing selections
24151 selectNext : function(keepExisting)
24153 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24154 this.selectRow(this.last+1, keepExisting);
24155 this.grid.getView().focusRow(this.last);
24160 * Selects the row that precedes the last selected row.
24161 * @param {Boolean} keepExisting (optional) True to keep existing selections
24163 selectPrevious : function(keepExisting){
24165 this.selectRow(this.last-1, keepExisting);
24166 this.grid.getView().focusRow(this.last);
24171 * Returns the selected records
24172 * @return {Array} Array of selected records
24174 getSelections : function(){
24175 return [].concat(this.selections.items);
24179 * Returns the first selected record.
24182 getSelected : function(){
24183 return this.selections.itemAt(0);
24188 * Clears all selections.
24190 clearSelections : function(fast)
24196 var ds = this.grid.store;
24197 var s = this.selections;
24198 s.each(function(r){
24199 this.deselectRow(ds.indexOfId(r.id));
24203 this.selections.clear();
24210 * Selects all rows.
24212 selectAll : function(){
24216 this.selections.clear();
24217 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24218 this.selectRow(i, true);
24223 * Returns True if there is a selection.
24224 * @return {Boolean}
24226 hasSelection : function(){
24227 return this.selections.length > 0;
24231 * Returns True if the specified row is selected.
24232 * @param {Number/Record} record The record or index of the record to check
24233 * @return {Boolean}
24235 isSelected : function(index){
24236 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24237 return (r && this.selections.key(r.id) ? true : false);
24241 * Returns True if the specified record id is selected.
24242 * @param {String} id The id of record to check
24243 * @return {Boolean}
24245 isIdSelected : function(id){
24246 return (this.selections.key(id) ? true : false);
24251 handleMouseDBClick : function(e, t){
24255 handleMouseDown : function(e, t)
24257 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24258 if(this.isLocked() || rowIndex < 0 ){
24261 if(e.shiftKey && this.last !== false){
24262 var last = this.last;
24263 this.selectRange(last, rowIndex, e.ctrlKey);
24264 this.last = last; // reset the last
24268 var isSelected = this.isSelected(rowIndex);
24269 //Roo.log("select row:" + rowIndex);
24271 this.deselectRow(rowIndex);
24273 this.selectRow(rowIndex, true);
24277 if(e.button !== 0 && isSelected){
24278 alert('rowIndex 2: ' + rowIndex);
24279 view.focusRow(rowIndex);
24280 }else if(e.ctrlKey && isSelected){
24281 this.deselectRow(rowIndex);
24282 }else if(!isSelected){
24283 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24284 view.focusRow(rowIndex);
24288 this.fireEvent("afterselectionchange", this);
24291 handleDragableRowClick : function(grid, rowIndex, e)
24293 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24294 this.selectRow(rowIndex, false);
24295 grid.view.focusRow(rowIndex);
24296 this.fireEvent("afterselectionchange", this);
24301 * Selects multiple rows.
24302 * @param {Array} rows Array of the indexes of the row to select
24303 * @param {Boolean} keepExisting (optional) True to keep existing selections
24305 selectRows : function(rows, keepExisting){
24307 this.clearSelections();
24309 for(var i = 0, len = rows.length; i < len; i++){
24310 this.selectRow(rows[i], true);
24315 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24316 * @param {Number} startRow The index of the first row in the range
24317 * @param {Number} endRow The index of the last row in the range
24318 * @param {Boolean} keepExisting (optional) True to retain existing selections
24320 selectRange : function(startRow, endRow, keepExisting){
24325 this.clearSelections();
24327 if(startRow <= endRow){
24328 for(var i = startRow; i <= endRow; i++){
24329 this.selectRow(i, true);
24332 for(var i = startRow; i >= endRow; i--){
24333 this.selectRow(i, true);
24339 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24340 * @param {Number} startRow The index of the first row in the range
24341 * @param {Number} endRow The index of the last row in the range
24343 deselectRange : function(startRow, endRow, preventViewNotify){
24347 for(var i = startRow; i <= endRow; i++){
24348 this.deselectRow(i, preventViewNotify);
24354 * @param {Number} row The index of the row to select
24355 * @param {Boolean} keepExisting (optional) True to keep existing selections
24357 selectRow : function(index, keepExisting, preventViewNotify)
24359 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24362 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24363 if(!keepExisting || this.singleSelect){
24364 this.clearSelections();
24367 var r = this.grid.store.getAt(index);
24368 //console.log('selectRow - record id :' + r.id);
24370 this.selections.add(r);
24371 this.last = this.lastActive = index;
24372 if(!preventViewNotify){
24373 var proxy = new Roo.Element(
24374 this.grid.getRowDom(index)
24376 proxy.addClass('bg-info info');
24378 this.fireEvent("rowselect", this, index, r);
24379 this.fireEvent("selectionchange", this);
24385 * @param {Number} row The index of the row to deselect
24387 deselectRow : function(index, preventViewNotify)
24392 if(this.last == index){
24395 if(this.lastActive == index){
24396 this.lastActive = false;
24399 var r = this.grid.store.getAt(index);
24404 this.selections.remove(r);
24405 //.console.log('deselectRow - record id :' + r.id);
24406 if(!preventViewNotify){
24408 var proxy = new Roo.Element(
24409 this.grid.getRowDom(index)
24411 proxy.removeClass('bg-info info');
24413 this.fireEvent("rowdeselect", this, index);
24414 this.fireEvent("selectionchange", this);
24418 restoreLast : function(){
24420 this.last = this._last;
24425 acceptsNav : function(row, col, cm){
24426 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24430 onEditorKey : function(field, e){
24431 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24436 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24438 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24440 }else if(k == e.ENTER && !e.ctrlKey){
24444 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24446 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24448 }else if(k == e.ESC){
24452 g.startEditing(newCell[0], newCell[1]);
24458 * Ext JS Library 1.1.1
24459 * Copyright(c) 2006-2007, Ext JS, LLC.
24461 * Originally Released Under LGPL - original licence link has changed is not relivant.
24464 * <script type="text/javascript">
24468 * @class Roo.bootstrap.PagingToolbar
24469 * @extends Roo.bootstrap.NavSimplebar
24470 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24472 * Create a new PagingToolbar
24473 * @param {Object} config The config object
24474 * @param {Roo.data.Store} store
24476 Roo.bootstrap.PagingToolbar = function(config)
24478 // old args format still supported... - xtype is prefered..
24479 // created from xtype...
24481 this.ds = config.dataSource;
24483 if (config.store && !this.ds) {
24484 this.store= Roo.factory(config.store, Roo.data);
24485 this.ds = this.store;
24486 this.ds.xmodule = this.xmodule || false;
24489 this.toolbarItems = [];
24490 if (config.items) {
24491 this.toolbarItems = config.items;
24494 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24499 this.bind(this.ds);
24502 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24506 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24508 * @cfg {Roo.data.Store} dataSource
24509 * The underlying data store providing the paged data
24512 * @cfg {String/HTMLElement/Element} container
24513 * container The id or element that will contain the toolbar
24516 * @cfg {Boolean} displayInfo
24517 * True to display the displayMsg (defaults to false)
24520 * @cfg {Number} pageSize
24521 * The number of records to display per page (defaults to 20)
24525 * @cfg {String} displayMsg
24526 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24528 displayMsg : 'Displaying {0} - {1} of {2}',
24530 * @cfg {String} emptyMsg
24531 * The message to display when no records are found (defaults to "No data to display")
24533 emptyMsg : 'No data to display',
24535 * Customizable piece of the default paging text (defaults to "Page")
24538 beforePageText : "Page",
24540 * Customizable piece of the default paging text (defaults to "of %0")
24543 afterPageText : "of {0}",
24545 * Customizable piece of the default paging text (defaults to "First Page")
24548 firstText : "First Page",
24550 * Customizable piece of the default paging text (defaults to "Previous Page")
24553 prevText : "Previous Page",
24555 * Customizable piece of the default paging text (defaults to "Next Page")
24558 nextText : "Next Page",
24560 * Customizable piece of the default paging text (defaults to "Last Page")
24563 lastText : "Last Page",
24565 * Customizable piece of the default paging text (defaults to "Refresh")
24568 refreshText : "Refresh",
24572 onRender : function(ct, position)
24574 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24575 this.navgroup.parentId = this.id;
24576 this.navgroup.onRender(this.el, null);
24577 // add the buttons to the navgroup
24579 if(this.displayInfo){
24580 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24581 this.displayEl = this.el.select('.x-paging-info', true).first();
24582 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24583 // this.displayEl = navel.el.select('span',true).first();
24589 Roo.each(_this.buttons, function(e){ // this might need to use render????
24590 Roo.factory(e).render(_this.el);
24594 Roo.each(_this.toolbarItems, function(e) {
24595 _this.navgroup.addItem(e);
24599 this.first = this.navgroup.addItem({
24600 tooltip: this.firstText,
24602 icon : 'fa fa-backward',
24604 preventDefault: true,
24605 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24608 this.prev = this.navgroup.addItem({
24609 tooltip: this.prevText,
24611 icon : 'fa fa-step-backward',
24613 preventDefault: true,
24614 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24616 //this.addSeparator();
24619 var field = this.navgroup.addItem( {
24621 cls : 'x-paging-position',
24623 html : this.beforePageText +
24624 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24625 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24628 this.field = field.el.select('input', true).first();
24629 this.field.on("keydown", this.onPagingKeydown, this);
24630 this.field.on("focus", function(){this.dom.select();});
24633 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24634 //this.field.setHeight(18);
24635 //this.addSeparator();
24636 this.next = this.navgroup.addItem({
24637 tooltip: this.nextText,
24639 html : ' <i class="fa fa-step-forward">',
24641 preventDefault: true,
24642 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24644 this.last = this.navgroup.addItem({
24645 tooltip: this.lastText,
24646 icon : 'fa fa-forward',
24649 preventDefault: true,
24650 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24652 //this.addSeparator();
24653 this.loading = this.navgroup.addItem({
24654 tooltip: this.refreshText,
24655 icon: 'fa fa-refresh',
24656 preventDefault: true,
24657 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24663 updateInfo : function(){
24664 if(this.displayEl){
24665 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24666 var msg = count == 0 ?
24670 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24672 this.displayEl.update(msg);
24677 onLoad : function(ds, r, o)
24679 this.cursor = o.params.start ? o.params.start : 0;
24681 var d = this.getPageData(),
24686 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24687 this.field.dom.value = ap;
24688 this.first.setDisabled(ap == 1);
24689 this.prev.setDisabled(ap == 1);
24690 this.next.setDisabled(ap == ps);
24691 this.last.setDisabled(ap == ps);
24692 this.loading.enable();
24697 getPageData : function(){
24698 var total = this.ds.getTotalCount();
24701 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24702 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24707 onLoadError : function(){
24708 this.loading.enable();
24712 onPagingKeydown : function(e){
24713 var k = e.getKey();
24714 var d = this.getPageData();
24716 var v = this.field.dom.value, pageNum;
24717 if(!v || isNaN(pageNum = parseInt(v, 10))){
24718 this.field.dom.value = d.activePage;
24721 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24722 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24725 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))
24727 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24728 this.field.dom.value = pageNum;
24729 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24732 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24734 var v = this.field.dom.value, pageNum;
24735 var increment = (e.shiftKey) ? 10 : 1;
24736 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24739 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24740 this.field.dom.value = d.activePage;
24743 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24745 this.field.dom.value = parseInt(v, 10) + increment;
24746 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24747 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24754 beforeLoad : function(){
24756 this.loading.disable();
24761 onClick : function(which){
24770 ds.load({params:{start: 0, limit: this.pageSize}});
24773 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24776 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24779 var total = ds.getTotalCount();
24780 var extra = total % this.pageSize;
24781 var lastStart = extra ? (total - extra) : total-this.pageSize;
24782 ds.load({params:{start: lastStart, limit: this.pageSize}});
24785 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24791 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24792 * @param {Roo.data.Store} store The data store to unbind
24794 unbind : function(ds){
24795 ds.un("beforeload", this.beforeLoad, this);
24796 ds.un("load", this.onLoad, this);
24797 ds.un("loadexception", this.onLoadError, this);
24798 ds.un("remove", this.updateInfo, this);
24799 ds.un("add", this.updateInfo, this);
24800 this.ds = undefined;
24804 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24805 * @param {Roo.data.Store} store The data store to bind
24807 bind : function(ds){
24808 ds.on("beforeload", this.beforeLoad, this);
24809 ds.on("load", this.onLoad, this);
24810 ds.on("loadexception", this.onLoadError, this);
24811 ds.on("remove", this.updateInfo, this);
24812 ds.on("add", this.updateInfo, this);
24823 * @class Roo.bootstrap.MessageBar
24824 * @extends Roo.bootstrap.Component
24825 * Bootstrap MessageBar class
24826 * @cfg {String} html contents of the MessageBar
24827 * @cfg {String} weight (info | success | warning | danger) default info
24828 * @cfg {String} beforeClass insert the bar before the given class
24829 * @cfg {Boolean} closable (true | false) default false
24830 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24833 * Create a new Element
24834 * @param {Object} config The config object
24837 Roo.bootstrap.MessageBar = function(config){
24838 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24841 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24847 beforeClass: 'bootstrap-sticky-wrap',
24849 getAutoCreate : function(){
24853 cls: 'alert alert-dismissable alert-' + this.weight,
24858 html: this.html || ''
24864 cfg.cls += ' alert-messages-fixed';
24878 onRender : function(ct, position)
24880 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24883 var cfg = Roo.apply({}, this.getAutoCreate());
24887 cfg.cls += ' ' + this.cls;
24890 cfg.style = this.style;
24892 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24894 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24897 this.el.select('>button.close').on('click', this.hide, this);
24903 if (!this.rendered) {
24909 this.fireEvent('show', this);
24915 if (!this.rendered) {
24921 this.fireEvent('hide', this);
24924 update : function()
24926 // var e = this.el.dom.firstChild;
24928 // if(this.closable){
24929 // e = e.nextSibling;
24932 // e.data = this.html || '';
24934 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24950 * @class Roo.bootstrap.Graph
24951 * @extends Roo.bootstrap.Component
24952 * Bootstrap Graph class
24956 @cfg {String} graphtype bar | vbar | pie
24957 @cfg {number} g_x coodinator | centre x (pie)
24958 @cfg {number} g_y coodinator | centre y (pie)
24959 @cfg {number} g_r radius (pie)
24960 @cfg {number} g_height height of the chart (respected by all elements in the set)
24961 @cfg {number} g_width width of the chart (respected by all elements in the set)
24962 @cfg {Object} title The title of the chart
24965 -opts (object) options for the chart
24967 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24968 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24970 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.
24971 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24973 o stretch (boolean)
24975 -opts (object) options for the pie
24978 o startAngle (number)
24979 o endAngle (number)
24983 * Create a new Input
24984 * @param {Object} config The config object
24987 Roo.bootstrap.Graph = function(config){
24988 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24994 * The img click event for the img.
24995 * @param {Roo.EventObject} e
25001 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25012 //g_colors: this.colors,
25019 getAutoCreate : function(){
25030 onRender : function(ct,position){
25033 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25035 if (typeof(Raphael) == 'undefined') {
25036 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25040 this.raphael = Raphael(this.el.dom);
25042 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25043 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25044 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25045 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25047 r.text(160, 10, "Single Series Chart").attr(txtattr);
25048 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25049 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25050 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25052 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25053 r.barchart(330, 10, 300, 220, data1);
25054 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25055 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25058 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25059 // r.barchart(30, 30, 560, 250, xdata, {
25060 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25061 // axis : "0 0 1 1",
25062 // axisxlabels : xdata
25063 // //yvalues : cols,
25066 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25068 // this.load(null,xdata,{
25069 // axis : "0 0 1 1",
25070 // axisxlabels : xdata
25075 load : function(graphtype,xdata,opts)
25077 this.raphael.clear();
25079 graphtype = this.graphtype;
25084 var r = this.raphael,
25085 fin = function () {
25086 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25088 fout = function () {
25089 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25091 pfin = function() {
25092 this.sector.stop();
25093 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25096 this.label[0].stop();
25097 this.label[0].attr({ r: 7.5 });
25098 this.label[1].attr({ "font-weight": 800 });
25101 pfout = function() {
25102 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25105 this.label[0].animate({ r: 5 }, 500, "bounce");
25106 this.label[1].attr({ "font-weight": 400 });
25112 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25115 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25118 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25119 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25121 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25128 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25133 setTitle: function(o)
25138 initEvents: function() {
25141 this.el.on('click', this.onClick, this);
25145 onClick : function(e)
25147 Roo.log('img onclick');
25148 this.fireEvent('click', this, e);
25160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25163 * @class Roo.bootstrap.dash.NumberBox
25164 * @extends Roo.bootstrap.Component
25165 * Bootstrap NumberBox class
25166 * @cfg {String} headline Box headline
25167 * @cfg {String} content Box content
25168 * @cfg {String} icon Box icon
25169 * @cfg {String} footer Footer text
25170 * @cfg {String} fhref Footer href
25173 * Create a new NumberBox
25174 * @param {Object} config The config object
25178 Roo.bootstrap.dash.NumberBox = function(config){
25179 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25183 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25192 getAutoCreate : function(){
25196 cls : 'small-box ',
25204 cls : 'roo-headline',
25205 html : this.headline
25209 cls : 'roo-content',
25210 html : this.content
25224 cls : 'ion ' + this.icon
25233 cls : 'small-box-footer',
25234 href : this.fhref || '#',
25238 cfg.cn.push(footer);
25245 onRender : function(ct,position){
25246 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25253 setHeadline: function (value)
25255 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25258 setFooter: function (value, href)
25260 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25263 this.el.select('a.small-box-footer',true).first().attr('href', href);
25268 setContent: function (value)
25270 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25273 initEvents: function()
25287 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25290 * @class Roo.bootstrap.dash.TabBox
25291 * @extends Roo.bootstrap.Component
25292 * Bootstrap TabBox class
25293 * @cfg {String} title Title of the TabBox
25294 * @cfg {String} icon Icon of the TabBox
25295 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25296 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25299 * Create a new TabBox
25300 * @param {Object} config The config object
25304 Roo.bootstrap.dash.TabBox = function(config){
25305 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25310 * When a pane is added
25311 * @param {Roo.bootstrap.dash.TabPane} pane
25315 * @event activatepane
25316 * When a pane is activated
25317 * @param {Roo.bootstrap.dash.TabPane} pane
25319 "activatepane" : true
25327 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25332 tabScrollable : false,
25334 getChildContainer : function()
25336 return this.el.select('.tab-content', true).first();
25339 getAutoCreate : function(){
25343 cls: 'pull-left header',
25351 cls: 'fa ' + this.icon
25357 cls: 'nav nav-tabs pull-right',
25363 if(this.tabScrollable){
25370 cls: 'nav nav-tabs pull-right',
25381 cls: 'nav-tabs-custom',
25386 cls: 'tab-content no-padding',
25394 initEvents : function()
25396 //Roo.log('add add pane handler');
25397 this.on('addpane', this.onAddPane, this);
25400 * Updates the box title
25401 * @param {String} html to set the title to.
25403 setTitle : function(value)
25405 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25407 onAddPane : function(pane)
25409 this.panes.push(pane);
25410 //Roo.log('addpane');
25412 // tabs are rendere left to right..
25413 if(!this.showtabs){
25417 var ctr = this.el.select('.nav-tabs', true).first();
25420 var existing = ctr.select('.nav-tab',true);
25421 var qty = existing.getCount();;
25424 var tab = ctr.createChild({
25426 cls : 'nav-tab' + (qty ? '' : ' active'),
25434 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25437 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25439 pane.el.addClass('active');
25444 onTabClick : function(ev,un,ob,pane)
25446 //Roo.log('tab - prev default');
25447 ev.preventDefault();
25450 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25451 pane.tab.addClass('active');
25452 //Roo.log(pane.title);
25453 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25454 // technically we should have a deactivate event.. but maybe add later.
25455 // and it should not de-activate the selected tab...
25456 this.fireEvent('activatepane', pane);
25457 pane.el.addClass('active');
25458 pane.fireEvent('activate');
25463 getActivePane : function()
25466 Roo.each(this.panes, function(p) {
25467 if(p.el.hasClass('active')){
25488 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25490 * @class Roo.bootstrap.TabPane
25491 * @extends Roo.bootstrap.Component
25492 * Bootstrap TabPane class
25493 * @cfg {Boolean} active (false | true) Default false
25494 * @cfg {String} title title of panel
25498 * Create a new TabPane
25499 * @param {Object} config The config object
25502 Roo.bootstrap.dash.TabPane = function(config){
25503 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25509 * When a pane is activated
25510 * @param {Roo.bootstrap.dash.TabPane} pane
25517 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25522 // the tabBox that this is attached to.
25525 getAutoCreate : function()
25533 cfg.cls += ' active';
25538 initEvents : function()
25540 //Roo.log('trigger add pane handler');
25541 this.parent().fireEvent('addpane', this)
25545 * Updates the tab title
25546 * @param {String} html to set the title to.
25548 setTitle: function(str)
25554 this.tab.select('a', true).first().dom.innerHTML = str;
25571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25574 * @class Roo.bootstrap.menu.Menu
25575 * @extends Roo.bootstrap.Component
25576 * Bootstrap Menu class - container for Menu
25577 * @cfg {String} html Text of the menu
25578 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25579 * @cfg {String} icon Font awesome icon
25580 * @cfg {String} pos Menu align to (top | bottom) default bottom
25584 * Create a new Menu
25585 * @param {Object} config The config object
25589 Roo.bootstrap.menu.Menu = function(config){
25590 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25594 * @event beforeshow
25595 * Fires before this menu is displayed
25596 * @param {Roo.bootstrap.menu.Menu} this
25600 * @event beforehide
25601 * Fires before this menu is hidden
25602 * @param {Roo.bootstrap.menu.Menu} this
25607 * Fires after this menu is displayed
25608 * @param {Roo.bootstrap.menu.Menu} this
25613 * Fires after this menu is hidden
25614 * @param {Roo.bootstrap.menu.Menu} this
25619 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25620 * @param {Roo.bootstrap.menu.Menu} this
25621 * @param {Roo.EventObject} e
25628 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25632 weight : 'default',
25637 getChildContainer : function() {
25638 if(this.isSubMenu){
25642 return this.el.select('ul.dropdown-menu', true).first();
25645 getAutoCreate : function()
25650 cls : 'roo-menu-text',
25658 cls : 'fa ' + this.icon
25669 cls : 'dropdown-button btn btn-' + this.weight,
25674 cls : 'dropdown-toggle btn btn-' + this.weight,
25684 cls : 'dropdown-menu'
25690 if(this.pos == 'top'){
25691 cfg.cls += ' dropup';
25694 if(this.isSubMenu){
25697 cls : 'dropdown-menu'
25704 onRender : function(ct, position)
25706 this.isSubMenu = ct.hasClass('dropdown-submenu');
25708 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25711 initEvents : function()
25713 if(this.isSubMenu){
25717 this.hidden = true;
25719 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25720 this.triggerEl.on('click', this.onTriggerPress, this);
25722 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25723 this.buttonEl.on('click', this.onClick, this);
25729 if(this.isSubMenu){
25733 return this.el.select('ul.dropdown-menu', true).first();
25736 onClick : function(e)
25738 this.fireEvent("click", this, e);
25741 onTriggerPress : function(e)
25743 if (this.isVisible()) {
25750 isVisible : function(){
25751 return !this.hidden;
25756 this.fireEvent("beforeshow", this);
25758 this.hidden = false;
25759 this.el.addClass('open');
25761 Roo.get(document).on("mouseup", this.onMouseUp, this);
25763 this.fireEvent("show", this);
25770 this.fireEvent("beforehide", this);
25772 this.hidden = true;
25773 this.el.removeClass('open');
25775 Roo.get(document).un("mouseup", this.onMouseUp);
25777 this.fireEvent("hide", this);
25780 onMouseUp : function()
25794 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25797 * @class Roo.bootstrap.menu.Item
25798 * @extends Roo.bootstrap.Component
25799 * Bootstrap MenuItem class
25800 * @cfg {Boolean} submenu (true | false) default false
25801 * @cfg {String} html text of the item
25802 * @cfg {String} href the link
25803 * @cfg {Boolean} disable (true | false) default false
25804 * @cfg {Boolean} preventDefault (true | false) default true
25805 * @cfg {String} icon Font awesome icon
25806 * @cfg {String} pos Submenu align to (left | right) default right
25810 * Create a new Item
25811 * @param {Object} config The config object
25815 Roo.bootstrap.menu.Item = function(config){
25816 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25820 * Fires when the mouse is hovering over this menu
25821 * @param {Roo.bootstrap.menu.Item} this
25822 * @param {Roo.EventObject} e
25827 * Fires when the mouse exits this menu
25828 * @param {Roo.bootstrap.menu.Item} this
25829 * @param {Roo.EventObject} e
25835 * The raw click event for the entire grid.
25836 * @param {Roo.EventObject} e
25842 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25847 preventDefault: true,
25852 getAutoCreate : function()
25857 cls : 'roo-menu-item-text',
25865 cls : 'fa ' + this.icon
25874 href : this.href || '#',
25881 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25885 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25887 if(this.pos == 'left'){
25888 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25895 initEvents : function()
25897 this.el.on('mouseover', this.onMouseOver, this);
25898 this.el.on('mouseout', this.onMouseOut, this);
25900 this.el.select('a', true).first().on('click', this.onClick, this);
25904 onClick : function(e)
25906 if(this.preventDefault){
25907 e.preventDefault();
25910 this.fireEvent("click", this, e);
25913 onMouseOver : function(e)
25915 if(this.submenu && this.pos == 'left'){
25916 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25919 this.fireEvent("mouseover", this, e);
25922 onMouseOut : function(e)
25924 this.fireEvent("mouseout", this, e);
25936 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25939 * @class Roo.bootstrap.menu.Separator
25940 * @extends Roo.bootstrap.Component
25941 * Bootstrap Separator class
25944 * Create a new Separator
25945 * @param {Object} config The config object
25949 Roo.bootstrap.menu.Separator = function(config){
25950 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25953 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25955 getAutoCreate : function(){
25976 * @class Roo.bootstrap.Tooltip
25977 * Bootstrap Tooltip class
25978 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25979 * to determine which dom element triggers the tooltip.
25981 * It needs to add support for additional attributes like tooltip-position
25984 * Create a new Toolti
25985 * @param {Object} config The config object
25988 Roo.bootstrap.Tooltip = function(config){
25989 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25991 this.alignment = Roo.bootstrap.Tooltip.alignment;
25993 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25994 this.alignment = config.alignment;
25999 Roo.apply(Roo.bootstrap.Tooltip, {
26001 * @function init initialize tooltip monitoring.
26005 currentTip : false,
26006 currentRegion : false,
26012 Roo.get(document).on('mouseover', this.enter ,this);
26013 Roo.get(document).on('mouseout', this.leave, this);
26016 this.currentTip = new Roo.bootstrap.Tooltip();
26019 enter : function(ev)
26021 var dom = ev.getTarget();
26023 //Roo.log(['enter',dom]);
26024 var el = Roo.fly(dom);
26025 if (this.currentEl) {
26027 //Roo.log(this.currentEl);
26028 //Roo.log(this.currentEl.contains(dom));
26029 if (this.currentEl == el) {
26032 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26038 if (this.currentTip.el) {
26039 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26043 if(!el || el.dom == document){
26049 // you can not look for children, as if el is the body.. then everythign is the child..
26050 if (!el.attr('tooltip')) { //
26051 if (!el.select("[tooltip]").elements.length) {
26054 // is the mouse over this child...?
26055 bindEl = el.select("[tooltip]").first();
26056 var xy = ev.getXY();
26057 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26058 //Roo.log("not in region.");
26061 //Roo.log("child element over..");
26064 this.currentEl = bindEl;
26065 this.currentTip.bind(bindEl);
26066 this.currentRegion = Roo.lib.Region.getRegion(dom);
26067 this.currentTip.enter();
26070 leave : function(ev)
26072 var dom = ev.getTarget();
26073 //Roo.log(['leave',dom]);
26074 if (!this.currentEl) {
26079 if (dom != this.currentEl.dom) {
26082 var xy = ev.getXY();
26083 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26086 // only activate leave if mouse cursor is outside... bounding box..
26091 if (this.currentTip) {
26092 this.currentTip.leave();
26094 //Roo.log('clear currentEl');
26095 this.currentEl = false;
26100 'left' : ['r-l', [-2,0], 'right'],
26101 'right' : ['l-r', [2,0], 'left'],
26102 'bottom' : ['t-b', [0,2], 'top'],
26103 'top' : [ 'b-t', [0,-2], 'bottom']
26109 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26114 delay : null, // can be { show : 300 , hide: 500}
26118 hoverState : null, //???
26120 placement : 'bottom',
26124 getAutoCreate : function(){
26131 cls : 'tooltip-arrow'
26134 cls : 'tooltip-inner'
26141 bind : function(el)
26147 enter : function () {
26149 if (this.timeout != null) {
26150 clearTimeout(this.timeout);
26153 this.hoverState = 'in';
26154 //Roo.log("enter - show");
26155 if (!this.delay || !this.delay.show) {
26160 this.timeout = setTimeout(function () {
26161 if (_t.hoverState == 'in') {
26164 }, this.delay.show);
26168 clearTimeout(this.timeout);
26170 this.hoverState = 'out';
26171 if (!this.delay || !this.delay.hide) {
26177 this.timeout = setTimeout(function () {
26178 //Roo.log("leave - timeout");
26180 if (_t.hoverState == 'out') {
26182 Roo.bootstrap.Tooltip.currentEl = false;
26187 show : function (msg)
26190 this.render(document.body);
26193 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26195 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26197 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26199 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26201 var placement = typeof this.placement == 'function' ?
26202 this.placement.call(this, this.el, on_el) :
26205 var autoToken = /\s?auto?\s?/i;
26206 var autoPlace = autoToken.test(placement);
26208 placement = placement.replace(autoToken, '') || 'top';
26212 //this.el.setXY([0,0]);
26214 //this.el.dom.style.display='block';
26216 //this.el.appendTo(on_el);
26218 var p = this.getPosition();
26219 var box = this.el.getBox();
26225 var align = this.alignment[placement];
26227 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26229 if(placement == 'top' || placement == 'bottom'){
26231 placement = 'right';
26234 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26235 placement = 'left';
26238 var scroll = Roo.select('body', true).first().getScroll();
26240 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26244 align = this.alignment[placement];
26247 this.el.alignTo(this.bindEl, align[0],align[1]);
26248 //var arrow = this.el.select('.arrow',true).first();
26249 //arrow.set(align[2],
26251 this.el.addClass(placement);
26253 this.el.addClass('in fade');
26255 this.hoverState = null;
26257 if (this.el.hasClass('fade')) {
26268 //this.el.setXY([0,0]);
26269 this.el.removeClass('in');
26285 * @class Roo.bootstrap.LocationPicker
26286 * @extends Roo.bootstrap.Component
26287 * Bootstrap LocationPicker class
26288 * @cfg {Number} latitude Position when init default 0
26289 * @cfg {Number} longitude Position when init default 0
26290 * @cfg {Number} zoom default 15
26291 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26292 * @cfg {Boolean} mapTypeControl default false
26293 * @cfg {Boolean} disableDoubleClickZoom default false
26294 * @cfg {Boolean} scrollwheel default true
26295 * @cfg {Boolean} streetViewControl default false
26296 * @cfg {Number} radius default 0
26297 * @cfg {String} locationName
26298 * @cfg {Boolean} draggable default true
26299 * @cfg {Boolean} enableAutocomplete default false
26300 * @cfg {Boolean} enableReverseGeocode default true
26301 * @cfg {String} markerTitle
26304 * Create a new LocationPicker
26305 * @param {Object} config The config object
26309 Roo.bootstrap.LocationPicker = function(config){
26311 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26316 * Fires when the picker initialized.
26317 * @param {Roo.bootstrap.LocationPicker} this
26318 * @param {Google Location} location
26322 * @event positionchanged
26323 * Fires when the picker position changed.
26324 * @param {Roo.bootstrap.LocationPicker} this
26325 * @param {Google Location} location
26327 positionchanged : true,
26330 * Fires when the map resize.
26331 * @param {Roo.bootstrap.LocationPicker} this
26336 * Fires when the map show.
26337 * @param {Roo.bootstrap.LocationPicker} this
26342 * Fires when the map hide.
26343 * @param {Roo.bootstrap.LocationPicker} this
26348 * Fires when click the map.
26349 * @param {Roo.bootstrap.LocationPicker} this
26350 * @param {Map event} e
26354 * @event mapRightClick
26355 * Fires when right click the map.
26356 * @param {Roo.bootstrap.LocationPicker} this
26357 * @param {Map event} e
26359 mapRightClick : true,
26361 * @event markerClick
26362 * Fires when click the marker.
26363 * @param {Roo.bootstrap.LocationPicker} this
26364 * @param {Map event} e
26366 markerClick : true,
26368 * @event markerRightClick
26369 * Fires when right click the marker.
26370 * @param {Roo.bootstrap.LocationPicker} this
26371 * @param {Map event} e
26373 markerRightClick : true,
26375 * @event OverlayViewDraw
26376 * Fires when OverlayView Draw
26377 * @param {Roo.bootstrap.LocationPicker} this
26379 OverlayViewDraw : true,
26381 * @event OverlayViewOnAdd
26382 * Fires when OverlayView Draw
26383 * @param {Roo.bootstrap.LocationPicker} this
26385 OverlayViewOnAdd : true,
26387 * @event OverlayViewOnRemove
26388 * Fires when OverlayView Draw
26389 * @param {Roo.bootstrap.LocationPicker} this
26391 OverlayViewOnRemove : true,
26393 * @event OverlayViewShow
26394 * Fires when OverlayView Draw
26395 * @param {Roo.bootstrap.LocationPicker} this
26396 * @param {Pixel} cpx
26398 OverlayViewShow : true,
26400 * @event OverlayViewHide
26401 * Fires when OverlayView Draw
26402 * @param {Roo.bootstrap.LocationPicker} this
26404 OverlayViewHide : true,
26406 * @event loadexception
26407 * Fires when load google lib failed.
26408 * @param {Roo.bootstrap.LocationPicker} this
26410 loadexception : true
26415 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26417 gMapContext: false,
26423 mapTypeControl: false,
26424 disableDoubleClickZoom: false,
26426 streetViewControl: false,
26430 enableAutocomplete: false,
26431 enableReverseGeocode: true,
26434 getAutoCreate: function()
26439 cls: 'roo-location-picker'
26445 initEvents: function(ct, position)
26447 if(!this.el.getWidth() || this.isApplied()){
26451 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26456 initial: function()
26458 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26459 this.fireEvent('loadexception', this);
26463 if(!this.mapTypeId){
26464 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26467 this.gMapContext = this.GMapContext();
26469 this.initOverlayView();
26471 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26475 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26476 _this.setPosition(_this.gMapContext.marker.position);
26479 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26480 _this.fireEvent('mapClick', this, event);
26484 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26485 _this.fireEvent('mapRightClick', this, event);
26489 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26490 _this.fireEvent('markerClick', this, event);
26494 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26495 _this.fireEvent('markerRightClick', this, event);
26499 this.setPosition(this.gMapContext.location);
26501 this.fireEvent('initial', this, this.gMapContext.location);
26504 initOverlayView: function()
26508 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26512 _this.fireEvent('OverlayViewDraw', _this);
26517 _this.fireEvent('OverlayViewOnAdd', _this);
26520 onRemove: function()
26522 _this.fireEvent('OverlayViewOnRemove', _this);
26525 show: function(cpx)
26527 _this.fireEvent('OverlayViewShow', _this, cpx);
26532 _this.fireEvent('OverlayViewHide', _this);
26538 fromLatLngToContainerPixel: function(event)
26540 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26543 isApplied: function()
26545 return this.getGmapContext() == false ? false : true;
26548 getGmapContext: function()
26550 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26553 GMapContext: function()
26555 var position = new google.maps.LatLng(this.latitude, this.longitude);
26557 var _map = new google.maps.Map(this.el.dom, {
26560 mapTypeId: this.mapTypeId,
26561 mapTypeControl: this.mapTypeControl,
26562 disableDoubleClickZoom: this.disableDoubleClickZoom,
26563 scrollwheel: this.scrollwheel,
26564 streetViewControl: this.streetViewControl,
26565 locationName: this.locationName,
26566 draggable: this.draggable,
26567 enableAutocomplete: this.enableAutocomplete,
26568 enableReverseGeocode: this.enableReverseGeocode
26571 var _marker = new google.maps.Marker({
26572 position: position,
26574 title: this.markerTitle,
26575 draggable: this.draggable
26582 location: position,
26583 radius: this.radius,
26584 locationName: this.locationName,
26585 addressComponents: {
26586 formatted_address: null,
26587 addressLine1: null,
26588 addressLine2: null,
26590 streetNumber: null,
26594 stateOrProvince: null
26597 domContainer: this.el.dom,
26598 geodecoder: new google.maps.Geocoder()
26602 drawCircle: function(center, radius, options)
26604 if (this.gMapContext.circle != null) {
26605 this.gMapContext.circle.setMap(null);
26609 options = Roo.apply({}, options, {
26610 strokeColor: "#0000FF",
26611 strokeOpacity: .35,
26613 fillColor: "#0000FF",
26617 options.map = this.gMapContext.map;
26618 options.radius = radius;
26619 options.center = center;
26620 this.gMapContext.circle = new google.maps.Circle(options);
26621 return this.gMapContext.circle;
26627 setPosition: function(location)
26629 this.gMapContext.location = location;
26630 this.gMapContext.marker.setPosition(location);
26631 this.gMapContext.map.panTo(location);
26632 this.drawCircle(location, this.gMapContext.radius, {});
26636 if (this.gMapContext.settings.enableReverseGeocode) {
26637 this.gMapContext.geodecoder.geocode({
26638 latLng: this.gMapContext.location
26639 }, function(results, status) {
26641 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26642 _this.gMapContext.locationName = results[0].formatted_address;
26643 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26645 _this.fireEvent('positionchanged', this, location);
26652 this.fireEvent('positionchanged', this, location);
26657 google.maps.event.trigger(this.gMapContext.map, "resize");
26659 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26661 this.fireEvent('resize', this);
26664 setPositionByLatLng: function(latitude, longitude)
26666 this.setPosition(new google.maps.LatLng(latitude, longitude));
26669 getCurrentPosition: function()
26672 latitude: this.gMapContext.location.lat(),
26673 longitude: this.gMapContext.location.lng()
26677 getAddressName: function()
26679 return this.gMapContext.locationName;
26682 getAddressComponents: function()
26684 return this.gMapContext.addressComponents;
26687 address_component_from_google_geocode: function(address_components)
26691 for (var i = 0; i < address_components.length; i++) {
26692 var component = address_components[i];
26693 if (component.types.indexOf("postal_code") >= 0) {
26694 result.postalCode = component.short_name;
26695 } else if (component.types.indexOf("street_number") >= 0) {
26696 result.streetNumber = component.short_name;
26697 } else if (component.types.indexOf("route") >= 0) {
26698 result.streetName = component.short_name;
26699 } else if (component.types.indexOf("neighborhood") >= 0) {
26700 result.city = component.short_name;
26701 } else if (component.types.indexOf("locality") >= 0) {
26702 result.city = component.short_name;
26703 } else if (component.types.indexOf("sublocality") >= 0) {
26704 result.district = component.short_name;
26705 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26706 result.stateOrProvince = component.short_name;
26707 } else if (component.types.indexOf("country") >= 0) {
26708 result.country = component.short_name;
26712 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26713 result.addressLine2 = "";
26717 setZoomLevel: function(zoom)
26719 this.gMapContext.map.setZoom(zoom);
26732 this.fireEvent('show', this);
26743 this.fireEvent('hide', this);
26748 Roo.apply(Roo.bootstrap.LocationPicker, {
26750 OverlayView : function(map, options)
26752 options = options || {};
26766 * @class Roo.bootstrap.Alert
26767 * @extends Roo.bootstrap.Component
26768 * Bootstrap Alert class
26769 * @cfg {String} title The title of alert
26770 * @cfg {String} html The content of alert
26771 * @cfg {String} weight ( success | info | warning | danger )
26772 * @cfg {String} faicon font-awesomeicon
26775 * Create a new alert
26776 * @param {Object} config The config object
26780 Roo.bootstrap.Alert = function(config){
26781 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26785 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26792 getAutoCreate : function()
26801 cls : 'roo-alert-icon'
26806 cls : 'roo-alert-title',
26811 cls : 'roo-alert-text',
26818 cfg.cn[0].cls += ' fa ' + this.faicon;
26822 cfg.cls += ' alert-' + this.weight;
26828 initEvents: function()
26830 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26833 setTitle : function(str)
26835 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26838 setText : function(str)
26840 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26843 setWeight : function(weight)
26846 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26849 this.weight = weight;
26851 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26854 setIcon : function(icon)
26857 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26860 this.faicon = icon;
26862 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26883 * @class Roo.bootstrap.UploadCropbox
26884 * @extends Roo.bootstrap.Component
26885 * Bootstrap UploadCropbox class
26886 * @cfg {String} emptyText show when image has been loaded
26887 * @cfg {String} rotateNotify show when image too small to rotate
26888 * @cfg {Number} errorTimeout default 3000
26889 * @cfg {Number} minWidth default 300
26890 * @cfg {Number} minHeight default 300
26891 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26892 * @cfg {Boolean} isDocument (true|false) default false
26893 * @cfg {String} url action url
26894 * @cfg {String} paramName default 'imageUpload'
26895 * @cfg {String} method default POST
26896 * @cfg {Boolean} loadMask (true|false) default true
26897 * @cfg {Boolean} loadingText default 'Loading...'
26900 * Create a new UploadCropbox
26901 * @param {Object} config The config object
26904 Roo.bootstrap.UploadCropbox = function(config){
26905 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26909 * @event beforeselectfile
26910 * Fire before select file
26911 * @param {Roo.bootstrap.UploadCropbox} this
26913 "beforeselectfile" : true,
26916 * Fire after initEvent
26917 * @param {Roo.bootstrap.UploadCropbox} this
26922 * Fire after initEvent
26923 * @param {Roo.bootstrap.UploadCropbox} this
26924 * @param {String} data
26929 * Fire when preparing the file data
26930 * @param {Roo.bootstrap.UploadCropbox} this
26931 * @param {Object} file
26936 * Fire when get exception
26937 * @param {Roo.bootstrap.UploadCropbox} this
26938 * @param {XMLHttpRequest} xhr
26940 "exception" : true,
26942 * @event beforeloadcanvas
26943 * Fire before load the canvas
26944 * @param {Roo.bootstrap.UploadCropbox} this
26945 * @param {String} src
26947 "beforeloadcanvas" : true,
26950 * Fire when trash image
26951 * @param {Roo.bootstrap.UploadCropbox} this
26956 * Fire when download the image
26957 * @param {Roo.bootstrap.UploadCropbox} this
26961 * @event footerbuttonclick
26962 * Fire when footerbuttonclick
26963 * @param {Roo.bootstrap.UploadCropbox} this
26964 * @param {String} type
26966 "footerbuttonclick" : true,
26970 * @param {Roo.bootstrap.UploadCropbox} this
26975 * Fire when rotate the image
26976 * @param {Roo.bootstrap.UploadCropbox} this
26977 * @param {String} pos
26982 * Fire when inspect the file
26983 * @param {Roo.bootstrap.UploadCropbox} this
26984 * @param {Object} file
26989 * Fire when xhr upload the file
26990 * @param {Roo.bootstrap.UploadCropbox} this
26991 * @param {Object} data
26996 * Fire when arrange the file data
26997 * @param {Roo.bootstrap.UploadCropbox} this
26998 * @param {Object} formData
27003 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27006 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27008 emptyText : 'Click to upload image',
27009 rotateNotify : 'Image is too small to rotate',
27010 errorTimeout : 3000,
27024 cropType : 'image/jpeg',
27026 canvasLoaded : false,
27027 isDocument : false,
27029 paramName : 'imageUpload',
27031 loadingText : 'Loading...',
27034 getAutoCreate : function()
27038 cls : 'roo-upload-cropbox',
27042 cls : 'roo-upload-cropbox-selector',
27047 cls : 'roo-upload-cropbox-body',
27048 style : 'cursor:pointer',
27052 cls : 'roo-upload-cropbox-preview'
27056 cls : 'roo-upload-cropbox-thumb'
27060 cls : 'roo-upload-cropbox-empty-notify',
27061 html : this.emptyText
27065 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27066 html : this.rotateNotify
27072 cls : 'roo-upload-cropbox-footer',
27075 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27085 onRender : function(ct, position)
27087 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27089 if (this.buttons.length) {
27091 Roo.each(this.buttons, function(bb) {
27093 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27095 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27101 this.maskEl = this.el;
27105 initEvents : function()
27107 this.urlAPI = (window.createObjectURL && window) ||
27108 (window.URL && URL.revokeObjectURL && URL) ||
27109 (window.webkitURL && webkitURL);
27111 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27112 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27114 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27115 this.selectorEl.hide();
27117 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27118 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27120 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27121 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27122 this.thumbEl.hide();
27124 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27125 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27127 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27128 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27129 this.errorEl.hide();
27131 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27132 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27133 this.footerEl.hide();
27135 this.setThumbBoxSize();
27141 this.fireEvent('initial', this);
27148 window.addEventListener("resize", function() { _this.resize(); } );
27150 this.bodyEl.on('click', this.beforeSelectFile, this);
27153 this.bodyEl.on('touchstart', this.onTouchStart, this);
27154 this.bodyEl.on('touchmove', this.onTouchMove, this);
27155 this.bodyEl.on('touchend', this.onTouchEnd, this);
27159 this.bodyEl.on('mousedown', this.onMouseDown, this);
27160 this.bodyEl.on('mousemove', this.onMouseMove, this);
27161 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27162 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27163 Roo.get(document).on('mouseup', this.onMouseUp, this);
27166 this.selectorEl.on('change', this.onFileSelected, this);
27172 this.baseScale = 1;
27174 this.baseRotate = 1;
27175 this.dragable = false;
27176 this.pinching = false;
27179 this.cropData = false;
27180 this.notifyEl.dom.innerHTML = this.emptyText;
27182 this.selectorEl.dom.value = '';
27186 resize : function()
27188 if(this.fireEvent('resize', this) != false){
27189 this.setThumbBoxPosition();
27190 this.setCanvasPosition();
27194 onFooterButtonClick : function(e, el, o, type)
27197 case 'rotate-left' :
27198 this.onRotateLeft(e);
27200 case 'rotate-right' :
27201 this.onRotateRight(e);
27204 this.beforeSelectFile(e);
27219 this.fireEvent('footerbuttonclick', this, type);
27222 beforeSelectFile : function(e)
27224 e.preventDefault();
27226 if(this.fireEvent('beforeselectfile', this) != false){
27227 this.selectorEl.dom.click();
27231 onFileSelected : function(e)
27233 e.preventDefault();
27235 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27239 var file = this.selectorEl.dom.files[0];
27241 if(this.fireEvent('inspect', this, file) != false){
27242 this.prepare(file);
27247 trash : function(e)
27249 this.fireEvent('trash', this);
27252 download : function(e)
27254 this.fireEvent('download', this);
27257 loadCanvas : function(src)
27259 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27263 this.imageEl = document.createElement('img');
27267 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27269 this.imageEl.src = src;
27273 onLoadCanvas : function()
27275 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27276 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27278 this.bodyEl.un('click', this.beforeSelectFile, this);
27280 this.notifyEl.hide();
27281 this.thumbEl.show();
27282 this.footerEl.show();
27284 this.baseRotateLevel();
27286 if(this.isDocument){
27287 this.setThumbBoxSize();
27290 this.setThumbBoxPosition();
27292 this.baseScaleLevel();
27298 this.canvasLoaded = true;
27301 this.maskEl.unmask();
27306 setCanvasPosition : function()
27308 if(!this.canvasEl){
27312 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27313 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27315 this.previewEl.setLeft(pw);
27316 this.previewEl.setTop(ph);
27320 onMouseDown : function(e)
27324 this.dragable = true;
27325 this.pinching = false;
27327 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27328 this.dragable = false;
27332 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27333 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27337 onMouseMove : function(e)
27341 if(!this.canvasLoaded){
27345 if (!this.dragable){
27349 var minX = Math.ceil(this.thumbEl.getLeft(true));
27350 var minY = Math.ceil(this.thumbEl.getTop(true));
27352 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27353 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27355 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27356 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27358 x = x - this.mouseX;
27359 y = y - this.mouseY;
27361 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27362 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27364 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27365 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27367 this.previewEl.setLeft(bgX);
27368 this.previewEl.setTop(bgY);
27370 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27371 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27374 onMouseUp : function(e)
27378 this.dragable = false;
27381 onMouseWheel : function(e)
27385 this.startScale = this.scale;
27387 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27389 if(!this.zoomable()){
27390 this.scale = this.startScale;
27399 zoomable : function()
27401 var minScale = this.thumbEl.getWidth() / this.minWidth;
27403 if(this.minWidth < this.minHeight){
27404 minScale = this.thumbEl.getHeight() / this.minHeight;
27407 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27408 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27412 (this.rotate == 0 || this.rotate == 180) &&
27414 width > this.imageEl.OriginWidth ||
27415 height > this.imageEl.OriginHeight ||
27416 (width < this.minWidth && height < this.minHeight)
27424 (this.rotate == 90 || this.rotate == 270) &&
27426 width > this.imageEl.OriginWidth ||
27427 height > this.imageEl.OriginHeight ||
27428 (width < this.minHeight && height < this.minWidth)
27435 !this.isDocument &&
27436 (this.rotate == 0 || this.rotate == 180) &&
27438 width < this.minWidth ||
27439 width > this.imageEl.OriginWidth ||
27440 height < this.minHeight ||
27441 height > this.imageEl.OriginHeight
27448 !this.isDocument &&
27449 (this.rotate == 90 || this.rotate == 270) &&
27451 width < this.minHeight ||
27452 width > this.imageEl.OriginWidth ||
27453 height < this.minWidth ||
27454 height > this.imageEl.OriginHeight
27464 onRotateLeft : function(e)
27466 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27468 var minScale = this.thumbEl.getWidth() / this.minWidth;
27470 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27471 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27473 this.startScale = this.scale;
27475 while (this.getScaleLevel() < minScale){
27477 this.scale = this.scale + 1;
27479 if(!this.zoomable()){
27484 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27485 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27490 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27497 this.scale = this.startScale;
27499 this.onRotateFail();
27504 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27506 if(this.isDocument){
27507 this.setThumbBoxSize();
27508 this.setThumbBoxPosition();
27509 this.setCanvasPosition();
27514 this.fireEvent('rotate', this, 'left');
27518 onRotateRight : function(e)
27520 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27522 var minScale = this.thumbEl.getWidth() / this.minWidth;
27524 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27525 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27527 this.startScale = this.scale;
27529 while (this.getScaleLevel() < minScale){
27531 this.scale = this.scale + 1;
27533 if(!this.zoomable()){
27538 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27539 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27544 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27551 this.scale = this.startScale;
27553 this.onRotateFail();
27558 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27560 if(this.isDocument){
27561 this.setThumbBoxSize();
27562 this.setThumbBoxPosition();
27563 this.setCanvasPosition();
27568 this.fireEvent('rotate', this, 'right');
27571 onRotateFail : function()
27573 this.errorEl.show(true);
27577 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27582 this.previewEl.dom.innerHTML = '';
27584 var canvasEl = document.createElement("canvas");
27586 var contextEl = canvasEl.getContext("2d");
27588 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27589 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27590 var center = this.imageEl.OriginWidth / 2;
27592 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27593 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27594 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27595 center = this.imageEl.OriginHeight / 2;
27598 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27600 contextEl.translate(center, center);
27601 contextEl.rotate(this.rotate * Math.PI / 180);
27603 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27605 this.canvasEl = document.createElement("canvas");
27607 this.contextEl = this.canvasEl.getContext("2d");
27609 switch (this.rotate) {
27612 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27613 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27615 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27620 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27621 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27623 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27624 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);
27628 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27633 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27634 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27636 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27637 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);
27641 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);
27646 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27647 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27649 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27650 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27654 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);
27661 this.previewEl.appendChild(this.canvasEl);
27663 this.setCanvasPosition();
27668 if(!this.canvasLoaded){
27672 var imageCanvas = document.createElement("canvas");
27674 var imageContext = imageCanvas.getContext("2d");
27676 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27677 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27679 var center = imageCanvas.width / 2;
27681 imageContext.translate(center, center);
27683 imageContext.rotate(this.rotate * Math.PI / 180);
27685 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27687 var canvas = document.createElement("canvas");
27689 var context = canvas.getContext("2d");
27691 canvas.width = this.minWidth;
27692 canvas.height = this.minHeight;
27694 switch (this.rotate) {
27697 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27698 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27700 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27701 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27703 var targetWidth = this.minWidth - 2 * x;
27704 var targetHeight = this.minHeight - 2 * y;
27708 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27709 scale = targetWidth / width;
27712 if(x > 0 && y == 0){
27713 scale = targetHeight / height;
27716 if(x > 0 && y > 0){
27717 scale = targetWidth / width;
27719 if(width < height){
27720 scale = targetHeight / height;
27724 context.scale(scale, scale);
27726 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27727 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27729 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27730 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27732 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27737 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27738 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27740 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27741 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27743 var targetWidth = this.minWidth - 2 * x;
27744 var targetHeight = this.minHeight - 2 * y;
27748 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27749 scale = targetWidth / width;
27752 if(x > 0 && y == 0){
27753 scale = targetHeight / height;
27756 if(x > 0 && y > 0){
27757 scale = targetWidth / width;
27759 if(width < height){
27760 scale = targetHeight / height;
27764 context.scale(scale, scale);
27766 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27767 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27769 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27770 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27772 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27774 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27779 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27780 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27782 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27783 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27785 var targetWidth = this.minWidth - 2 * x;
27786 var targetHeight = this.minHeight - 2 * y;
27790 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27791 scale = targetWidth / width;
27794 if(x > 0 && y == 0){
27795 scale = targetHeight / height;
27798 if(x > 0 && y > 0){
27799 scale = targetWidth / width;
27801 if(width < height){
27802 scale = targetHeight / height;
27806 context.scale(scale, scale);
27808 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27809 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27811 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27812 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27814 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27815 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27817 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27822 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27823 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27825 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27826 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27828 var targetWidth = this.minWidth - 2 * x;
27829 var targetHeight = this.minHeight - 2 * y;
27833 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27834 scale = targetWidth / width;
27837 if(x > 0 && y == 0){
27838 scale = targetHeight / height;
27841 if(x > 0 && y > 0){
27842 scale = targetWidth / width;
27844 if(width < height){
27845 scale = targetHeight / height;
27849 context.scale(scale, scale);
27851 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27852 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27854 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27855 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27857 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27859 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27866 this.cropData = canvas.toDataURL(this.cropType);
27868 if(this.fireEvent('crop', this, this.cropData) !== false){
27869 this.process(this.file, this.cropData);
27876 setThumbBoxSize : function()
27880 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27881 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27882 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27884 this.minWidth = width;
27885 this.minHeight = height;
27887 if(this.rotate == 90 || this.rotate == 270){
27888 this.minWidth = height;
27889 this.minHeight = width;
27894 width = Math.ceil(this.minWidth * height / this.minHeight);
27896 if(this.minWidth > this.minHeight){
27898 height = Math.ceil(this.minHeight * width / this.minWidth);
27901 this.thumbEl.setStyle({
27902 width : width + 'px',
27903 height : height + 'px'
27910 setThumbBoxPosition : function()
27912 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27913 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27915 this.thumbEl.setLeft(x);
27916 this.thumbEl.setTop(y);
27920 baseRotateLevel : function()
27922 this.baseRotate = 1;
27925 typeof(this.exif) != 'undefined' &&
27926 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27927 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27929 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27932 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27936 baseScaleLevel : function()
27940 if(this.isDocument){
27942 if(this.baseRotate == 6 || this.baseRotate == 8){
27944 height = this.thumbEl.getHeight();
27945 this.baseScale = height / this.imageEl.OriginWidth;
27947 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27948 width = this.thumbEl.getWidth();
27949 this.baseScale = width / this.imageEl.OriginHeight;
27955 height = this.thumbEl.getHeight();
27956 this.baseScale = height / this.imageEl.OriginHeight;
27958 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27959 width = this.thumbEl.getWidth();
27960 this.baseScale = width / this.imageEl.OriginWidth;
27966 if(this.baseRotate == 6 || this.baseRotate == 8){
27968 width = this.thumbEl.getHeight();
27969 this.baseScale = width / this.imageEl.OriginHeight;
27971 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27972 height = this.thumbEl.getWidth();
27973 this.baseScale = height / this.imageEl.OriginHeight;
27976 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27977 height = this.thumbEl.getWidth();
27978 this.baseScale = height / this.imageEl.OriginHeight;
27980 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27981 width = this.thumbEl.getHeight();
27982 this.baseScale = width / this.imageEl.OriginWidth;
27989 width = this.thumbEl.getWidth();
27990 this.baseScale = width / this.imageEl.OriginWidth;
27992 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27993 height = this.thumbEl.getHeight();
27994 this.baseScale = height / this.imageEl.OriginHeight;
27997 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27999 height = this.thumbEl.getHeight();
28000 this.baseScale = height / this.imageEl.OriginHeight;
28002 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28003 width = this.thumbEl.getWidth();
28004 this.baseScale = width / this.imageEl.OriginWidth;
28012 getScaleLevel : function()
28014 return this.baseScale * Math.pow(1.1, this.scale);
28017 onTouchStart : function(e)
28019 if(!this.canvasLoaded){
28020 this.beforeSelectFile(e);
28024 var touches = e.browserEvent.touches;
28030 if(touches.length == 1){
28031 this.onMouseDown(e);
28035 if(touches.length != 2){
28041 for(var i = 0, finger; finger = touches[i]; i++){
28042 coords.push(finger.pageX, finger.pageY);
28045 var x = Math.pow(coords[0] - coords[2], 2);
28046 var y = Math.pow(coords[1] - coords[3], 2);
28048 this.startDistance = Math.sqrt(x + y);
28050 this.startScale = this.scale;
28052 this.pinching = true;
28053 this.dragable = false;
28057 onTouchMove : function(e)
28059 if(!this.pinching && !this.dragable){
28063 var touches = e.browserEvent.touches;
28070 this.onMouseMove(e);
28076 for(var i = 0, finger; finger = touches[i]; i++){
28077 coords.push(finger.pageX, finger.pageY);
28080 var x = Math.pow(coords[0] - coords[2], 2);
28081 var y = Math.pow(coords[1] - coords[3], 2);
28083 this.endDistance = Math.sqrt(x + y);
28085 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28087 if(!this.zoomable()){
28088 this.scale = this.startScale;
28096 onTouchEnd : function(e)
28098 this.pinching = false;
28099 this.dragable = false;
28103 process : function(file, crop)
28106 this.maskEl.mask(this.loadingText);
28109 this.xhr = new XMLHttpRequest();
28111 file.xhr = this.xhr;
28113 this.xhr.open(this.method, this.url, true);
28116 "Accept": "application/json",
28117 "Cache-Control": "no-cache",
28118 "X-Requested-With": "XMLHttpRequest"
28121 for (var headerName in headers) {
28122 var headerValue = headers[headerName];
28124 this.xhr.setRequestHeader(headerName, headerValue);
28130 this.xhr.onload = function()
28132 _this.xhrOnLoad(_this.xhr);
28135 this.xhr.onerror = function()
28137 _this.xhrOnError(_this.xhr);
28140 var formData = new FormData();
28142 formData.append('returnHTML', 'NO');
28145 formData.append('crop', crop);
28148 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28149 formData.append(this.paramName, file, file.name);
28152 if(typeof(file.filename) != 'undefined'){
28153 formData.append('filename', file.filename);
28156 if(typeof(file.mimetype) != 'undefined'){
28157 formData.append('mimetype', file.mimetype);
28160 if(this.fireEvent('arrange', this, formData) != false){
28161 this.xhr.send(formData);
28165 xhrOnLoad : function(xhr)
28168 this.maskEl.unmask();
28171 if (xhr.readyState !== 4) {
28172 this.fireEvent('exception', this, xhr);
28176 var response = Roo.decode(xhr.responseText);
28178 if(!response.success){
28179 this.fireEvent('exception', this, xhr);
28183 var response = Roo.decode(xhr.responseText);
28185 this.fireEvent('upload', this, response);
28189 xhrOnError : function()
28192 this.maskEl.unmask();
28195 Roo.log('xhr on error');
28197 var response = Roo.decode(xhr.responseText);
28203 prepare : function(file)
28206 this.maskEl.mask(this.loadingText);
28212 if(typeof(file) === 'string'){
28213 this.loadCanvas(file);
28217 if(!file || !this.urlAPI){
28222 this.cropType = file.type;
28226 if(this.fireEvent('prepare', this, this.file) != false){
28228 var reader = new FileReader();
28230 reader.onload = function (e) {
28231 if (e.target.error) {
28232 Roo.log(e.target.error);
28236 var buffer = e.target.result,
28237 dataView = new DataView(buffer),
28239 maxOffset = dataView.byteLength - 4,
28243 if (dataView.getUint16(0) === 0xffd8) {
28244 while (offset < maxOffset) {
28245 markerBytes = dataView.getUint16(offset);
28247 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28248 markerLength = dataView.getUint16(offset + 2) + 2;
28249 if (offset + markerLength > dataView.byteLength) {
28250 Roo.log('Invalid meta data: Invalid segment size.');
28254 if(markerBytes == 0xffe1){
28255 _this.parseExifData(
28262 offset += markerLength;
28272 var url = _this.urlAPI.createObjectURL(_this.file);
28274 _this.loadCanvas(url);
28279 reader.readAsArrayBuffer(this.file);
28285 parseExifData : function(dataView, offset, length)
28287 var tiffOffset = offset + 10,
28291 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28292 // No Exif data, might be XMP data instead
28296 // Check for the ASCII code for "Exif" (0x45786966):
28297 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28298 // No Exif data, might be XMP data instead
28301 if (tiffOffset + 8 > dataView.byteLength) {
28302 Roo.log('Invalid Exif data: Invalid segment size.');
28305 // Check for the two null bytes:
28306 if (dataView.getUint16(offset + 8) !== 0x0000) {
28307 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28310 // Check the byte alignment:
28311 switch (dataView.getUint16(tiffOffset)) {
28313 littleEndian = true;
28316 littleEndian = false;
28319 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28322 // Check for the TIFF tag marker (0x002A):
28323 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28324 Roo.log('Invalid Exif data: Missing TIFF marker.');
28327 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28328 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28330 this.parseExifTags(
28333 tiffOffset + dirOffset,
28338 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28343 if (dirOffset + 6 > dataView.byteLength) {
28344 Roo.log('Invalid Exif data: Invalid directory offset.');
28347 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28348 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28349 if (dirEndOffset + 4 > dataView.byteLength) {
28350 Roo.log('Invalid Exif data: Invalid directory size.');
28353 for (i = 0; i < tagsNumber; i += 1) {
28357 dirOffset + 2 + 12 * i, // tag offset
28361 // Return the offset to the next directory:
28362 return dataView.getUint32(dirEndOffset, littleEndian);
28365 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28367 var tag = dataView.getUint16(offset, littleEndian);
28369 this.exif[tag] = this.getExifValue(
28373 dataView.getUint16(offset + 2, littleEndian), // tag type
28374 dataView.getUint32(offset + 4, littleEndian), // tag length
28379 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28381 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28390 Roo.log('Invalid Exif data: Invalid tag type.');
28394 tagSize = tagType.size * length;
28395 // Determine if the value is contained in the dataOffset bytes,
28396 // or if the value at the dataOffset is a pointer to the actual data:
28397 dataOffset = tagSize > 4 ?
28398 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28399 if (dataOffset + tagSize > dataView.byteLength) {
28400 Roo.log('Invalid Exif data: Invalid data offset.');
28403 if (length === 1) {
28404 return tagType.getValue(dataView, dataOffset, littleEndian);
28407 for (i = 0; i < length; i += 1) {
28408 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28411 if (tagType.ascii) {
28413 // Concatenate the chars:
28414 for (i = 0; i < values.length; i += 1) {
28416 // Ignore the terminating NULL byte(s):
28417 if (c === '\u0000') {
28429 Roo.apply(Roo.bootstrap.UploadCropbox, {
28431 'Orientation': 0x0112
28435 1: 0, //'top-left',
28437 3: 180, //'bottom-right',
28438 // 4: 'bottom-left',
28440 6: 90, //'right-top',
28441 // 7: 'right-bottom',
28442 8: 270 //'left-bottom'
28446 // byte, 8-bit unsigned int:
28448 getValue: function (dataView, dataOffset) {
28449 return dataView.getUint8(dataOffset);
28453 // ascii, 8-bit byte:
28455 getValue: function (dataView, dataOffset) {
28456 return String.fromCharCode(dataView.getUint8(dataOffset));
28461 // short, 16 bit int:
28463 getValue: function (dataView, dataOffset, littleEndian) {
28464 return dataView.getUint16(dataOffset, littleEndian);
28468 // long, 32 bit int:
28470 getValue: function (dataView, dataOffset, littleEndian) {
28471 return dataView.getUint32(dataOffset, littleEndian);
28475 // rational = two long values, first is numerator, second is denominator:
28477 getValue: function (dataView, dataOffset, littleEndian) {
28478 return dataView.getUint32(dataOffset, littleEndian) /
28479 dataView.getUint32(dataOffset + 4, littleEndian);
28483 // slong, 32 bit signed int:
28485 getValue: function (dataView, dataOffset, littleEndian) {
28486 return dataView.getInt32(dataOffset, littleEndian);
28490 // srational, two slongs, first is numerator, second is denominator:
28492 getValue: function (dataView, dataOffset, littleEndian) {
28493 return dataView.getInt32(dataOffset, littleEndian) /
28494 dataView.getInt32(dataOffset + 4, littleEndian);
28504 cls : 'btn-group roo-upload-cropbox-rotate-left',
28505 action : 'rotate-left',
28509 cls : 'btn btn-default',
28510 html : '<i class="fa fa-undo"></i>'
28516 cls : 'btn-group roo-upload-cropbox-picture',
28517 action : 'picture',
28521 cls : 'btn btn-default',
28522 html : '<i class="fa fa-picture-o"></i>'
28528 cls : 'btn-group roo-upload-cropbox-rotate-right',
28529 action : 'rotate-right',
28533 cls : 'btn btn-default',
28534 html : '<i class="fa fa-repeat"></i>'
28542 cls : 'btn-group roo-upload-cropbox-rotate-left',
28543 action : 'rotate-left',
28547 cls : 'btn btn-default',
28548 html : '<i class="fa fa-undo"></i>'
28554 cls : 'btn-group roo-upload-cropbox-download',
28555 action : 'download',
28559 cls : 'btn btn-default',
28560 html : '<i class="fa fa-download"></i>'
28566 cls : 'btn-group roo-upload-cropbox-crop',
28571 cls : 'btn btn-default',
28572 html : '<i class="fa fa-crop"></i>'
28578 cls : 'btn-group roo-upload-cropbox-trash',
28583 cls : 'btn btn-default',
28584 html : '<i class="fa fa-trash"></i>'
28590 cls : 'btn-group roo-upload-cropbox-rotate-right',
28591 action : 'rotate-right',
28595 cls : 'btn btn-default',
28596 html : '<i class="fa fa-repeat"></i>'
28604 cls : 'btn-group roo-upload-cropbox-rotate-left',
28605 action : 'rotate-left',
28609 cls : 'btn btn-default',
28610 html : '<i class="fa fa-undo"></i>'
28616 cls : 'btn-group roo-upload-cropbox-rotate-right',
28617 action : 'rotate-right',
28621 cls : 'btn btn-default',
28622 html : '<i class="fa fa-repeat"></i>'
28635 * @class Roo.bootstrap.DocumentManager
28636 * @extends Roo.bootstrap.Component
28637 * Bootstrap DocumentManager class
28638 * @cfg {String} paramName default 'imageUpload'
28639 * @cfg {String} toolTipName default 'filename'
28640 * @cfg {String} method default POST
28641 * @cfg {String} url action url
28642 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28643 * @cfg {Boolean} multiple multiple upload default true
28644 * @cfg {Number} thumbSize default 300
28645 * @cfg {String} fieldLabel
28646 * @cfg {Number} labelWidth default 4
28647 * @cfg {String} labelAlign (left|top) default left
28648 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28649 * @cfg {Number} labellg set the width of label (1-12)
28650 * @cfg {Number} labelmd set the width of label (1-12)
28651 * @cfg {Number} labelsm set the width of label (1-12)
28652 * @cfg {Number} labelxs set the width of label (1-12)
28655 * Create a new DocumentManager
28656 * @param {Object} config The config object
28659 Roo.bootstrap.DocumentManager = function(config){
28660 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28663 this.delegates = [];
28668 * Fire when initial the DocumentManager
28669 * @param {Roo.bootstrap.DocumentManager} this
28674 * inspect selected file
28675 * @param {Roo.bootstrap.DocumentManager} this
28676 * @param {File} file
28681 * Fire when xhr load exception
28682 * @param {Roo.bootstrap.DocumentManager} this
28683 * @param {XMLHttpRequest} xhr
28685 "exception" : true,
28687 * @event afterupload
28688 * Fire when xhr load exception
28689 * @param {Roo.bootstrap.DocumentManager} this
28690 * @param {XMLHttpRequest} xhr
28692 "afterupload" : true,
28695 * prepare the form data
28696 * @param {Roo.bootstrap.DocumentManager} this
28697 * @param {Object} formData
28702 * Fire when remove the file
28703 * @param {Roo.bootstrap.DocumentManager} this
28704 * @param {Object} file
28709 * Fire after refresh the file
28710 * @param {Roo.bootstrap.DocumentManager} this
28715 * Fire after click the image
28716 * @param {Roo.bootstrap.DocumentManager} this
28717 * @param {Object} file
28722 * Fire when upload a image and editable set to true
28723 * @param {Roo.bootstrap.DocumentManager} this
28724 * @param {Object} file
28728 * @event beforeselectfile
28729 * Fire before select file
28730 * @param {Roo.bootstrap.DocumentManager} this
28732 "beforeselectfile" : true,
28735 * Fire before process file
28736 * @param {Roo.bootstrap.DocumentManager} this
28737 * @param {Object} file
28741 * @event previewrendered
28742 * Fire when preview rendered
28743 * @param {Roo.bootstrap.DocumentManager} this
28744 * @param {Object} file
28746 "previewrendered" : true,
28749 "previewResize" : true
28754 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28763 paramName : 'imageUpload',
28764 toolTipName : 'filename',
28767 labelAlign : 'left',
28777 getAutoCreate : function()
28779 var managerWidget = {
28781 cls : 'roo-document-manager',
28785 cls : 'roo-document-manager-selector',
28790 cls : 'roo-document-manager-uploader',
28794 cls : 'roo-document-manager-upload-btn',
28795 html : '<i class="fa fa-plus"></i>'
28806 cls : 'column col-md-12',
28811 if(this.fieldLabel.length){
28816 cls : 'column col-md-12',
28817 html : this.fieldLabel
28821 cls : 'column col-md-12',
28826 if(this.labelAlign == 'left'){
28831 html : this.fieldLabel
28840 if(this.labelWidth > 12){
28841 content[0].style = "width: " + this.labelWidth + 'px';
28844 if(this.labelWidth < 13 && this.labelmd == 0){
28845 this.labelmd = this.labelWidth;
28848 if(this.labellg > 0){
28849 content[0].cls += ' col-lg-' + this.labellg;
28850 content[1].cls += ' col-lg-' + (12 - this.labellg);
28853 if(this.labelmd > 0){
28854 content[0].cls += ' col-md-' + this.labelmd;
28855 content[1].cls += ' col-md-' + (12 - this.labelmd);
28858 if(this.labelsm > 0){
28859 content[0].cls += ' col-sm-' + this.labelsm;
28860 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28863 if(this.labelxs > 0){
28864 content[0].cls += ' col-xs-' + this.labelxs;
28865 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28873 cls : 'row clearfix',
28881 initEvents : function()
28883 this.managerEl = this.el.select('.roo-document-manager', true).first();
28884 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28886 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28887 this.selectorEl.hide();
28890 this.selectorEl.attr('multiple', 'multiple');
28893 this.selectorEl.on('change', this.onFileSelected, this);
28895 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28896 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28898 this.uploader.on('click', this.onUploaderClick, this);
28900 this.renderProgressDialog();
28904 window.addEventListener("resize", function() { _this.refresh(); } );
28906 this.fireEvent('initial', this);
28909 renderProgressDialog : function()
28913 this.progressDialog = new Roo.bootstrap.Modal({
28914 cls : 'roo-document-manager-progress-dialog',
28915 allow_close : false,
28925 btnclick : function() {
28926 _this.uploadCancel();
28932 this.progressDialog.render(Roo.get(document.body));
28934 this.progress = new Roo.bootstrap.Progress({
28935 cls : 'roo-document-manager-progress',
28940 this.progress.render(this.progressDialog.getChildContainer());
28942 this.progressBar = new Roo.bootstrap.ProgressBar({
28943 cls : 'roo-document-manager-progress-bar',
28946 aria_valuemax : 12,
28950 this.progressBar.render(this.progress.getChildContainer());
28953 onUploaderClick : function(e)
28955 e.preventDefault();
28957 if(this.fireEvent('beforeselectfile', this) != false){
28958 this.selectorEl.dom.click();
28963 onFileSelected : function(e)
28965 e.preventDefault();
28967 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28971 Roo.each(this.selectorEl.dom.files, function(file){
28972 if(this.fireEvent('inspect', this, file) != false){
28973 this.files.push(file);
28983 this.selectorEl.dom.value = '';
28985 if(!this.files || !this.files.length){
28989 if(this.boxes > 0 && this.files.length > this.boxes){
28990 this.files = this.files.slice(0, this.boxes);
28993 this.uploader.show();
28995 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28996 this.uploader.hide();
29005 Roo.each(this.files, function(file){
29007 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29008 var f = this.renderPreview(file);
29013 if(file.type.indexOf('image') != -1){
29014 this.delegates.push(
29016 _this.process(file);
29017 }).createDelegate(this)
29025 _this.process(file);
29026 }).createDelegate(this)
29031 this.files = files;
29033 this.delegates = this.delegates.concat(docs);
29035 if(!this.delegates.length){
29040 this.progressBar.aria_valuemax = this.delegates.length;
29047 arrange : function()
29049 if(!this.delegates.length){
29050 this.progressDialog.hide();
29055 var delegate = this.delegates.shift();
29057 this.progressDialog.show();
29059 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29061 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29066 refresh : function()
29068 this.uploader.show();
29070 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29071 this.uploader.hide();
29074 Roo.isTouch ? this.closable(false) : this.closable(true);
29076 this.fireEvent('refresh', this);
29079 onRemove : function(e, el, o)
29081 e.preventDefault();
29083 this.fireEvent('remove', this, o);
29087 remove : function(o)
29091 Roo.each(this.files, function(file){
29092 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29101 this.files = files;
29108 Roo.each(this.files, function(file){
29113 file.target.remove();
29122 onClick : function(e, el, o)
29124 e.preventDefault();
29126 this.fireEvent('click', this, o);
29130 closable : function(closable)
29132 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29134 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29146 xhrOnLoad : function(xhr)
29148 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29152 if (xhr.readyState !== 4) {
29154 this.fireEvent('exception', this, xhr);
29158 var response = Roo.decode(xhr.responseText);
29160 if(!response.success){
29162 this.fireEvent('exception', this, xhr);
29166 var file = this.renderPreview(response.data);
29168 this.files.push(file);
29172 this.fireEvent('afterupload', this, xhr);
29176 xhrOnError : function(xhr)
29178 Roo.log('xhr on error');
29180 var response = Roo.decode(xhr.responseText);
29187 process : function(file)
29189 if(this.fireEvent('process', this, file) !== false){
29190 if(this.editable && file.type.indexOf('image') != -1){
29191 this.fireEvent('edit', this, file);
29195 this.uploadStart(file, false);
29202 uploadStart : function(file, crop)
29204 this.xhr = new XMLHttpRequest();
29206 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29211 file.xhr = this.xhr;
29213 this.managerEl.createChild({
29215 cls : 'roo-document-manager-loading',
29219 tooltip : file.name,
29220 cls : 'roo-document-manager-thumb',
29221 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29227 this.xhr.open(this.method, this.url, true);
29230 "Accept": "application/json",
29231 "Cache-Control": "no-cache",
29232 "X-Requested-With": "XMLHttpRequest"
29235 for (var headerName in headers) {
29236 var headerValue = headers[headerName];
29238 this.xhr.setRequestHeader(headerName, headerValue);
29244 this.xhr.onload = function()
29246 _this.xhrOnLoad(_this.xhr);
29249 this.xhr.onerror = function()
29251 _this.xhrOnError(_this.xhr);
29254 var formData = new FormData();
29256 formData.append('returnHTML', 'NO');
29259 formData.append('crop', crop);
29262 formData.append(this.paramName, file, file.name);
29269 if(this.fireEvent('prepare', this, formData, options) != false){
29271 if(options.manually){
29275 this.xhr.send(formData);
29279 this.uploadCancel();
29282 uploadCancel : function()
29288 this.delegates = [];
29290 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29297 renderPreview : function(file)
29299 if(typeof(file.target) != 'undefined' && file.target){
29303 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29305 var previewEl = this.managerEl.createChild({
29307 cls : 'roo-document-manager-preview',
29311 tooltip : file[this.toolTipName],
29312 cls : 'roo-document-manager-thumb',
29313 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29318 html : '<i class="fa fa-times-circle"></i>'
29323 var close = previewEl.select('button.close', true).first();
29325 close.on('click', this.onRemove, this, file);
29327 file.target = previewEl;
29329 var image = previewEl.select('img', true).first();
29333 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29335 image.on('click', this.onClick, this, file);
29337 this.fireEvent('previewrendered', this, file);
29343 onPreviewLoad : function(file, image)
29345 if(typeof(file.target) == 'undefined' || !file.target){
29349 var width = image.dom.naturalWidth || image.dom.width;
29350 var height = image.dom.naturalHeight || image.dom.height;
29352 if(!this.previewResize) {
29356 if(width > height){
29357 file.target.addClass('wide');
29361 file.target.addClass('tall');
29366 uploadFromSource : function(file, crop)
29368 this.xhr = new XMLHttpRequest();
29370 this.managerEl.createChild({
29372 cls : 'roo-document-manager-loading',
29376 tooltip : file.name,
29377 cls : 'roo-document-manager-thumb',
29378 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29384 this.xhr.open(this.method, this.url, true);
29387 "Accept": "application/json",
29388 "Cache-Control": "no-cache",
29389 "X-Requested-With": "XMLHttpRequest"
29392 for (var headerName in headers) {
29393 var headerValue = headers[headerName];
29395 this.xhr.setRequestHeader(headerName, headerValue);
29401 this.xhr.onload = function()
29403 _this.xhrOnLoad(_this.xhr);
29406 this.xhr.onerror = function()
29408 _this.xhrOnError(_this.xhr);
29411 var formData = new FormData();
29413 formData.append('returnHTML', 'NO');
29415 formData.append('crop', crop);
29417 if(typeof(file.filename) != 'undefined'){
29418 formData.append('filename', file.filename);
29421 if(typeof(file.mimetype) != 'undefined'){
29422 formData.append('mimetype', file.mimetype);
29427 if(this.fireEvent('prepare', this, formData) != false){
29428 this.xhr.send(formData);
29438 * @class Roo.bootstrap.DocumentViewer
29439 * @extends Roo.bootstrap.Component
29440 * Bootstrap DocumentViewer class
29441 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29442 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29445 * Create a new DocumentViewer
29446 * @param {Object} config The config object
29449 Roo.bootstrap.DocumentViewer = function(config){
29450 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29455 * Fire after initEvent
29456 * @param {Roo.bootstrap.DocumentViewer} this
29462 * @param {Roo.bootstrap.DocumentViewer} this
29467 * Fire after download button
29468 * @param {Roo.bootstrap.DocumentViewer} this
29473 * Fire after trash button
29474 * @param {Roo.bootstrap.DocumentViewer} this
29481 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29483 showDownload : true,
29487 getAutoCreate : function()
29491 cls : 'roo-document-viewer',
29495 cls : 'roo-document-viewer-body',
29499 cls : 'roo-document-viewer-thumb',
29503 cls : 'roo-document-viewer-image'
29511 cls : 'roo-document-viewer-footer',
29514 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29518 cls : 'btn-group roo-document-viewer-download',
29522 cls : 'btn btn-default',
29523 html : '<i class="fa fa-download"></i>'
29529 cls : 'btn-group roo-document-viewer-trash',
29533 cls : 'btn btn-default',
29534 html : '<i class="fa fa-trash"></i>'
29547 initEvents : function()
29549 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29550 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29552 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29553 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29555 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29556 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29558 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29559 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29561 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29562 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29564 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29565 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29567 this.bodyEl.on('click', this.onClick, this);
29568 this.downloadBtn.on('click', this.onDownload, this);
29569 this.trashBtn.on('click', this.onTrash, this);
29571 this.downloadBtn.hide();
29572 this.trashBtn.hide();
29574 if(this.showDownload){
29575 this.downloadBtn.show();
29578 if(this.showTrash){
29579 this.trashBtn.show();
29582 if(!this.showDownload && !this.showTrash) {
29583 this.footerEl.hide();
29588 initial : function()
29590 this.fireEvent('initial', this);
29594 onClick : function(e)
29596 e.preventDefault();
29598 this.fireEvent('click', this);
29601 onDownload : function(e)
29603 e.preventDefault();
29605 this.fireEvent('download', this);
29608 onTrash : function(e)
29610 e.preventDefault();
29612 this.fireEvent('trash', this);
29624 * @class Roo.bootstrap.NavProgressBar
29625 * @extends Roo.bootstrap.Component
29626 * Bootstrap NavProgressBar class
29629 * Create a new nav progress bar
29630 * @param {Object} config The config object
29633 Roo.bootstrap.NavProgressBar = function(config){
29634 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29636 this.bullets = this.bullets || [];
29638 // Roo.bootstrap.NavProgressBar.register(this);
29642 * Fires when the active item changes
29643 * @param {Roo.bootstrap.NavProgressBar} this
29644 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29645 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29652 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29657 getAutoCreate : function()
29659 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29663 cls : 'roo-navigation-bar-group',
29667 cls : 'roo-navigation-top-bar'
29671 cls : 'roo-navigation-bullets-bar',
29675 cls : 'roo-navigation-bar'
29682 cls : 'roo-navigation-bottom-bar'
29692 initEvents: function()
29697 onRender : function(ct, position)
29699 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29701 if(this.bullets.length){
29702 Roo.each(this.bullets, function(b){
29711 addItem : function(cfg)
29713 var item = new Roo.bootstrap.NavProgressItem(cfg);
29715 item.parentId = this.id;
29716 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29719 var top = new Roo.bootstrap.Element({
29721 cls : 'roo-navigation-bar-text'
29724 var bottom = new Roo.bootstrap.Element({
29726 cls : 'roo-navigation-bar-text'
29729 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29730 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29732 var topText = new Roo.bootstrap.Element({
29734 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29737 var bottomText = new Roo.bootstrap.Element({
29739 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29742 topText.onRender(top.el, null);
29743 bottomText.onRender(bottom.el, null);
29746 item.bottomEl = bottom;
29749 this.barItems.push(item);
29754 getActive : function()
29756 var active = false;
29758 Roo.each(this.barItems, function(v){
29760 if (!v.isActive()) {
29772 setActiveItem : function(item)
29776 Roo.each(this.barItems, function(v){
29777 if (v.rid == item.rid) {
29781 if (v.isActive()) {
29782 v.setActive(false);
29787 item.setActive(true);
29789 this.fireEvent('changed', this, item, prev);
29792 getBarItem: function(rid)
29796 Roo.each(this.barItems, function(e) {
29797 if (e.rid != rid) {
29808 indexOfItem : function(item)
29812 Roo.each(this.barItems, function(v, i){
29814 if (v.rid != item.rid) {
29825 setActiveNext : function()
29827 var i = this.indexOfItem(this.getActive());
29829 if (i > this.barItems.length) {
29833 this.setActiveItem(this.barItems[i+1]);
29836 setActivePrev : function()
29838 var i = this.indexOfItem(this.getActive());
29844 this.setActiveItem(this.barItems[i-1]);
29847 format : function()
29849 if(!this.barItems.length){
29853 var width = 100 / this.barItems.length;
29855 Roo.each(this.barItems, function(i){
29856 i.el.setStyle('width', width + '%');
29857 i.topEl.el.setStyle('width', width + '%');
29858 i.bottomEl.el.setStyle('width', width + '%');
29867 * Nav Progress Item
29872 * @class Roo.bootstrap.NavProgressItem
29873 * @extends Roo.bootstrap.Component
29874 * Bootstrap NavProgressItem class
29875 * @cfg {String} rid the reference id
29876 * @cfg {Boolean} active (true|false) Is item active default false
29877 * @cfg {Boolean} disabled (true|false) Is item active default false
29878 * @cfg {String} html
29879 * @cfg {String} position (top|bottom) text position default bottom
29880 * @cfg {String} icon show icon instead of number
29883 * Create a new NavProgressItem
29884 * @param {Object} config The config object
29886 Roo.bootstrap.NavProgressItem = function(config){
29887 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29892 * The raw click event for the entire grid.
29893 * @param {Roo.bootstrap.NavProgressItem} this
29894 * @param {Roo.EventObject} e
29901 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29907 position : 'bottom',
29910 getAutoCreate : function()
29912 var iconCls = 'roo-navigation-bar-item-icon';
29914 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29918 cls: 'roo-navigation-bar-item',
29928 cfg.cls += ' active';
29931 cfg.cls += ' disabled';
29937 disable : function()
29939 this.setDisabled(true);
29942 enable : function()
29944 this.setDisabled(false);
29947 initEvents: function()
29949 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29951 this.iconEl.on('click', this.onClick, this);
29954 onClick : function(e)
29956 e.preventDefault();
29962 if(this.fireEvent('click', this, e) === false){
29966 this.parent().setActiveItem(this);
29969 isActive: function ()
29971 return this.active;
29974 setActive : function(state)
29976 if(this.active == state){
29980 this.active = state;
29983 this.el.addClass('active');
29987 this.el.removeClass('active');
29992 setDisabled : function(state)
29994 if(this.disabled == state){
29998 this.disabled = state;
30001 this.el.addClass('disabled');
30005 this.el.removeClass('disabled');
30008 tooltipEl : function()
30010 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30023 * @class Roo.bootstrap.FieldLabel
30024 * @extends Roo.bootstrap.Component
30025 * Bootstrap FieldLabel class
30026 * @cfg {String} html contents of the element
30027 * @cfg {String} tag tag of the element default label
30028 * @cfg {String} cls class of the element
30029 * @cfg {String} target label target
30030 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30031 * @cfg {String} invalidClass default "text-warning"
30032 * @cfg {String} validClass default "text-success"
30033 * @cfg {String} iconTooltip default "This field is required"
30034 * @cfg {String} indicatorpos (left|right) default left
30037 * Create a new FieldLabel
30038 * @param {Object} config The config object
30041 Roo.bootstrap.FieldLabel = function(config){
30042 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30047 * Fires after the field has been marked as invalid.
30048 * @param {Roo.form.FieldLabel} this
30049 * @param {String} msg The validation message
30054 * Fires after the field has been validated with no errors.
30055 * @param {Roo.form.FieldLabel} this
30061 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30068 invalidClass : 'has-warning',
30069 validClass : 'has-success',
30070 iconTooltip : 'This field is required',
30071 indicatorpos : 'left',
30073 getAutoCreate : function(){
30076 if (!this.allowBlank) {
30082 cls : 'roo-bootstrap-field-label ' + this.cls,
30087 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30088 tooltip : this.iconTooltip
30097 if(this.indicatorpos == 'right'){
30100 cls : 'roo-bootstrap-field-label ' + this.cls,
30109 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30110 tooltip : this.iconTooltip
30119 initEvents: function()
30121 Roo.bootstrap.Element.superclass.initEvents.call(this);
30123 this.indicator = this.indicatorEl();
30125 if(this.indicator){
30126 this.indicator.removeClass('visible');
30127 this.indicator.addClass('invisible');
30130 Roo.bootstrap.FieldLabel.register(this);
30133 indicatorEl : function()
30135 var indicator = this.el.select('i.roo-required-indicator',true).first();
30146 * Mark this field as valid
30148 markValid : function()
30150 if(this.indicator){
30151 this.indicator.removeClass('visible');
30152 this.indicator.addClass('invisible');
30155 this.el.removeClass(this.invalidClass);
30157 this.el.addClass(this.validClass);
30159 this.fireEvent('valid', this);
30163 * Mark this field as invalid
30164 * @param {String} msg The validation message
30166 markInvalid : function(msg)
30168 if(this.indicator){
30169 this.indicator.removeClass('invisible');
30170 this.indicator.addClass('visible');
30173 this.el.removeClass(this.validClass);
30175 this.el.addClass(this.invalidClass);
30177 this.fireEvent('invalid', this, msg);
30183 Roo.apply(Roo.bootstrap.FieldLabel, {
30188 * register a FieldLabel Group
30189 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30191 register : function(label)
30193 if(this.groups.hasOwnProperty(label.target)){
30197 this.groups[label.target] = label;
30201 * fetch a FieldLabel Group based on the target
30202 * @param {string} target
30203 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30205 get: function(target) {
30206 if (typeof(this.groups[target]) == 'undefined') {
30210 return this.groups[target] ;
30219 * page DateSplitField.
30225 * @class Roo.bootstrap.DateSplitField
30226 * @extends Roo.bootstrap.Component
30227 * Bootstrap DateSplitField class
30228 * @cfg {string} fieldLabel - the label associated
30229 * @cfg {Number} labelWidth set the width of label (0-12)
30230 * @cfg {String} labelAlign (top|left)
30231 * @cfg {Boolean} dayAllowBlank (true|false) default false
30232 * @cfg {Boolean} monthAllowBlank (true|false) default false
30233 * @cfg {Boolean} yearAllowBlank (true|false) default false
30234 * @cfg {string} dayPlaceholder
30235 * @cfg {string} monthPlaceholder
30236 * @cfg {string} yearPlaceholder
30237 * @cfg {string} dayFormat default 'd'
30238 * @cfg {string} monthFormat default 'm'
30239 * @cfg {string} yearFormat default 'Y'
30240 * @cfg {Number} labellg set the width of label (1-12)
30241 * @cfg {Number} labelmd set the width of label (1-12)
30242 * @cfg {Number} labelsm set the width of label (1-12)
30243 * @cfg {Number} labelxs set the width of label (1-12)
30247 * Create a new DateSplitField
30248 * @param {Object} config The config object
30251 Roo.bootstrap.DateSplitField = function(config){
30252 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30258 * getting the data of years
30259 * @param {Roo.bootstrap.DateSplitField} this
30260 * @param {Object} years
30265 * getting the data of days
30266 * @param {Roo.bootstrap.DateSplitField} this
30267 * @param {Object} days
30272 * Fires after the field has been marked as invalid.
30273 * @param {Roo.form.Field} this
30274 * @param {String} msg The validation message
30279 * Fires after the field has been validated with no errors.
30280 * @param {Roo.form.Field} this
30286 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30289 labelAlign : 'top',
30291 dayAllowBlank : false,
30292 monthAllowBlank : false,
30293 yearAllowBlank : false,
30294 dayPlaceholder : '',
30295 monthPlaceholder : '',
30296 yearPlaceholder : '',
30300 isFormField : true,
30306 getAutoCreate : function()
30310 cls : 'row roo-date-split-field-group',
30315 cls : 'form-hidden-field roo-date-split-field-group-value',
30321 var labelCls = 'col-md-12';
30322 var contentCls = 'col-md-4';
30324 if(this.fieldLabel){
30328 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30332 html : this.fieldLabel
30337 if(this.labelAlign == 'left'){
30339 if(this.labelWidth > 12){
30340 label.style = "width: " + this.labelWidth + 'px';
30343 if(this.labelWidth < 13 && this.labelmd == 0){
30344 this.labelmd = this.labelWidth;
30347 if(this.labellg > 0){
30348 labelCls = ' col-lg-' + this.labellg;
30349 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30352 if(this.labelmd > 0){
30353 labelCls = ' col-md-' + this.labelmd;
30354 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30357 if(this.labelsm > 0){
30358 labelCls = ' col-sm-' + this.labelsm;
30359 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30362 if(this.labelxs > 0){
30363 labelCls = ' col-xs-' + this.labelxs;
30364 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30368 label.cls += ' ' + labelCls;
30370 cfg.cn.push(label);
30373 Roo.each(['day', 'month', 'year'], function(t){
30376 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30383 inputEl: function ()
30385 return this.el.select('.roo-date-split-field-group-value', true).first();
30388 onRender : function(ct, position)
30392 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30394 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30396 this.dayField = new Roo.bootstrap.ComboBox({
30397 allowBlank : this.dayAllowBlank,
30398 alwaysQuery : true,
30399 displayField : 'value',
30402 forceSelection : true,
30404 placeholder : this.dayPlaceholder,
30405 selectOnFocus : true,
30406 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30407 triggerAction : 'all',
30409 valueField : 'value',
30410 store : new Roo.data.SimpleStore({
30411 data : (function() {
30413 _this.fireEvent('days', _this, days);
30416 fields : [ 'value' ]
30419 select : function (_self, record, index)
30421 _this.setValue(_this.getValue());
30426 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30428 this.monthField = new Roo.bootstrap.MonthField({
30429 after : '<i class=\"fa fa-calendar\"></i>',
30430 allowBlank : this.monthAllowBlank,
30431 placeholder : this.monthPlaceholder,
30434 render : function (_self)
30436 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30437 e.preventDefault();
30441 select : function (_self, oldvalue, newvalue)
30443 _this.setValue(_this.getValue());
30448 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30450 this.yearField = new Roo.bootstrap.ComboBox({
30451 allowBlank : this.yearAllowBlank,
30452 alwaysQuery : true,
30453 displayField : 'value',
30456 forceSelection : true,
30458 placeholder : this.yearPlaceholder,
30459 selectOnFocus : true,
30460 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30461 triggerAction : 'all',
30463 valueField : 'value',
30464 store : new Roo.data.SimpleStore({
30465 data : (function() {
30467 _this.fireEvent('years', _this, years);
30470 fields : [ 'value' ]
30473 select : function (_self, record, index)
30475 _this.setValue(_this.getValue());
30480 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30483 setValue : function(v, format)
30485 this.inputEl.dom.value = v;
30487 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30489 var d = Date.parseDate(v, f);
30496 this.setDay(d.format(this.dayFormat));
30497 this.setMonth(d.format(this.monthFormat));
30498 this.setYear(d.format(this.yearFormat));
30505 setDay : function(v)
30507 this.dayField.setValue(v);
30508 this.inputEl.dom.value = this.getValue();
30513 setMonth : function(v)
30515 this.monthField.setValue(v, true);
30516 this.inputEl.dom.value = this.getValue();
30521 setYear : function(v)
30523 this.yearField.setValue(v);
30524 this.inputEl.dom.value = this.getValue();
30529 getDay : function()
30531 return this.dayField.getValue();
30534 getMonth : function()
30536 return this.monthField.getValue();
30539 getYear : function()
30541 return this.yearField.getValue();
30544 getValue : function()
30546 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30548 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30558 this.inputEl.dom.value = '';
30563 validate : function()
30565 var d = this.dayField.validate();
30566 var m = this.monthField.validate();
30567 var y = this.yearField.validate();
30572 (!this.dayAllowBlank && !d) ||
30573 (!this.monthAllowBlank && !m) ||
30574 (!this.yearAllowBlank && !y)
30579 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30588 this.markInvalid();
30593 markValid : function()
30596 var label = this.el.select('label', true).first();
30597 var icon = this.el.select('i.fa-star', true).first();
30603 this.fireEvent('valid', this);
30607 * Mark this field as invalid
30608 * @param {String} msg The validation message
30610 markInvalid : function(msg)
30613 var label = this.el.select('label', true).first();
30614 var icon = this.el.select('i.fa-star', true).first();
30616 if(label && !icon){
30617 this.el.select('.roo-date-split-field-label', true).createChild({
30619 cls : 'text-danger fa fa-lg fa-star',
30620 tooltip : 'This field is required',
30621 style : 'margin-right:5px;'
30625 this.fireEvent('invalid', this, msg);
30628 clearInvalid : function()
30630 var label = this.el.select('label', true).first();
30631 var icon = this.el.select('i.fa-star', true).first();
30637 this.fireEvent('valid', this);
30640 getName: function()
30650 * http://masonry.desandro.com
30652 * The idea is to render all the bricks based on vertical width...
30654 * The original code extends 'outlayer' - we might need to use that....
30660 * @class Roo.bootstrap.LayoutMasonry
30661 * @extends Roo.bootstrap.Component
30662 * Bootstrap Layout Masonry class
30665 * Create a new Element
30666 * @param {Object} config The config object
30669 Roo.bootstrap.LayoutMasonry = function(config){
30671 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30675 Roo.bootstrap.LayoutMasonry.register(this);
30681 * Fire after layout the items
30682 * @param {Roo.bootstrap.LayoutMasonry} this
30683 * @param {Roo.EventObject} e
30690 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30693 * @cfg {Boolean} isLayoutInstant = no animation?
30695 isLayoutInstant : false, // needed?
30698 * @cfg {Number} boxWidth width of the columns
30703 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30708 * @cfg {Number} padWidth padding below box..
30713 * @cfg {Number} gutter gutter width..
30718 * @cfg {Number} maxCols maximum number of columns
30724 * @cfg {Boolean} isAutoInitial defalut true
30726 isAutoInitial : true,
30731 * @cfg {Boolean} isHorizontal defalut false
30733 isHorizontal : false,
30735 currentSize : null,
30741 bricks: null, //CompositeElement
30745 _isLayoutInited : false,
30747 // isAlternative : false, // only use for vertical layout...
30750 * @cfg {Number} alternativePadWidth padding below box..
30752 alternativePadWidth : 50,
30754 selectedBrick : [],
30756 getAutoCreate : function(){
30758 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30762 cls: 'blog-masonary-wrapper ' + this.cls,
30764 cls : 'mas-boxes masonary'
30771 getChildContainer: function( )
30773 if (this.boxesEl) {
30774 return this.boxesEl;
30777 this.boxesEl = this.el.select('.mas-boxes').first();
30779 return this.boxesEl;
30783 initEvents : function()
30787 if(this.isAutoInitial){
30788 Roo.log('hook children rendered');
30789 this.on('childrenrendered', function() {
30790 Roo.log('children rendered');
30796 initial : function()
30798 this.selectedBrick = [];
30800 this.currentSize = this.el.getBox(true);
30802 Roo.EventManager.onWindowResize(this.resize, this);
30804 if(!this.isAutoInitial){
30812 //this.layout.defer(500,this);
30816 resize : function()
30818 var cs = this.el.getBox(true);
30821 this.currentSize.width == cs.width &&
30822 this.currentSize.x == cs.x &&
30823 this.currentSize.height == cs.height &&
30824 this.currentSize.y == cs.y
30826 Roo.log("no change in with or X or Y");
30830 this.currentSize = cs;
30836 layout : function()
30838 this._resetLayout();
30840 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30842 this.layoutItems( isInstant );
30844 this._isLayoutInited = true;
30846 this.fireEvent('layout', this);
30850 _resetLayout : function()
30852 if(this.isHorizontal){
30853 this.horizontalMeasureColumns();
30857 this.verticalMeasureColumns();
30861 verticalMeasureColumns : function()
30863 this.getContainerWidth();
30865 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30866 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30870 var boxWidth = this.boxWidth + this.padWidth;
30872 if(this.containerWidth < this.boxWidth){
30873 boxWidth = this.containerWidth
30876 var containerWidth = this.containerWidth;
30878 var cols = Math.floor(containerWidth / boxWidth);
30880 this.cols = Math.max( cols, 1 );
30882 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30884 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30886 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30888 this.colWidth = boxWidth + avail - this.padWidth;
30890 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30891 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30894 horizontalMeasureColumns : function()
30896 this.getContainerWidth();
30898 var boxWidth = this.boxWidth;
30900 if(this.containerWidth < boxWidth){
30901 boxWidth = this.containerWidth;
30904 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30906 this.el.setHeight(boxWidth);
30910 getContainerWidth : function()
30912 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30915 layoutItems : function( isInstant )
30917 Roo.log(this.bricks);
30919 var items = Roo.apply([], this.bricks);
30921 if(this.isHorizontal){
30922 this._horizontalLayoutItems( items , isInstant );
30926 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30927 // this._verticalAlternativeLayoutItems( items , isInstant );
30931 this._verticalLayoutItems( items , isInstant );
30935 _verticalLayoutItems : function ( items , isInstant)
30937 if ( !items || !items.length ) {
30942 ['xs', 'xs', 'xs', 'tall'],
30943 ['xs', 'xs', 'tall'],
30944 ['xs', 'xs', 'sm'],
30945 ['xs', 'xs', 'xs'],
30951 ['sm', 'xs', 'xs'],
30955 ['tall', 'xs', 'xs', 'xs'],
30956 ['tall', 'xs', 'xs'],
30968 Roo.each(items, function(item, k){
30970 switch (item.size) {
30971 // these layouts take up a full box,
30982 boxes.push([item]);
31005 var filterPattern = function(box, length)
31013 var pattern = box.slice(0, length);
31017 Roo.each(pattern, function(i){
31018 format.push(i.size);
31021 Roo.each(standard, function(s){
31023 if(String(s) != String(format)){
31032 if(!match && length == 1){
31037 filterPattern(box, length - 1);
31041 queue.push(pattern);
31043 box = box.slice(length, box.length);
31045 filterPattern(box, 4);
31051 Roo.each(boxes, function(box, k){
31057 if(box.length == 1){
31062 filterPattern(box, 4);
31066 this._processVerticalLayoutQueue( queue, isInstant );
31070 // _verticalAlternativeLayoutItems : function( items , isInstant )
31072 // if ( !items || !items.length ) {
31076 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31080 _horizontalLayoutItems : function ( items , isInstant)
31082 if ( !items || !items.length || items.length < 3) {
31088 var eItems = items.slice(0, 3);
31090 items = items.slice(3, items.length);
31093 ['xs', 'xs', 'xs', 'wide'],
31094 ['xs', 'xs', 'wide'],
31095 ['xs', 'xs', 'sm'],
31096 ['xs', 'xs', 'xs'],
31102 ['sm', 'xs', 'xs'],
31106 ['wide', 'xs', 'xs', 'xs'],
31107 ['wide', 'xs', 'xs'],
31120 Roo.each(items, function(item, k){
31122 switch (item.size) {
31133 boxes.push([item]);
31157 var filterPattern = function(box, length)
31165 var pattern = box.slice(0, length);
31169 Roo.each(pattern, function(i){
31170 format.push(i.size);
31173 Roo.each(standard, function(s){
31175 if(String(s) != String(format)){
31184 if(!match && length == 1){
31189 filterPattern(box, length - 1);
31193 queue.push(pattern);
31195 box = box.slice(length, box.length);
31197 filterPattern(box, 4);
31203 Roo.each(boxes, function(box, k){
31209 if(box.length == 1){
31214 filterPattern(box, 4);
31221 var pos = this.el.getBox(true);
31225 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31227 var hit_end = false;
31229 Roo.each(queue, function(box){
31233 Roo.each(box, function(b){
31235 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31245 Roo.each(box, function(b){
31247 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31250 mx = Math.max(mx, b.x);
31254 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31258 Roo.each(box, function(b){
31260 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31274 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31277 /** Sets position of item in DOM
31278 * @param {Element} item
31279 * @param {Number} x - horizontal position
31280 * @param {Number} y - vertical position
31281 * @param {Boolean} isInstant - disables transitions
31283 _processVerticalLayoutQueue : function( queue, isInstant )
31285 var pos = this.el.getBox(true);
31290 for (var i = 0; i < this.cols; i++){
31294 Roo.each(queue, function(box, k){
31296 var col = k % this.cols;
31298 Roo.each(box, function(b,kk){
31300 b.el.position('absolute');
31302 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31303 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31305 if(b.size == 'md-left' || b.size == 'md-right'){
31306 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31307 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31310 b.el.setWidth(width);
31311 b.el.setHeight(height);
31313 b.el.select('iframe',true).setSize(width,height);
31317 for (var i = 0; i < this.cols; i++){
31319 if(maxY[i] < maxY[col]){
31324 col = Math.min(col, i);
31328 x = pos.x + col * (this.colWidth + this.padWidth);
31332 var positions = [];
31334 switch (box.length){
31336 positions = this.getVerticalOneBoxColPositions(x, y, box);
31339 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31342 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31345 positions = this.getVerticalFourBoxColPositions(x, y, box);
31351 Roo.each(box, function(b,kk){
31353 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31355 var sz = b.el.getSize();
31357 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31365 for (var i = 0; i < this.cols; i++){
31366 mY = Math.max(mY, maxY[i]);
31369 this.el.setHeight(mY - pos.y);
31373 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31375 // var pos = this.el.getBox(true);
31378 // var maxX = pos.right;
31380 // var maxHeight = 0;
31382 // Roo.each(items, function(item, k){
31386 // item.el.position('absolute');
31388 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31390 // item.el.setWidth(width);
31392 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31394 // item.el.setHeight(height);
31397 // item.el.setXY([x, y], isInstant ? false : true);
31399 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31402 // y = y + height + this.alternativePadWidth;
31404 // maxHeight = maxHeight + height + this.alternativePadWidth;
31408 // this.el.setHeight(maxHeight);
31412 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31414 var pos = this.el.getBox(true);
31419 var maxX = pos.right;
31421 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31423 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31425 Roo.each(queue, function(box, k){
31427 Roo.each(box, function(b, kk){
31429 b.el.position('absolute');
31431 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31432 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31434 if(b.size == 'md-left' || b.size == 'md-right'){
31435 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31436 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31439 b.el.setWidth(width);
31440 b.el.setHeight(height);
31448 var positions = [];
31450 switch (box.length){
31452 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31455 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31458 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31461 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31467 Roo.each(box, function(b,kk){
31469 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31471 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31479 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31481 Roo.each(eItems, function(b,k){
31483 b.size = (k == 0) ? 'sm' : 'xs';
31484 b.x = (k == 0) ? 2 : 1;
31485 b.y = (k == 0) ? 2 : 1;
31487 b.el.position('absolute');
31489 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31491 b.el.setWidth(width);
31493 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31495 b.el.setHeight(height);
31499 var positions = [];
31502 x : maxX - this.unitWidth * 2 - this.gutter,
31507 x : maxX - this.unitWidth,
31508 y : minY + (this.unitWidth + this.gutter) * 2
31512 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31516 Roo.each(eItems, function(b,k){
31518 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31524 getVerticalOneBoxColPositions : function(x, y, box)
31528 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31530 if(box[0].size == 'md-left'){
31534 if(box[0].size == 'md-right'){
31539 x : x + (this.unitWidth + this.gutter) * rand,
31546 getVerticalTwoBoxColPositions : function(x, y, box)
31550 if(box[0].size == 'xs'){
31554 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31558 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31572 x : x + (this.unitWidth + this.gutter) * 2,
31573 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31580 getVerticalThreeBoxColPositions : function(x, y, box)
31584 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31592 x : x + (this.unitWidth + this.gutter) * 1,
31597 x : x + (this.unitWidth + this.gutter) * 2,
31605 if(box[0].size == 'xs' && box[1].size == 'xs'){
31614 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31618 x : x + (this.unitWidth + this.gutter) * 1,
31632 x : x + (this.unitWidth + this.gutter) * 2,
31637 x : x + (this.unitWidth + this.gutter) * 2,
31638 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31645 getVerticalFourBoxColPositions : function(x, y, box)
31649 if(box[0].size == 'xs'){
31658 y : y + (this.unitHeight + this.gutter) * 1
31663 y : y + (this.unitHeight + this.gutter) * 2
31667 x : x + (this.unitWidth + this.gutter) * 1,
31681 x : x + (this.unitWidth + this.gutter) * 2,
31686 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31687 y : y + (this.unitHeight + this.gutter) * 1
31691 x : x + (this.unitWidth + this.gutter) * 2,
31692 y : y + (this.unitWidth + this.gutter) * 2
31699 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31703 if(box[0].size == 'md-left'){
31705 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31712 if(box[0].size == 'md-right'){
31714 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31715 y : minY + (this.unitWidth + this.gutter) * 1
31721 var rand = Math.floor(Math.random() * (4 - box[0].y));
31724 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31725 y : minY + (this.unitWidth + this.gutter) * rand
31732 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31736 if(box[0].size == 'xs'){
31739 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31744 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31745 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31753 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31758 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31759 y : minY + (this.unitWidth + this.gutter) * 2
31766 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31770 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31773 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31778 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31779 y : minY + (this.unitWidth + this.gutter) * 1
31783 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31784 y : minY + (this.unitWidth + this.gutter) * 2
31791 if(box[0].size == 'xs' && box[1].size == 'xs'){
31794 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31799 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31804 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31805 y : minY + (this.unitWidth + this.gutter) * 1
31813 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31818 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31819 y : minY + (this.unitWidth + this.gutter) * 2
31823 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31824 y : minY + (this.unitWidth + this.gutter) * 2
31831 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31835 if(box[0].size == 'xs'){
31838 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31843 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31848 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),
31853 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31854 y : minY + (this.unitWidth + this.gutter) * 1
31862 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31867 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31868 y : minY + (this.unitWidth + this.gutter) * 2
31872 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31873 y : minY + (this.unitWidth + this.gutter) * 2
31877 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),
31878 y : minY + (this.unitWidth + this.gutter) * 2
31886 * remove a Masonry Brick
31887 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31889 removeBrick : function(brick_id)
31895 for (var i = 0; i<this.bricks.length; i++) {
31896 if (this.bricks[i].id == brick_id) {
31897 this.bricks.splice(i,1);
31898 this.el.dom.removeChild(Roo.get(brick_id).dom);
31905 * adds a Masonry Brick
31906 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31908 addBrick : function(cfg)
31910 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31911 //this.register(cn);
31912 cn.parentId = this.id;
31913 cn.onRender(this.el, null);
31918 * register a Masonry Brick
31919 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31922 register : function(brick)
31924 this.bricks.push(brick);
31925 brick.masonryId = this.id;
31929 * clear all the Masonry Brick
31931 clearAll : function()
31934 //this.getChildContainer().dom.innerHTML = "";
31935 this.el.dom.innerHTML = '';
31938 getSelected : function()
31940 if (!this.selectedBrick) {
31944 return this.selectedBrick;
31948 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31952 * register a Masonry Layout
31953 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31956 register : function(layout)
31958 this.groups[layout.id] = layout;
31961 * fetch a Masonry Layout based on the masonry layout ID
31962 * @param {string} the masonry layout to add
31963 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31966 get: function(layout_id) {
31967 if (typeof(this.groups[layout_id]) == 'undefined') {
31970 return this.groups[layout_id] ;
31982 * http://masonry.desandro.com
31984 * The idea is to render all the bricks based on vertical width...
31986 * The original code extends 'outlayer' - we might need to use that....
31992 * @class Roo.bootstrap.LayoutMasonryAuto
31993 * @extends Roo.bootstrap.Component
31994 * Bootstrap Layout Masonry class
31997 * Create a new Element
31998 * @param {Object} config The config object
32001 Roo.bootstrap.LayoutMasonryAuto = function(config){
32002 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32005 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32008 * @cfg {Boolean} isFitWidth - resize the width..
32010 isFitWidth : false, // options..
32012 * @cfg {Boolean} isOriginLeft = left align?
32014 isOriginLeft : true,
32016 * @cfg {Boolean} isOriginTop = top align?
32018 isOriginTop : false,
32020 * @cfg {Boolean} isLayoutInstant = no animation?
32022 isLayoutInstant : false, // needed?
32024 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32026 isResizingContainer : true,
32028 * @cfg {Number} columnWidth width of the columns
32034 * @cfg {Number} maxCols maximum number of columns
32039 * @cfg {Number} padHeight padding below box..
32045 * @cfg {Boolean} isAutoInitial defalut true
32048 isAutoInitial : true,
32054 initialColumnWidth : 0,
32055 currentSize : null,
32057 colYs : null, // array.
32064 bricks: null, //CompositeElement
32065 cols : 0, // array?
32066 // element : null, // wrapped now this.el
32067 _isLayoutInited : null,
32070 getAutoCreate : function(){
32074 cls: 'blog-masonary-wrapper ' + this.cls,
32076 cls : 'mas-boxes masonary'
32083 getChildContainer: function( )
32085 if (this.boxesEl) {
32086 return this.boxesEl;
32089 this.boxesEl = this.el.select('.mas-boxes').first();
32091 return this.boxesEl;
32095 initEvents : function()
32099 if(this.isAutoInitial){
32100 Roo.log('hook children rendered');
32101 this.on('childrenrendered', function() {
32102 Roo.log('children rendered');
32109 initial : function()
32111 this.reloadItems();
32113 this.currentSize = this.el.getBox(true);
32115 /// was window resize... - let's see if this works..
32116 Roo.EventManager.onWindowResize(this.resize, this);
32118 if(!this.isAutoInitial){
32123 this.layout.defer(500,this);
32126 reloadItems: function()
32128 this.bricks = this.el.select('.masonry-brick', true);
32130 this.bricks.each(function(b) {
32131 //Roo.log(b.getSize());
32132 if (!b.attr('originalwidth')) {
32133 b.attr('originalwidth', b.getSize().width);
32138 Roo.log(this.bricks.elements.length);
32141 resize : function()
32144 var cs = this.el.getBox(true);
32146 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32147 Roo.log("no change in with or X");
32150 this.currentSize = cs;
32154 layout : function()
32157 this._resetLayout();
32158 //this._manageStamps();
32160 // don't animate first layout
32161 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32162 this.layoutItems( isInstant );
32164 // flag for initalized
32165 this._isLayoutInited = true;
32168 layoutItems : function( isInstant )
32170 //var items = this._getItemsForLayout( this.items );
32171 // original code supports filtering layout items.. we just ignore it..
32173 this._layoutItems( this.bricks , isInstant );
32175 this._postLayout();
32177 _layoutItems : function ( items , isInstant)
32179 //this.fireEvent( 'layout', this, items );
32182 if ( !items || !items.elements.length ) {
32183 // no items, emit event with empty array
32188 items.each(function(item) {
32189 Roo.log("layout item");
32191 // get x/y object from method
32192 var position = this._getItemLayoutPosition( item );
32194 position.item = item;
32195 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32196 queue.push( position );
32199 this._processLayoutQueue( queue );
32201 /** Sets position of item in DOM
32202 * @param {Element} item
32203 * @param {Number} x - horizontal position
32204 * @param {Number} y - vertical position
32205 * @param {Boolean} isInstant - disables transitions
32207 _processLayoutQueue : function( queue )
32209 for ( var i=0, len = queue.length; i < len; i++ ) {
32210 var obj = queue[i];
32211 obj.item.position('absolute');
32212 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32218 * Any logic you want to do after each layout,
32219 * i.e. size the container
32221 _postLayout : function()
32223 this.resizeContainer();
32226 resizeContainer : function()
32228 if ( !this.isResizingContainer ) {
32231 var size = this._getContainerSize();
32233 this.el.setSize(size.width,size.height);
32234 this.boxesEl.setSize(size.width,size.height);
32240 _resetLayout : function()
32242 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32243 this.colWidth = this.el.getWidth();
32244 //this.gutter = this.el.getWidth();
32246 this.measureColumns();
32252 this.colYs.push( 0 );
32258 measureColumns : function()
32260 this.getContainerWidth();
32261 // if columnWidth is 0, default to outerWidth of first item
32262 if ( !this.columnWidth ) {
32263 var firstItem = this.bricks.first();
32264 Roo.log(firstItem);
32265 this.columnWidth = this.containerWidth;
32266 if (firstItem && firstItem.attr('originalwidth') ) {
32267 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32269 // columnWidth fall back to item of first element
32270 Roo.log("set column width?");
32271 this.initialColumnWidth = this.columnWidth ;
32273 // if first elem has no width, default to size of container
32278 if (this.initialColumnWidth) {
32279 this.columnWidth = this.initialColumnWidth;
32284 // column width is fixed at the top - however if container width get's smaller we should
32287 // this bit calcs how man columns..
32289 var columnWidth = this.columnWidth += this.gutter;
32291 // calculate columns
32292 var containerWidth = this.containerWidth + this.gutter;
32294 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32295 // fix rounding errors, typically with gutters
32296 var excess = columnWidth - containerWidth % columnWidth;
32299 // if overshoot is less than a pixel, round up, otherwise floor it
32300 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32301 cols = Math[ mathMethod ]( cols );
32302 this.cols = Math.max( cols, 1 );
32303 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32305 // padding positioning..
32306 var totalColWidth = this.cols * this.columnWidth;
32307 var padavail = this.containerWidth - totalColWidth;
32308 // so for 2 columns - we need 3 'pads'
32310 var padNeeded = (1+this.cols) * this.padWidth;
32312 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32314 this.columnWidth += padExtra
32315 //this.padWidth = Math.floor(padavail / ( this.cols));
32317 // adjust colum width so that padding is fixed??
32319 // we have 3 columns ... total = width * 3
32320 // we have X left over... that should be used by
32322 //if (this.expandC) {
32330 getContainerWidth : function()
32332 /* // container is parent if fit width
32333 var container = this.isFitWidth ? this.element.parentNode : this.element;
32334 // check that this.size and size are there
32335 // IE8 triggers resize on body size change, so they might not be
32337 var size = getSize( container ); //FIXME
32338 this.containerWidth = size && size.innerWidth; //FIXME
32341 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32345 _getItemLayoutPosition : function( item ) // what is item?
32347 // we resize the item to our columnWidth..
32349 item.setWidth(this.columnWidth);
32350 item.autoBoxAdjust = false;
32352 var sz = item.getSize();
32354 // how many columns does this brick span
32355 var remainder = this.containerWidth % this.columnWidth;
32357 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32358 // round if off by 1 pixel, otherwise use ceil
32359 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32360 colSpan = Math.min( colSpan, this.cols );
32362 // normally this should be '1' as we dont' currently allow multi width columns..
32364 var colGroup = this._getColGroup( colSpan );
32365 // get the minimum Y value from the columns
32366 var minimumY = Math.min.apply( Math, colGroup );
32367 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32369 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32371 // position the brick
32373 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32374 y: this.currentSize.y + minimumY + this.padHeight
32378 // apply setHeight to necessary columns
32379 var setHeight = minimumY + sz.height + this.padHeight;
32380 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32382 var setSpan = this.cols + 1 - colGroup.length;
32383 for ( var i = 0; i < setSpan; i++ ) {
32384 this.colYs[ shortColIndex + i ] = setHeight ;
32391 * @param {Number} colSpan - number of columns the element spans
32392 * @returns {Array} colGroup
32394 _getColGroup : function( colSpan )
32396 if ( colSpan < 2 ) {
32397 // if brick spans only one column, use all the column Ys
32402 // how many different places could this brick fit horizontally
32403 var groupCount = this.cols + 1 - colSpan;
32404 // for each group potential horizontal position
32405 for ( var i = 0; i < groupCount; i++ ) {
32406 // make an array of colY values for that one group
32407 var groupColYs = this.colYs.slice( i, i + colSpan );
32408 // and get the max value of the array
32409 colGroup[i] = Math.max.apply( Math, groupColYs );
32414 _manageStamp : function( stamp )
32416 var stampSize = stamp.getSize();
32417 var offset = stamp.getBox();
32418 // get the columns that this stamp affects
32419 var firstX = this.isOriginLeft ? offset.x : offset.right;
32420 var lastX = firstX + stampSize.width;
32421 var firstCol = Math.floor( firstX / this.columnWidth );
32422 firstCol = Math.max( 0, firstCol );
32424 var lastCol = Math.floor( lastX / this.columnWidth );
32425 // lastCol should not go over if multiple of columnWidth #425
32426 lastCol -= lastX % this.columnWidth ? 0 : 1;
32427 lastCol = Math.min( this.cols - 1, lastCol );
32429 // set colYs to bottom of the stamp
32430 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32433 for ( var i = firstCol; i <= lastCol; i++ ) {
32434 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32439 _getContainerSize : function()
32441 this.maxY = Math.max.apply( Math, this.colYs );
32446 if ( this.isFitWidth ) {
32447 size.width = this._getContainerFitWidth();
32453 _getContainerFitWidth : function()
32455 var unusedCols = 0;
32456 // count unused columns
32459 if ( this.colYs[i] !== 0 ) {
32464 // fit container to columns that have been used
32465 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32468 needsResizeLayout : function()
32470 var previousWidth = this.containerWidth;
32471 this.getContainerWidth();
32472 return previousWidth !== this.containerWidth;
32487 * @class Roo.bootstrap.MasonryBrick
32488 * @extends Roo.bootstrap.Component
32489 * Bootstrap MasonryBrick class
32492 * Create a new MasonryBrick
32493 * @param {Object} config The config object
32496 Roo.bootstrap.MasonryBrick = function(config){
32498 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32500 Roo.bootstrap.MasonryBrick.register(this);
32506 * When a MasonryBrick is clcik
32507 * @param {Roo.bootstrap.MasonryBrick} this
32508 * @param {Roo.EventObject} e
32514 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32517 * @cfg {String} title
32521 * @cfg {String} html
32525 * @cfg {String} bgimage
32529 * @cfg {String} videourl
32533 * @cfg {String} cls
32537 * @cfg {String} href
32541 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32546 * @cfg {String} placetitle (center|bottom)
32551 * @cfg {Boolean} isFitContainer defalut true
32553 isFitContainer : true,
32556 * @cfg {Boolean} preventDefault defalut false
32558 preventDefault : false,
32561 * @cfg {Boolean} inverse defalut false
32563 maskInverse : false,
32565 getAutoCreate : function()
32567 if(!this.isFitContainer){
32568 return this.getSplitAutoCreate();
32571 var cls = 'masonry-brick masonry-brick-full';
32573 if(this.href.length){
32574 cls += ' masonry-brick-link';
32577 if(this.bgimage.length){
32578 cls += ' masonry-brick-image';
32581 if(this.maskInverse){
32582 cls += ' mask-inverse';
32585 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32586 cls += ' enable-mask';
32590 cls += ' masonry-' + this.size + '-brick';
32593 if(this.placetitle.length){
32595 switch (this.placetitle) {
32597 cls += ' masonry-center-title';
32600 cls += ' masonry-bottom-title';
32607 if(!this.html.length && !this.bgimage.length){
32608 cls += ' masonry-center-title';
32611 if(!this.html.length && this.bgimage.length){
32612 cls += ' masonry-bottom-title';
32617 cls += ' ' + this.cls;
32621 tag: (this.href.length) ? 'a' : 'div',
32626 cls: 'masonry-brick-mask'
32630 cls: 'masonry-brick-paragraph',
32636 if(this.href.length){
32637 cfg.href = this.href;
32640 var cn = cfg.cn[1].cn;
32642 if(this.title.length){
32645 cls: 'masonry-brick-title',
32650 if(this.html.length){
32653 cls: 'masonry-brick-text',
32658 if (!this.title.length && !this.html.length) {
32659 cfg.cn[1].cls += ' hide';
32662 if(this.bgimage.length){
32665 cls: 'masonry-brick-image-view',
32670 if(this.videourl.length){
32671 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32672 // youtube support only?
32675 cls: 'masonry-brick-image-view',
32678 allowfullscreen : true
32686 getSplitAutoCreate : function()
32688 var cls = 'masonry-brick masonry-brick-split';
32690 if(this.href.length){
32691 cls += ' masonry-brick-link';
32694 if(this.bgimage.length){
32695 cls += ' masonry-brick-image';
32699 cls += ' masonry-' + this.size + '-brick';
32702 switch (this.placetitle) {
32704 cls += ' masonry-center-title';
32707 cls += ' masonry-bottom-title';
32710 if(!this.bgimage.length){
32711 cls += ' masonry-center-title';
32714 if(this.bgimage.length){
32715 cls += ' masonry-bottom-title';
32721 cls += ' ' + this.cls;
32725 tag: (this.href.length) ? 'a' : 'div',
32730 cls: 'masonry-brick-split-head',
32734 cls: 'masonry-brick-paragraph',
32741 cls: 'masonry-brick-split-body',
32747 if(this.href.length){
32748 cfg.href = this.href;
32751 if(this.title.length){
32752 cfg.cn[0].cn[0].cn.push({
32754 cls: 'masonry-brick-title',
32759 if(this.html.length){
32760 cfg.cn[1].cn.push({
32762 cls: 'masonry-brick-text',
32767 if(this.bgimage.length){
32768 cfg.cn[0].cn.push({
32770 cls: 'masonry-brick-image-view',
32775 if(this.videourl.length){
32776 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32777 // youtube support only?
32778 cfg.cn[0].cn.cn.push({
32780 cls: 'masonry-brick-image-view',
32783 allowfullscreen : true
32790 initEvents: function()
32792 switch (this.size) {
32825 this.el.on('touchstart', this.onTouchStart, this);
32826 this.el.on('touchmove', this.onTouchMove, this);
32827 this.el.on('touchend', this.onTouchEnd, this);
32828 this.el.on('contextmenu', this.onContextMenu, this);
32830 this.el.on('mouseenter' ,this.enter, this);
32831 this.el.on('mouseleave', this.leave, this);
32832 this.el.on('click', this.onClick, this);
32835 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32836 this.parent().bricks.push(this);
32841 onClick: function(e, el)
32843 var time = this.endTimer - this.startTimer;
32844 // Roo.log(e.preventDefault());
32847 e.preventDefault();
32852 if(!this.preventDefault){
32856 e.preventDefault();
32858 if (this.activeClass != '') {
32859 this.selectBrick();
32862 this.fireEvent('click', this, e);
32865 enter: function(e, el)
32867 e.preventDefault();
32869 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32873 if(this.bgimage.length && this.html.length){
32874 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32878 leave: function(e, el)
32880 e.preventDefault();
32882 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32886 if(this.bgimage.length && this.html.length){
32887 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32891 onTouchStart: function(e, el)
32893 // e.preventDefault();
32895 this.touchmoved = false;
32897 if(!this.isFitContainer){
32901 if(!this.bgimage.length || !this.html.length){
32905 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32907 this.timer = new Date().getTime();
32911 onTouchMove: function(e, el)
32913 this.touchmoved = true;
32916 onContextMenu : function(e,el)
32918 e.preventDefault();
32919 e.stopPropagation();
32923 onTouchEnd: function(e, el)
32925 // e.preventDefault();
32927 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32934 if(!this.bgimage.length || !this.html.length){
32936 if(this.href.length){
32937 window.location.href = this.href;
32943 if(!this.isFitContainer){
32947 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32949 window.location.href = this.href;
32952 //selection on single brick only
32953 selectBrick : function() {
32955 if (!this.parentId) {
32959 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32960 var index = m.selectedBrick.indexOf(this.id);
32963 m.selectedBrick.splice(index,1);
32964 this.el.removeClass(this.activeClass);
32968 for(var i = 0; i < m.selectedBrick.length; i++) {
32969 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32970 b.el.removeClass(b.activeClass);
32973 m.selectedBrick = [];
32975 m.selectedBrick.push(this.id);
32976 this.el.addClass(this.activeClass);
32980 isSelected : function(){
32981 return this.el.hasClass(this.activeClass);
32986 Roo.apply(Roo.bootstrap.MasonryBrick, {
32989 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32991 * register a Masonry Brick
32992 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32995 register : function(brick)
32997 //this.groups[brick.id] = brick;
32998 this.groups.add(brick.id, brick);
33001 * fetch a masonry brick based on the masonry brick ID
33002 * @param {string} the masonry brick to add
33003 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33006 get: function(brick_id)
33008 // if (typeof(this.groups[brick_id]) == 'undefined') {
33011 // return this.groups[brick_id] ;
33013 if(this.groups.key(brick_id)) {
33014 return this.groups.key(brick_id);
33032 * @class Roo.bootstrap.Brick
33033 * @extends Roo.bootstrap.Component
33034 * Bootstrap Brick class
33037 * Create a new Brick
33038 * @param {Object} config The config object
33041 Roo.bootstrap.Brick = function(config){
33042 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33048 * When a Brick is click
33049 * @param {Roo.bootstrap.Brick} this
33050 * @param {Roo.EventObject} e
33056 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33059 * @cfg {String} title
33063 * @cfg {String} html
33067 * @cfg {String} bgimage
33071 * @cfg {String} cls
33075 * @cfg {String} href
33079 * @cfg {String} video
33083 * @cfg {Boolean} square
33087 getAutoCreate : function()
33089 var cls = 'roo-brick';
33091 if(this.href.length){
33092 cls += ' roo-brick-link';
33095 if(this.bgimage.length){
33096 cls += ' roo-brick-image';
33099 if(!this.html.length && !this.bgimage.length){
33100 cls += ' roo-brick-center-title';
33103 if(!this.html.length && this.bgimage.length){
33104 cls += ' roo-brick-bottom-title';
33108 cls += ' ' + this.cls;
33112 tag: (this.href.length) ? 'a' : 'div',
33117 cls: 'roo-brick-paragraph',
33123 if(this.href.length){
33124 cfg.href = this.href;
33127 var cn = cfg.cn[0].cn;
33129 if(this.title.length){
33132 cls: 'roo-brick-title',
33137 if(this.html.length){
33140 cls: 'roo-brick-text',
33147 if(this.bgimage.length){
33150 cls: 'roo-brick-image-view',
33158 initEvents: function()
33160 if(this.title.length || this.html.length){
33161 this.el.on('mouseenter' ,this.enter, this);
33162 this.el.on('mouseleave', this.leave, this);
33165 Roo.EventManager.onWindowResize(this.resize, this);
33167 if(this.bgimage.length){
33168 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33169 this.imageEl.on('load', this.onImageLoad, this);
33176 onImageLoad : function()
33181 resize : function()
33183 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33185 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33187 if(this.bgimage.length){
33188 var image = this.el.select('.roo-brick-image-view', true).first();
33190 image.setWidth(paragraph.getWidth());
33193 image.setHeight(paragraph.getWidth());
33196 this.el.setHeight(image.getHeight());
33197 paragraph.setHeight(image.getHeight());
33203 enter: function(e, el)
33205 e.preventDefault();
33207 if(this.bgimage.length){
33208 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33209 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33213 leave: function(e, el)
33215 e.preventDefault();
33217 if(this.bgimage.length){
33218 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33219 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33234 * @class Roo.bootstrap.NumberField
33235 * @extends Roo.bootstrap.Input
33236 * Bootstrap NumberField class
33242 * Create a new NumberField
33243 * @param {Object} config The config object
33246 Roo.bootstrap.NumberField = function(config){
33247 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33250 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33253 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33255 allowDecimals : true,
33257 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33259 decimalSeparator : ".",
33261 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33263 decimalPrecision : 2,
33265 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33267 allowNegative : true,
33270 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33274 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33276 minValue : Number.NEGATIVE_INFINITY,
33278 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33280 maxValue : Number.MAX_VALUE,
33282 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33284 minText : "The minimum value for this field is {0}",
33286 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33288 maxText : "The maximum value for this field is {0}",
33290 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33291 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33293 nanText : "{0} is not a valid number",
33295 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33297 thousandsDelimiter : false,
33299 * @cfg {String} valueAlign alignment of value
33301 valueAlign : "left",
33303 getAutoCreate : function()
33305 var hiddenInput = {
33309 cls: 'hidden-number-input'
33313 hiddenInput.name = this.name;
33318 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33320 this.name = hiddenInput.name;
33322 if(cfg.cn.length > 0) {
33323 cfg.cn.push(hiddenInput);
33330 initEvents : function()
33332 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33334 var allowed = "0123456789";
33336 if(this.allowDecimals){
33337 allowed += this.decimalSeparator;
33340 if(this.allowNegative){
33344 if(this.thousandsDelimiter) {
33348 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33350 var keyPress = function(e){
33352 var k = e.getKey();
33354 var c = e.getCharCode();
33357 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33358 allowed.indexOf(String.fromCharCode(c)) === -1
33364 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33368 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33373 this.el.on("keypress", keyPress, this);
33376 validateValue : function(value)
33379 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33383 var num = this.parseValue(value);
33386 this.markInvalid(String.format(this.nanText, value));
33390 if(num < this.minValue){
33391 this.markInvalid(String.format(this.minText, this.minValue));
33395 if(num > this.maxValue){
33396 this.markInvalid(String.format(this.maxText, this.maxValue));
33403 getValue : function()
33405 var v = this.hiddenEl().getValue();
33407 return this.fixPrecision(this.parseValue(v));
33410 parseValue : function(value)
33412 if(this.thousandsDelimiter) {
33414 r = new RegExp(",", "g");
33415 value = value.replace(r, "");
33418 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33419 return isNaN(value) ? '' : value;
33422 fixPrecision : function(value)
33424 if(this.thousandsDelimiter) {
33426 r = new RegExp(",", "g");
33427 value = value.replace(r, "");
33430 var nan = isNaN(value);
33432 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33433 return nan ? '' : value;
33435 return parseFloat(value).toFixed(this.decimalPrecision);
33438 setValue : function(v)
33440 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33446 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33448 this.inputEl().dom.value = (v == '') ? '' :
33449 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33451 if(!this.allowZero && v === '0') {
33452 this.hiddenEl().dom.value = '';
33453 this.inputEl().dom.value = '';
33460 decimalPrecisionFcn : function(v)
33462 return Math.floor(v);
33465 beforeBlur : function()
33467 var v = this.parseValue(this.getRawValue());
33469 if(v || v === 0 || v === ''){
33474 hiddenEl : function()
33476 return this.el.select('input.hidden-number-input',true).first();
33488 * @class Roo.bootstrap.DocumentSlider
33489 * @extends Roo.bootstrap.Component
33490 * Bootstrap DocumentSlider class
33493 * Create a new DocumentViewer
33494 * @param {Object} config The config object
33497 Roo.bootstrap.DocumentSlider = function(config){
33498 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33505 * Fire after initEvent
33506 * @param {Roo.bootstrap.DocumentSlider} this
33511 * Fire after update
33512 * @param {Roo.bootstrap.DocumentSlider} this
33518 * @param {Roo.bootstrap.DocumentSlider} this
33524 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33530 getAutoCreate : function()
33534 cls : 'roo-document-slider',
33538 cls : 'roo-document-slider-header',
33542 cls : 'roo-document-slider-header-title'
33548 cls : 'roo-document-slider-body',
33552 cls : 'roo-document-slider-prev',
33556 cls : 'fa fa-chevron-left'
33562 cls : 'roo-document-slider-thumb',
33566 cls : 'roo-document-slider-image'
33572 cls : 'roo-document-slider-next',
33576 cls : 'fa fa-chevron-right'
33588 initEvents : function()
33590 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33591 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33593 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33594 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33596 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33597 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33599 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33600 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33602 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33603 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33605 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33606 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33608 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33609 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33611 this.thumbEl.on('click', this.onClick, this);
33613 this.prevIndicator.on('click', this.prev, this);
33615 this.nextIndicator.on('click', this.next, this);
33619 initial : function()
33621 if(this.files.length){
33622 this.indicator = 1;
33626 this.fireEvent('initial', this);
33629 update : function()
33631 this.imageEl.attr('src', this.files[this.indicator - 1]);
33633 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33635 this.prevIndicator.show();
33637 if(this.indicator == 1){
33638 this.prevIndicator.hide();
33641 this.nextIndicator.show();
33643 if(this.indicator == this.files.length){
33644 this.nextIndicator.hide();
33647 this.thumbEl.scrollTo('top');
33649 this.fireEvent('update', this);
33652 onClick : function(e)
33654 e.preventDefault();
33656 this.fireEvent('click', this);
33661 e.preventDefault();
33663 this.indicator = Math.max(1, this.indicator - 1);
33670 e.preventDefault();
33672 this.indicator = Math.min(this.files.length, this.indicator + 1);
33686 * @class Roo.bootstrap.RadioSet
33687 * @extends Roo.bootstrap.Input
33688 * Bootstrap RadioSet class
33689 * @cfg {String} indicatorpos (left|right) default left
33690 * @cfg {Boolean} inline (true|false) inline the element (default true)
33691 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33693 * Create a new RadioSet
33694 * @param {Object} config The config object
33697 Roo.bootstrap.RadioSet = function(config){
33699 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33703 Roo.bootstrap.RadioSet.register(this);
33708 * Fires when the element is checked or unchecked.
33709 * @param {Roo.bootstrap.RadioSet} this This radio
33710 * @param {Roo.bootstrap.Radio} item The checked item
33715 * Fires when the element is click.
33716 * @param {Roo.bootstrap.RadioSet} this This radio set
33717 * @param {Roo.bootstrap.Radio} item The checked item
33718 * @param {Roo.EventObject} e The event object
33725 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33733 indicatorpos : 'left',
33735 getAutoCreate : function()
33739 cls : 'roo-radio-set-label',
33743 html : this.fieldLabel
33748 if(this.indicatorpos == 'left'){
33751 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33752 tooltip : 'This field is required'
33757 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33758 tooltip : 'This field is required'
33764 cls : 'roo-radio-set-items'
33767 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33769 if (align === 'left' && this.fieldLabel.length) {
33772 cls : "roo-radio-set-right",
33778 if(this.labelWidth > 12){
33779 label.style = "width: " + this.labelWidth + 'px';
33782 if(this.labelWidth < 13 && this.labelmd == 0){
33783 this.labelmd = this.labelWidth;
33786 if(this.labellg > 0){
33787 label.cls += ' col-lg-' + this.labellg;
33788 items.cls += ' col-lg-' + (12 - this.labellg);
33791 if(this.labelmd > 0){
33792 label.cls += ' col-md-' + this.labelmd;
33793 items.cls += ' col-md-' + (12 - this.labelmd);
33796 if(this.labelsm > 0){
33797 label.cls += ' col-sm-' + this.labelsm;
33798 items.cls += ' col-sm-' + (12 - this.labelsm);
33801 if(this.labelxs > 0){
33802 label.cls += ' col-xs-' + this.labelxs;
33803 items.cls += ' col-xs-' + (12 - this.labelxs);
33809 cls : 'roo-radio-set',
33813 cls : 'roo-radio-set-input',
33816 value : this.value ? this.value : ''
33823 if(this.weight.length){
33824 cfg.cls += ' roo-radio-' + this.weight;
33828 cfg.cls += ' roo-radio-set-inline';
33832 ['xs','sm','md','lg'].map(function(size){
33833 if (settings[size]) {
33834 cfg.cls += ' col-' + size + '-' + settings[size];
33842 initEvents : function()
33844 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33845 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33847 if(!this.fieldLabel.length){
33848 this.labelEl.hide();
33851 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33852 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33854 this.indicator = this.indicatorEl();
33856 if(this.indicator){
33857 this.indicator.addClass('invisible');
33860 this.originalValue = this.getValue();
33864 inputEl: function ()
33866 return this.el.select('.roo-radio-set-input', true).first();
33869 getChildContainer : function()
33871 return this.itemsEl;
33874 register : function(item)
33876 this.radioes.push(item);
33880 validate : function()
33882 if(this.getVisibilityEl().hasClass('hidden')){
33888 Roo.each(this.radioes, function(i){
33897 if(this.allowBlank) {
33901 if(this.disabled || valid){
33906 this.markInvalid();
33911 markValid : function()
33913 if(this.labelEl.isVisible(true)){
33914 this.indicatorEl().removeClass('visible');
33915 this.indicatorEl().addClass('invisible');
33918 this.el.removeClass([this.invalidClass, this.validClass]);
33919 this.el.addClass(this.validClass);
33921 this.fireEvent('valid', this);
33924 markInvalid : function(msg)
33926 if(this.allowBlank || this.disabled){
33930 if(this.labelEl.isVisible(true)){
33931 this.indicatorEl().removeClass('invisible');
33932 this.indicatorEl().addClass('visible');
33935 this.el.removeClass([this.invalidClass, this.validClass]);
33936 this.el.addClass(this.invalidClass);
33938 this.fireEvent('invalid', this, msg);
33942 setValue : function(v, suppressEvent)
33944 if(this.value === v){
33951 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33954 Roo.each(this.radioes, function(i){
33956 i.el.removeClass('checked');
33959 Roo.each(this.radioes, function(i){
33961 if(i.value === v || i.value.toString() === v.toString()){
33963 i.el.addClass('checked');
33965 if(suppressEvent !== true){
33966 this.fireEvent('check', this, i);
33977 clearInvalid : function(){
33979 if(!this.el || this.preventMark){
33983 this.el.removeClass([this.invalidClass]);
33985 this.fireEvent('valid', this);
33990 Roo.apply(Roo.bootstrap.RadioSet, {
33994 register : function(set)
33996 this.groups[set.name] = set;
33999 get: function(name)
34001 if (typeof(this.groups[name]) == 'undefined') {
34005 return this.groups[name] ;
34011 * Ext JS Library 1.1.1
34012 * Copyright(c) 2006-2007, Ext JS, LLC.
34014 * Originally Released Under LGPL - original licence link has changed is not relivant.
34017 * <script type="text/javascript">
34022 * @class Roo.bootstrap.SplitBar
34023 * @extends Roo.util.Observable
34024 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34028 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34029 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34030 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34031 split.minSize = 100;
34032 split.maxSize = 600;
34033 split.animate = true;
34034 split.on('moved', splitterMoved);
34037 * Create a new SplitBar
34038 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34039 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34040 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34041 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34042 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34043 position of the SplitBar).
34045 Roo.bootstrap.SplitBar = function(cfg){
34050 // dragElement : elm
34051 // resizingElement: el,
34053 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34054 // placement : Roo.bootstrap.SplitBar.LEFT ,
34055 // existingProxy ???
34058 this.el = Roo.get(cfg.dragElement, true);
34059 this.el.dom.unselectable = "on";
34061 this.resizingEl = Roo.get(cfg.resizingElement, true);
34065 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34066 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34069 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34072 * The minimum size of the resizing element. (Defaults to 0)
34078 * The maximum size of the resizing element. (Defaults to 2000)
34081 this.maxSize = 2000;
34084 * Whether to animate the transition to the new size
34087 this.animate = false;
34090 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34093 this.useShim = false;
34098 if(!cfg.existingProxy){
34100 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34102 this.proxy = Roo.get(cfg.existingProxy).dom;
34105 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34108 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34111 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34114 this.dragSpecs = {};
34117 * @private The adapter to use to positon and resize elements
34119 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34120 this.adapter.init(this);
34122 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34124 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34125 this.el.addClass("roo-splitbar-h");
34128 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34129 this.el.addClass("roo-splitbar-v");
34135 * Fires when the splitter is moved (alias for {@link #event-moved})
34136 * @param {Roo.bootstrap.SplitBar} this
34137 * @param {Number} newSize the new width or height
34142 * Fires when the splitter is moved
34143 * @param {Roo.bootstrap.SplitBar} this
34144 * @param {Number} newSize the new width or height
34148 * @event beforeresize
34149 * Fires before the splitter is dragged
34150 * @param {Roo.bootstrap.SplitBar} this
34152 "beforeresize" : true,
34154 "beforeapply" : true
34157 Roo.util.Observable.call(this);
34160 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34161 onStartProxyDrag : function(x, y){
34162 this.fireEvent("beforeresize", this);
34164 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34166 o.enableDisplayMode("block");
34167 // all splitbars share the same overlay
34168 Roo.bootstrap.SplitBar.prototype.overlay = o;
34170 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34171 this.overlay.show();
34172 Roo.get(this.proxy).setDisplayed("block");
34173 var size = this.adapter.getElementSize(this);
34174 this.activeMinSize = this.getMinimumSize();;
34175 this.activeMaxSize = this.getMaximumSize();;
34176 var c1 = size - this.activeMinSize;
34177 var c2 = Math.max(this.activeMaxSize - size, 0);
34178 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34179 this.dd.resetConstraints();
34180 this.dd.setXConstraint(
34181 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34182 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34184 this.dd.setYConstraint(0, 0);
34186 this.dd.resetConstraints();
34187 this.dd.setXConstraint(0, 0);
34188 this.dd.setYConstraint(
34189 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34190 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34193 this.dragSpecs.startSize = size;
34194 this.dragSpecs.startPoint = [x, y];
34195 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34199 * @private Called after the drag operation by the DDProxy
34201 onEndProxyDrag : function(e){
34202 Roo.get(this.proxy).setDisplayed(false);
34203 var endPoint = Roo.lib.Event.getXY(e);
34205 this.overlay.hide();
34208 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34209 newSize = this.dragSpecs.startSize +
34210 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34211 endPoint[0] - this.dragSpecs.startPoint[0] :
34212 this.dragSpecs.startPoint[0] - endPoint[0]
34215 newSize = this.dragSpecs.startSize +
34216 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34217 endPoint[1] - this.dragSpecs.startPoint[1] :
34218 this.dragSpecs.startPoint[1] - endPoint[1]
34221 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34222 if(newSize != this.dragSpecs.startSize){
34223 if(this.fireEvent('beforeapply', this, newSize) !== false){
34224 this.adapter.setElementSize(this, newSize);
34225 this.fireEvent("moved", this, newSize);
34226 this.fireEvent("resize", this, newSize);
34232 * Get the adapter this SplitBar uses
34233 * @return The adapter object
34235 getAdapter : function(){
34236 return this.adapter;
34240 * Set the adapter this SplitBar uses
34241 * @param {Object} adapter A SplitBar adapter object
34243 setAdapter : function(adapter){
34244 this.adapter = adapter;
34245 this.adapter.init(this);
34249 * Gets the minimum size for the resizing element
34250 * @return {Number} The minimum size
34252 getMinimumSize : function(){
34253 return this.minSize;
34257 * Sets the minimum size for the resizing element
34258 * @param {Number} minSize The minimum size
34260 setMinimumSize : function(minSize){
34261 this.minSize = minSize;
34265 * Gets the maximum size for the resizing element
34266 * @return {Number} The maximum size
34268 getMaximumSize : function(){
34269 return this.maxSize;
34273 * Sets the maximum size for the resizing element
34274 * @param {Number} maxSize The maximum size
34276 setMaximumSize : function(maxSize){
34277 this.maxSize = maxSize;
34281 * Sets the initialize size for the resizing element
34282 * @param {Number} size The initial size
34284 setCurrentSize : function(size){
34285 var oldAnimate = this.animate;
34286 this.animate = false;
34287 this.adapter.setElementSize(this, size);
34288 this.animate = oldAnimate;
34292 * Destroy this splitbar.
34293 * @param {Boolean} removeEl True to remove the element
34295 destroy : function(removeEl){
34297 this.shim.remove();
34300 this.proxy.parentNode.removeChild(this.proxy);
34308 * @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.
34310 Roo.bootstrap.SplitBar.createProxy = function(dir){
34311 var proxy = new Roo.Element(document.createElement("div"));
34312 proxy.unselectable();
34313 var cls = 'roo-splitbar-proxy';
34314 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34315 document.body.appendChild(proxy.dom);
34320 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34321 * Default Adapter. It assumes the splitter and resizing element are not positioned
34322 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34324 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34327 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34328 // do nothing for now
34329 init : function(s){
34333 * Called before drag operations to get the current size of the resizing element.
34334 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34336 getElementSize : function(s){
34337 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34338 return s.resizingEl.getWidth();
34340 return s.resizingEl.getHeight();
34345 * Called after drag operations to set the size of the resizing element.
34346 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34347 * @param {Number} newSize The new size to set
34348 * @param {Function} onComplete A function to be invoked when resizing is complete
34350 setElementSize : function(s, newSize, onComplete){
34351 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34353 s.resizingEl.setWidth(newSize);
34355 onComplete(s, newSize);
34358 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34363 s.resizingEl.setHeight(newSize);
34365 onComplete(s, newSize);
34368 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34375 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34376 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34377 * Adapter that moves the splitter element to align with the resized sizing element.
34378 * Used with an absolute positioned SplitBar.
34379 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34380 * document.body, make sure you assign an id to the body element.
34382 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34383 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34384 this.container = Roo.get(container);
34387 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34388 init : function(s){
34389 this.basic.init(s);
34392 getElementSize : function(s){
34393 return this.basic.getElementSize(s);
34396 setElementSize : function(s, newSize, onComplete){
34397 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34400 moveSplitter : function(s){
34401 var yes = Roo.bootstrap.SplitBar;
34402 switch(s.placement){
34404 s.el.setX(s.resizingEl.getRight());
34407 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34410 s.el.setY(s.resizingEl.getBottom());
34413 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34420 * Orientation constant - Create a vertical SplitBar
34424 Roo.bootstrap.SplitBar.VERTICAL = 1;
34427 * Orientation constant - Create a horizontal SplitBar
34431 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34434 * Placement constant - The resizing element is to the left of the splitter element
34438 Roo.bootstrap.SplitBar.LEFT = 1;
34441 * Placement constant - The resizing element is to the right of the splitter element
34445 Roo.bootstrap.SplitBar.RIGHT = 2;
34448 * Placement constant - The resizing element is positioned above the splitter element
34452 Roo.bootstrap.SplitBar.TOP = 3;
34455 * Placement constant - The resizing element is positioned under splitter element
34459 Roo.bootstrap.SplitBar.BOTTOM = 4;
34460 Roo.namespace("Roo.bootstrap.layout");/*
34462 * Ext JS Library 1.1.1
34463 * Copyright(c) 2006-2007, Ext JS, LLC.
34465 * Originally Released Under LGPL - original licence link has changed is not relivant.
34468 * <script type="text/javascript">
34472 * @class Roo.bootstrap.layout.Manager
34473 * @extends Roo.bootstrap.Component
34474 * Base class for layout managers.
34476 Roo.bootstrap.layout.Manager = function(config)
34478 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34484 /** false to disable window resize monitoring @type Boolean */
34485 this.monitorWindowResize = true;
34490 * Fires when a layout is performed.
34491 * @param {Roo.LayoutManager} this
34495 * @event regionresized
34496 * Fires when the user resizes a region.
34497 * @param {Roo.LayoutRegion} region The resized region
34498 * @param {Number} newSize The new size (width for east/west, height for north/south)
34500 "regionresized" : true,
34502 * @event regioncollapsed
34503 * Fires when a region is collapsed.
34504 * @param {Roo.LayoutRegion} region The collapsed region
34506 "regioncollapsed" : true,
34508 * @event regionexpanded
34509 * Fires when a region is expanded.
34510 * @param {Roo.LayoutRegion} region The expanded region
34512 "regionexpanded" : true
34514 this.updating = false;
34517 this.el = Roo.get(config.el);
34523 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34528 monitorWindowResize : true,
34534 onRender : function(ct, position)
34537 this.el = Roo.get(ct);
34540 //this.fireEvent('render',this);
34544 initEvents: function()
34548 // ie scrollbar fix
34549 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34550 document.body.scroll = "no";
34551 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34552 this.el.position('relative');
34554 this.id = this.el.id;
34555 this.el.addClass("roo-layout-container");
34556 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34557 if(this.el.dom != document.body ) {
34558 this.el.on('resize', this.layout,this);
34559 this.el.on('show', this.layout,this);
34565 * Returns true if this layout is currently being updated
34566 * @return {Boolean}
34568 isUpdating : function(){
34569 return this.updating;
34573 * Suspend the LayoutManager from doing auto-layouts while
34574 * making multiple add or remove calls
34576 beginUpdate : function(){
34577 this.updating = true;
34581 * Restore auto-layouts and optionally disable the manager from performing a layout
34582 * @param {Boolean} noLayout true to disable a layout update
34584 endUpdate : function(noLayout){
34585 this.updating = false;
34591 layout: function(){
34595 onRegionResized : function(region, newSize){
34596 this.fireEvent("regionresized", region, newSize);
34600 onRegionCollapsed : function(region){
34601 this.fireEvent("regioncollapsed", region);
34604 onRegionExpanded : function(region){
34605 this.fireEvent("regionexpanded", region);
34609 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34610 * performs box-model adjustments.
34611 * @return {Object} The size as an object {width: (the width), height: (the height)}
34613 getViewSize : function()
34616 if(this.el.dom != document.body){
34617 size = this.el.getSize();
34619 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34621 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34622 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34627 * Returns the Element this layout is bound to.
34628 * @return {Roo.Element}
34630 getEl : function(){
34635 * Returns the specified region.
34636 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34637 * @return {Roo.LayoutRegion}
34639 getRegion : function(target){
34640 return this.regions[target.toLowerCase()];
34643 onWindowResize : function(){
34644 if(this.monitorWindowResize){
34651 * Ext JS Library 1.1.1
34652 * Copyright(c) 2006-2007, Ext JS, LLC.
34654 * Originally Released Under LGPL - original licence link has changed is not relivant.
34657 * <script type="text/javascript">
34660 * @class Roo.bootstrap.layout.Border
34661 * @extends Roo.bootstrap.layout.Manager
34662 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34663 * please see: examples/bootstrap/nested.html<br><br>
34665 <b>The container the layout is rendered into can be either the body element or any other element.
34666 If it is not the body element, the container needs to either be an absolute positioned element,
34667 or you will need to add "position:relative" to the css of the container. You will also need to specify
34668 the container size if it is not the body element.</b>
34671 * Create a new Border
34672 * @param {Object} config Configuration options
34674 Roo.bootstrap.layout.Border = function(config){
34675 config = config || {};
34676 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34680 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34681 if(config[region]){
34682 config[region].region = region;
34683 this.addRegion(config[region]);
34689 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34691 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34693 * Creates and adds a new region if it doesn't already exist.
34694 * @param {String} target The target region key (north, south, east, west or center).
34695 * @param {Object} config The regions config object
34696 * @return {BorderLayoutRegion} The new region
34698 addRegion : function(config)
34700 if(!this.regions[config.region]){
34701 var r = this.factory(config);
34702 this.bindRegion(r);
34704 return this.regions[config.region];
34708 bindRegion : function(r){
34709 this.regions[r.config.region] = r;
34711 r.on("visibilitychange", this.layout, this);
34712 r.on("paneladded", this.layout, this);
34713 r.on("panelremoved", this.layout, this);
34714 r.on("invalidated", this.layout, this);
34715 r.on("resized", this.onRegionResized, this);
34716 r.on("collapsed", this.onRegionCollapsed, this);
34717 r.on("expanded", this.onRegionExpanded, this);
34721 * Performs a layout update.
34723 layout : function()
34725 if(this.updating) {
34729 // render all the rebions if they have not been done alreayd?
34730 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34731 if(this.regions[region] && !this.regions[region].bodyEl){
34732 this.regions[region].onRender(this.el)
34736 var size = this.getViewSize();
34737 var w = size.width;
34738 var h = size.height;
34743 //var x = 0, y = 0;
34745 var rs = this.regions;
34746 var north = rs["north"];
34747 var south = rs["south"];
34748 var west = rs["west"];
34749 var east = rs["east"];
34750 var center = rs["center"];
34751 //if(this.hideOnLayout){ // not supported anymore
34752 //c.el.setStyle("display", "none");
34754 if(north && north.isVisible()){
34755 var b = north.getBox();
34756 var m = north.getMargins();
34757 b.width = w - (m.left+m.right);
34760 centerY = b.height + b.y + m.bottom;
34761 centerH -= centerY;
34762 north.updateBox(this.safeBox(b));
34764 if(south && south.isVisible()){
34765 var b = south.getBox();
34766 var m = south.getMargins();
34767 b.width = w - (m.left+m.right);
34769 var totalHeight = (b.height + m.top + m.bottom);
34770 b.y = h - totalHeight + m.top;
34771 centerH -= totalHeight;
34772 south.updateBox(this.safeBox(b));
34774 if(west && west.isVisible()){
34775 var b = west.getBox();
34776 var m = west.getMargins();
34777 b.height = centerH - (m.top+m.bottom);
34779 b.y = centerY + m.top;
34780 var totalWidth = (b.width + m.left + m.right);
34781 centerX += totalWidth;
34782 centerW -= totalWidth;
34783 west.updateBox(this.safeBox(b));
34785 if(east && east.isVisible()){
34786 var b = east.getBox();
34787 var m = east.getMargins();
34788 b.height = centerH - (m.top+m.bottom);
34789 var totalWidth = (b.width + m.left + m.right);
34790 b.x = w - totalWidth + m.left;
34791 b.y = centerY + m.top;
34792 centerW -= totalWidth;
34793 east.updateBox(this.safeBox(b));
34796 var m = center.getMargins();
34798 x: centerX + m.left,
34799 y: centerY + m.top,
34800 width: centerW - (m.left+m.right),
34801 height: centerH - (m.top+m.bottom)
34803 //if(this.hideOnLayout){
34804 //center.el.setStyle("display", "block");
34806 center.updateBox(this.safeBox(centerBox));
34809 this.fireEvent("layout", this);
34813 safeBox : function(box){
34814 box.width = Math.max(0, box.width);
34815 box.height = Math.max(0, box.height);
34820 * Adds a ContentPanel (or subclass) to this layout.
34821 * @param {String} target The target region key (north, south, east, west or center).
34822 * @param {Roo.ContentPanel} panel The panel to add
34823 * @return {Roo.ContentPanel} The added panel
34825 add : function(target, panel){
34827 target = target.toLowerCase();
34828 return this.regions[target].add(panel);
34832 * Remove a ContentPanel (or subclass) to this layout.
34833 * @param {String} target The target region key (north, south, east, west or center).
34834 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34835 * @return {Roo.ContentPanel} The removed panel
34837 remove : function(target, panel){
34838 target = target.toLowerCase();
34839 return this.regions[target].remove(panel);
34843 * Searches all regions for a panel with the specified id
34844 * @param {String} panelId
34845 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34847 findPanel : function(panelId){
34848 var rs = this.regions;
34849 for(var target in rs){
34850 if(typeof rs[target] != "function"){
34851 var p = rs[target].getPanel(panelId);
34861 * Searches all regions for a panel with the specified id and activates (shows) it.
34862 * @param {String/ContentPanel} panelId The panels id or the panel itself
34863 * @return {Roo.ContentPanel} The shown panel or null
34865 showPanel : function(panelId) {
34866 var rs = this.regions;
34867 for(var target in rs){
34868 var r = rs[target];
34869 if(typeof r != "function"){
34870 if(r.hasPanel(panelId)){
34871 return r.showPanel(panelId);
34879 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34880 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34883 restoreState : function(provider){
34885 provider = Roo.state.Manager;
34887 var sm = new Roo.LayoutStateManager();
34888 sm.init(this, provider);
34894 * Adds a xtype elements to the layout.
34898 xtype : 'ContentPanel',
34905 xtype : 'NestedLayoutPanel',
34911 items : [ ... list of content panels or nested layout panels.. ]
34915 * @param {Object} cfg Xtype definition of item to add.
34917 addxtype : function(cfg)
34919 // basically accepts a pannel...
34920 // can accept a layout region..!?!?
34921 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34924 // theory? children can only be panels??
34926 //if (!cfg.xtype.match(/Panel$/)) {
34931 if (typeof(cfg.region) == 'undefined') {
34932 Roo.log("Failed to add Panel, region was not set");
34936 var region = cfg.region;
34942 xitems = cfg.items;
34949 case 'Content': // ContentPanel (el, cfg)
34950 case 'Scroll': // ContentPanel (el, cfg)
34952 cfg.autoCreate = true;
34953 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34955 // var el = this.el.createChild();
34956 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34959 this.add(region, ret);
34963 case 'TreePanel': // our new panel!
34964 cfg.el = this.el.createChild();
34965 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34966 this.add(region, ret);
34971 // create a new Layout (which is a Border Layout...
34973 var clayout = cfg.layout;
34974 clayout.el = this.el.createChild();
34975 clayout.items = clayout.items || [];
34979 // replace this exitems with the clayout ones..
34980 xitems = clayout.items;
34982 // force background off if it's in center...
34983 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34984 cfg.background = false;
34986 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34989 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34990 //console.log('adding nested layout panel ' + cfg.toSource());
34991 this.add(region, ret);
34992 nb = {}; /// find first...
34997 // needs grid and region
34999 //var el = this.getRegion(region).el.createChild();
35001 *var el = this.el.createChild();
35002 // create the grid first...
35003 cfg.grid.container = el;
35004 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35007 if (region == 'center' && this.active ) {
35008 cfg.background = false;
35011 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35013 this.add(region, ret);
35015 if (cfg.background) {
35016 // render grid on panel activation (if panel background)
35017 ret.on('activate', function(gp) {
35018 if (!gp.grid.rendered) {
35019 // gp.grid.render(el);
35023 // cfg.grid.render(el);
35029 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35030 // it was the old xcomponent building that caused this before.
35031 // espeically if border is the top element in the tree.
35041 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35043 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35044 this.add(region, ret);
35048 throw "Can not add '" + cfg.xtype + "' to Border";
35054 this.beginUpdate();
35058 Roo.each(xitems, function(i) {
35059 region = nb && i.region ? i.region : false;
35061 var add = ret.addxtype(i);
35064 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35065 if (!i.background) {
35066 abn[region] = nb[region] ;
35073 // make the last non-background panel active..
35074 //if (nb) { Roo.log(abn); }
35077 for(var r in abn) {
35078 region = this.getRegion(r);
35080 // tried using nb[r], but it does not work..
35082 region.showPanel(abn[r]);
35093 factory : function(cfg)
35096 var validRegions = Roo.bootstrap.layout.Border.regions;
35098 var target = cfg.region;
35101 var r = Roo.bootstrap.layout;
35105 return new r.North(cfg);
35107 return new r.South(cfg);
35109 return new r.East(cfg);
35111 return new r.West(cfg);
35113 return new r.Center(cfg);
35115 throw 'Layout region "'+target+'" not supported.';
35122 * Ext JS Library 1.1.1
35123 * Copyright(c) 2006-2007, Ext JS, LLC.
35125 * Originally Released Under LGPL - original licence link has changed is not relivant.
35128 * <script type="text/javascript">
35132 * @class Roo.bootstrap.layout.Basic
35133 * @extends Roo.util.Observable
35134 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35135 * and does not have a titlebar, tabs or any other features. All it does is size and position
35136 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35137 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35138 * @cfg {string} region the region that it inhabits..
35139 * @cfg {bool} skipConfig skip config?
35143 Roo.bootstrap.layout.Basic = function(config){
35145 this.mgr = config.mgr;
35147 this.position = config.region;
35149 var skipConfig = config.skipConfig;
35153 * @scope Roo.BasicLayoutRegion
35157 * @event beforeremove
35158 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35159 * @param {Roo.LayoutRegion} this
35160 * @param {Roo.ContentPanel} panel The panel
35161 * @param {Object} e The cancel event object
35163 "beforeremove" : true,
35165 * @event invalidated
35166 * Fires when the layout for this region is changed.
35167 * @param {Roo.LayoutRegion} this
35169 "invalidated" : true,
35171 * @event visibilitychange
35172 * Fires when this region is shown or hidden
35173 * @param {Roo.LayoutRegion} this
35174 * @param {Boolean} visibility true or false
35176 "visibilitychange" : true,
35178 * @event paneladded
35179 * Fires when a panel is added.
35180 * @param {Roo.LayoutRegion} this
35181 * @param {Roo.ContentPanel} panel The panel
35183 "paneladded" : true,
35185 * @event panelremoved
35186 * Fires when a panel is removed.
35187 * @param {Roo.LayoutRegion} this
35188 * @param {Roo.ContentPanel} panel The panel
35190 "panelremoved" : true,
35192 * @event beforecollapse
35193 * Fires when this region before collapse.
35194 * @param {Roo.LayoutRegion} this
35196 "beforecollapse" : true,
35199 * Fires when this region is collapsed.
35200 * @param {Roo.LayoutRegion} this
35202 "collapsed" : true,
35205 * Fires when this region is expanded.
35206 * @param {Roo.LayoutRegion} this
35211 * Fires when this region is slid into view.
35212 * @param {Roo.LayoutRegion} this
35214 "slideshow" : true,
35217 * Fires when this region slides out of view.
35218 * @param {Roo.LayoutRegion} this
35220 "slidehide" : true,
35222 * @event panelactivated
35223 * Fires when a panel is activated.
35224 * @param {Roo.LayoutRegion} this
35225 * @param {Roo.ContentPanel} panel The activated panel
35227 "panelactivated" : true,
35230 * Fires when the user resizes this region.
35231 * @param {Roo.LayoutRegion} this
35232 * @param {Number} newSize The new size (width for east/west, height for north/south)
35236 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35237 this.panels = new Roo.util.MixedCollection();
35238 this.panels.getKey = this.getPanelId.createDelegate(this);
35240 this.activePanel = null;
35241 // ensure listeners are added...
35243 if (config.listeners || config.events) {
35244 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35245 listeners : config.listeners || {},
35246 events : config.events || {}
35250 if(skipConfig !== true){
35251 this.applyConfig(config);
35255 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35257 getPanelId : function(p){
35261 applyConfig : function(config){
35262 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35263 this.config = config;
35268 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35269 * the width, for horizontal (north, south) the height.
35270 * @param {Number} newSize The new width or height
35272 resizeTo : function(newSize){
35273 var el = this.el ? this.el :
35274 (this.activePanel ? this.activePanel.getEl() : null);
35276 switch(this.position){
35279 el.setWidth(newSize);
35280 this.fireEvent("resized", this, newSize);
35284 el.setHeight(newSize);
35285 this.fireEvent("resized", this, newSize);
35291 getBox : function(){
35292 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35295 getMargins : function(){
35296 return this.margins;
35299 updateBox : function(box){
35301 var el = this.activePanel.getEl();
35302 el.dom.style.left = box.x + "px";
35303 el.dom.style.top = box.y + "px";
35304 this.activePanel.setSize(box.width, box.height);
35308 * Returns the container element for this region.
35309 * @return {Roo.Element}
35311 getEl : function(){
35312 return this.activePanel;
35316 * Returns true if this region is currently visible.
35317 * @return {Boolean}
35319 isVisible : function(){
35320 return this.activePanel ? true : false;
35323 setActivePanel : function(panel){
35324 panel = this.getPanel(panel);
35325 if(this.activePanel && this.activePanel != panel){
35326 this.activePanel.setActiveState(false);
35327 this.activePanel.getEl().setLeftTop(-10000,-10000);
35329 this.activePanel = panel;
35330 panel.setActiveState(true);
35332 panel.setSize(this.box.width, this.box.height);
35334 this.fireEvent("panelactivated", this, panel);
35335 this.fireEvent("invalidated");
35339 * Show the specified panel.
35340 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35341 * @return {Roo.ContentPanel} The shown panel or null
35343 showPanel : function(panel){
35344 panel = this.getPanel(panel);
35346 this.setActivePanel(panel);
35352 * Get the active panel for this region.
35353 * @return {Roo.ContentPanel} The active panel or null
35355 getActivePanel : function(){
35356 return this.activePanel;
35360 * Add the passed ContentPanel(s)
35361 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35362 * @return {Roo.ContentPanel} The panel added (if only one was added)
35364 add : function(panel){
35365 if(arguments.length > 1){
35366 for(var i = 0, len = arguments.length; i < len; i++) {
35367 this.add(arguments[i]);
35371 if(this.hasPanel(panel)){
35372 this.showPanel(panel);
35375 var el = panel.getEl();
35376 if(el.dom.parentNode != this.mgr.el.dom){
35377 this.mgr.el.dom.appendChild(el.dom);
35379 if(panel.setRegion){
35380 panel.setRegion(this);
35382 this.panels.add(panel);
35383 el.setStyle("position", "absolute");
35384 if(!panel.background){
35385 this.setActivePanel(panel);
35386 if(this.config.initialSize && this.panels.getCount()==1){
35387 this.resizeTo(this.config.initialSize);
35390 this.fireEvent("paneladded", this, panel);
35395 * Returns true if the panel is in this region.
35396 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35397 * @return {Boolean}
35399 hasPanel : function(panel){
35400 if(typeof panel == "object"){ // must be panel obj
35401 panel = panel.getId();
35403 return this.getPanel(panel) ? true : false;
35407 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35408 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35409 * @param {Boolean} preservePanel Overrides the config preservePanel option
35410 * @return {Roo.ContentPanel} The panel that was removed
35412 remove : function(panel, preservePanel){
35413 panel = this.getPanel(panel);
35418 this.fireEvent("beforeremove", this, panel, e);
35419 if(e.cancel === true){
35422 var panelId = panel.getId();
35423 this.panels.removeKey(panelId);
35428 * Returns the panel specified or null if it's not in this region.
35429 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35430 * @return {Roo.ContentPanel}
35432 getPanel : function(id){
35433 if(typeof id == "object"){ // must be panel obj
35436 return this.panels.get(id);
35440 * Returns this regions position (north/south/east/west/center).
35443 getPosition: function(){
35444 return this.position;
35448 * Ext JS Library 1.1.1
35449 * Copyright(c) 2006-2007, Ext JS, LLC.
35451 * Originally Released Under LGPL - original licence link has changed is not relivant.
35454 * <script type="text/javascript">
35458 * @class Roo.bootstrap.layout.Region
35459 * @extends Roo.bootstrap.layout.Basic
35460 * This class represents a region in a layout manager.
35462 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35463 * @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})
35464 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35465 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35466 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35467 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35468 * @cfg {String} title The title for the region (overrides panel titles)
35469 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35470 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35471 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35472 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35473 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35474 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35475 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35476 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35477 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35478 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35480 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35481 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35482 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35483 * @cfg {Number} width For East/West panels
35484 * @cfg {Number} height For North/South panels
35485 * @cfg {Boolean} split To show the splitter
35486 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35488 * @cfg {string} cls Extra CSS classes to add to region
35490 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35491 * @cfg {string} region the region that it inhabits..
35494 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35495 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35497 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35498 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35499 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35501 Roo.bootstrap.layout.Region = function(config)
35503 this.applyConfig(config);
35505 var mgr = config.mgr;
35506 var pos = config.region;
35507 config.skipConfig = true;
35508 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35511 this.onRender(mgr.el);
35514 this.visible = true;
35515 this.collapsed = false;
35516 this.unrendered_panels = [];
35519 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35521 position: '', // set by wrapper (eg. north/south etc..)
35522 unrendered_panels : null, // unrendered panels.
35523 createBody : function(){
35524 /** This region's body element
35525 * @type Roo.Element */
35526 this.bodyEl = this.el.createChild({
35528 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35532 onRender: function(ctr, pos)
35534 var dh = Roo.DomHelper;
35535 /** This region's container element
35536 * @type Roo.Element */
35537 this.el = dh.append(ctr.dom, {
35539 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35541 /** This region's title element
35542 * @type Roo.Element */
35544 this.titleEl = dh.append(this.el.dom,
35547 unselectable: "on",
35548 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35550 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35551 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35554 this.titleEl.enableDisplayMode();
35555 /** This region's title text element
35556 * @type HTMLElement */
35557 this.titleTextEl = this.titleEl.dom.firstChild;
35558 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35560 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35561 this.closeBtn.enableDisplayMode();
35562 this.closeBtn.on("click", this.closeClicked, this);
35563 this.closeBtn.hide();
35565 this.createBody(this.config);
35566 if(this.config.hideWhenEmpty){
35568 this.on("paneladded", this.validateVisibility, this);
35569 this.on("panelremoved", this.validateVisibility, this);
35571 if(this.autoScroll){
35572 this.bodyEl.setStyle("overflow", "auto");
35574 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35576 //if(c.titlebar !== false){
35577 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35578 this.titleEl.hide();
35580 this.titleEl.show();
35581 if(this.config.title){
35582 this.titleTextEl.innerHTML = this.config.title;
35586 if(this.config.collapsed){
35587 this.collapse(true);
35589 if(this.config.hidden){
35593 if (this.unrendered_panels && this.unrendered_panels.length) {
35594 for (var i =0;i< this.unrendered_panels.length; i++) {
35595 this.add(this.unrendered_panels[i]);
35597 this.unrendered_panels = null;
35603 applyConfig : function(c)
35606 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35607 var dh = Roo.DomHelper;
35608 if(c.titlebar !== false){
35609 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35610 this.collapseBtn.on("click", this.collapse, this);
35611 this.collapseBtn.enableDisplayMode();
35613 if(c.showPin === true || this.showPin){
35614 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35615 this.stickBtn.enableDisplayMode();
35616 this.stickBtn.on("click", this.expand, this);
35617 this.stickBtn.hide();
35622 /** This region's collapsed element
35623 * @type Roo.Element */
35626 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35627 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35630 if(c.floatable !== false){
35631 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35632 this.collapsedEl.on("click", this.collapseClick, this);
35635 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35636 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35637 id: "message", unselectable: "on", style:{"float":"left"}});
35638 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35640 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35641 this.expandBtn.on("click", this.expand, this);
35645 if(this.collapseBtn){
35646 this.collapseBtn.setVisible(c.collapsible == true);
35649 this.cmargins = c.cmargins || this.cmargins ||
35650 (this.position == "west" || this.position == "east" ?
35651 {top: 0, left: 2, right:2, bottom: 0} :
35652 {top: 2, left: 0, right:0, bottom: 2});
35654 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35657 this.bottomTabs = c.tabPosition != "top";
35659 this.autoScroll = c.autoScroll || false;
35664 this.duration = c.duration || .30;
35665 this.slideDuration = c.slideDuration || .45;
35670 * Returns true if this region is currently visible.
35671 * @return {Boolean}
35673 isVisible : function(){
35674 return this.visible;
35678 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35679 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35681 //setCollapsedTitle : function(title){
35682 // title = title || " ";
35683 // if(this.collapsedTitleTextEl){
35684 // this.collapsedTitleTextEl.innerHTML = title;
35688 getBox : function(){
35690 // if(!this.collapsed){
35691 b = this.el.getBox(false, true);
35693 // b = this.collapsedEl.getBox(false, true);
35698 getMargins : function(){
35699 return this.margins;
35700 //return this.collapsed ? this.cmargins : this.margins;
35703 highlight : function(){
35704 this.el.addClass("x-layout-panel-dragover");
35707 unhighlight : function(){
35708 this.el.removeClass("x-layout-panel-dragover");
35711 updateBox : function(box)
35713 if (!this.bodyEl) {
35714 return; // not rendered yet..
35718 if(!this.collapsed){
35719 this.el.dom.style.left = box.x + "px";
35720 this.el.dom.style.top = box.y + "px";
35721 this.updateBody(box.width, box.height);
35723 this.collapsedEl.dom.style.left = box.x + "px";
35724 this.collapsedEl.dom.style.top = box.y + "px";
35725 this.collapsedEl.setSize(box.width, box.height);
35728 this.tabs.autoSizeTabs();
35732 updateBody : function(w, h)
35735 this.el.setWidth(w);
35736 w -= this.el.getBorderWidth("rl");
35737 if(this.config.adjustments){
35738 w += this.config.adjustments[0];
35741 if(h !== null && h > 0){
35742 this.el.setHeight(h);
35743 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35744 h -= this.el.getBorderWidth("tb");
35745 if(this.config.adjustments){
35746 h += this.config.adjustments[1];
35748 this.bodyEl.setHeight(h);
35750 h = this.tabs.syncHeight(h);
35753 if(this.panelSize){
35754 w = w !== null ? w : this.panelSize.width;
35755 h = h !== null ? h : this.panelSize.height;
35757 if(this.activePanel){
35758 var el = this.activePanel.getEl();
35759 w = w !== null ? w : el.getWidth();
35760 h = h !== null ? h : el.getHeight();
35761 this.panelSize = {width: w, height: h};
35762 this.activePanel.setSize(w, h);
35764 if(Roo.isIE && this.tabs){
35765 this.tabs.el.repaint();
35770 * Returns the container element for this region.
35771 * @return {Roo.Element}
35773 getEl : function(){
35778 * Hides this region.
35781 //if(!this.collapsed){
35782 this.el.dom.style.left = "-2000px";
35785 // this.collapsedEl.dom.style.left = "-2000px";
35786 // this.collapsedEl.hide();
35788 this.visible = false;
35789 this.fireEvent("visibilitychange", this, false);
35793 * Shows this region if it was previously hidden.
35796 //if(!this.collapsed){
35799 // this.collapsedEl.show();
35801 this.visible = true;
35802 this.fireEvent("visibilitychange", this, true);
35805 closeClicked : function(){
35806 if(this.activePanel){
35807 this.remove(this.activePanel);
35811 collapseClick : function(e){
35813 e.stopPropagation();
35816 e.stopPropagation();
35822 * Collapses this region.
35823 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35826 collapse : function(skipAnim, skipCheck = false){
35827 if(this.collapsed) {
35831 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35833 this.collapsed = true;
35835 this.split.el.hide();
35837 if(this.config.animate && skipAnim !== true){
35838 this.fireEvent("invalidated", this);
35839 this.animateCollapse();
35841 this.el.setLocation(-20000,-20000);
35843 this.collapsedEl.show();
35844 this.fireEvent("collapsed", this);
35845 this.fireEvent("invalidated", this);
35851 animateCollapse : function(){
35856 * Expands this region if it was previously collapsed.
35857 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35858 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35861 expand : function(e, skipAnim){
35863 e.stopPropagation();
35865 if(!this.collapsed || this.el.hasActiveFx()) {
35869 this.afterSlideIn();
35872 this.collapsed = false;
35873 if(this.config.animate && skipAnim !== true){
35874 this.animateExpand();
35878 this.split.el.show();
35880 this.collapsedEl.setLocation(-2000,-2000);
35881 this.collapsedEl.hide();
35882 this.fireEvent("invalidated", this);
35883 this.fireEvent("expanded", this);
35887 animateExpand : function(){
35891 initTabs : function()
35893 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35895 var ts = new Roo.bootstrap.panel.Tabs({
35896 el: this.bodyEl.dom,
35897 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35898 disableTooltips: this.config.disableTabTips,
35899 toolbar : this.config.toolbar
35902 if(this.config.hideTabs){
35903 ts.stripWrap.setDisplayed(false);
35906 ts.resizeTabs = this.config.resizeTabs === true;
35907 ts.minTabWidth = this.config.minTabWidth || 40;
35908 ts.maxTabWidth = this.config.maxTabWidth || 250;
35909 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35910 ts.monitorResize = false;
35911 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35912 ts.bodyEl.addClass('roo-layout-tabs-body');
35913 this.panels.each(this.initPanelAsTab, this);
35916 initPanelAsTab : function(panel){
35917 var ti = this.tabs.addTab(
35921 this.config.closeOnTab && panel.isClosable(),
35924 if(panel.tabTip !== undefined){
35925 ti.setTooltip(panel.tabTip);
35927 ti.on("activate", function(){
35928 this.setActivePanel(panel);
35931 if(this.config.closeOnTab){
35932 ti.on("beforeclose", function(t, e){
35934 this.remove(panel);
35938 panel.tabItem = ti;
35943 updatePanelTitle : function(panel, title)
35945 if(this.activePanel == panel){
35946 this.updateTitle(title);
35949 var ti = this.tabs.getTab(panel.getEl().id);
35951 if(panel.tabTip !== undefined){
35952 ti.setTooltip(panel.tabTip);
35957 updateTitle : function(title){
35958 if(this.titleTextEl && !this.config.title){
35959 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35963 setActivePanel : function(panel)
35965 panel = this.getPanel(panel);
35966 if(this.activePanel && this.activePanel != panel){
35967 if(this.activePanel.setActiveState(false) === false){
35971 this.activePanel = panel;
35972 panel.setActiveState(true);
35973 if(this.panelSize){
35974 panel.setSize(this.panelSize.width, this.panelSize.height);
35977 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35979 this.updateTitle(panel.getTitle());
35981 this.fireEvent("invalidated", this);
35983 this.fireEvent("panelactivated", this, panel);
35987 * Shows the specified panel.
35988 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35989 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35991 showPanel : function(panel)
35993 panel = this.getPanel(panel);
35996 var tab = this.tabs.getTab(panel.getEl().id);
35997 if(tab.isHidden()){
35998 this.tabs.unhideTab(tab.id);
36002 this.setActivePanel(panel);
36009 * Get the active panel for this region.
36010 * @return {Roo.ContentPanel} The active panel or null
36012 getActivePanel : function(){
36013 return this.activePanel;
36016 validateVisibility : function(){
36017 if(this.panels.getCount() < 1){
36018 this.updateTitle(" ");
36019 this.closeBtn.hide();
36022 if(!this.isVisible()){
36029 * Adds the passed ContentPanel(s) to this region.
36030 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36031 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36033 add : function(panel)
36035 if(arguments.length > 1){
36036 for(var i = 0, len = arguments.length; i < len; i++) {
36037 this.add(arguments[i]);
36042 // if we have not been rendered yet, then we can not really do much of this..
36043 if (!this.bodyEl) {
36044 this.unrendered_panels.push(panel);
36051 if(this.hasPanel(panel)){
36052 this.showPanel(panel);
36055 panel.setRegion(this);
36056 this.panels.add(panel);
36057 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36058 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36059 // and hide them... ???
36060 this.bodyEl.dom.appendChild(panel.getEl().dom);
36061 if(panel.background !== true){
36062 this.setActivePanel(panel);
36064 this.fireEvent("paneladded", this, panel);
36071 this.initPanelAsTab(panel);
36075 if(panel.background !== true){
36076 this.tabs.activate(panel.getEl().id);
36078 this.fireEvent("paneladded", this, panel);
36083 * Hides the tab for the specified panel.
36084 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36086 hidePanel : function(panel){
36087 if(this.tabs && (panel = this.getPanel(panel))){
36088 this.tabs.hideTab(panel.getEl().id);
36093 * Unhides the tab for a previously hidden panel.
36094 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36096 unhidePanel : function(panel){
36097 if(this.tabs && (panel = this.getPanel(panel))){
36098 this.tabs.unhideTab(panel.getEl().id);
36102 clearPanels : function(){
36103 while(this.panels.getCount() > 0){
36104 this.remove(this.panels.first());
36109 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36110 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36111 * @param {Boolean} preservePanel Overrides the config preservePanel option
36112 * @return {Roo.ContentPanel} The panel that was removed
36114 remove : function(panel, preservePanel)
36116 panel = this.getPanel(panel);
36121 this.fireEvent("beforeremove", this, panel, e);
36122 if(e.cancel === true){
36125 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36126 var panelId = panel.getId();
36127 this.panels.removeKey(panelId);
36129 document.body.appendChild(panel.getEl().dom);
36132 this.tabs.removeTab(panel.getEl().id);
36133 }else if (!preservePanel){
36134 this.bodyEl.dom.removeChild(panel.getEl().dom);
36136 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36137 var p = this.panels.first();
36138 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36139 tempEl.appendChild(p.getEl().dom);
36140 this.bodyEl.update("");
36141 this.bodyEl.dom.appendChild(p.getEl().dom);
36143 this.updateTitle(p.getTitle());
36145 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36146 this.setActivePanel(p);
36148 panel.setRegion(null);
36149 if(this.activePanel == panel){
36150 this.activePanel = null;
36152 if(this.config.autoDestroy !== false && preservePanel !== true){
36153 try{panel.destroy();}catch(e){}
36155 this.fireEvent("panelremoved", this, panel);
36160 * Returns the TabPanel component used by this region
36161 * @return {Roo.TabPanel}
36163 getTabs : function(){
36167 createTool : function(parentEl, className){
36168 var btn = Roo.DomHelper.append(parentEl, {
36170 cls: "x-layout-tools-button",
36173 cls: "roo-layout-tools-button-inner " + className,
36177 btn.addClassOnOver("roo-layout-tools-button-over");
36182 * Ext JS Library 1.1.1
36183 * Copyright(c) 2006-2007, Ext JS, LLC.
36185 * Originally Released Under LGPL - original licence link has changed is not relivant.
36188 * <script type="text/javascript">
36194 * @class Roo.SplitLayoutRegion
36195 * @extends Roo.LayoutRegion
36196 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36198 Roo.bootstrap.layout.Split = function(config){
36199 this.cursor = config.cursor;
36200 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36203 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36205 splitTip : "Drag to resize.",
36206 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36207 useSplitTips : false,
36209 applyConfig : function(config){
36210 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36213 onRender : function(ctr,pos) {
36215 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36216 if(!this.config.split){
36221 var splitEl = Roo.DomHelper.append(ctr.dom, {
36223 id: this.el.id + "-split",
36224 cls: "roo-layout-split roo-layout-split-"+this.position,
36227 /** The SplitBar for this region
36228 * @type Roo.SplitBar */
36229 // does not exist yet...
36230 Roo.log([this.position, this.orientation]);
36232 this.split = new Roo.bootstrap.SplitBar({
36233 dragElement : splitEl,
36234 resizingElement: this.el,
36235 orientation : this.orientation
36238 this.split.on("moved", this.onSplitMove, this);
36239 this.split.useShim = this.config.useShim === true;
36240 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36241 if(this.useSplitTips){
36242 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36244 //if(config.collapsible){
36245 // this.split.el.on("dblclick", this.collapse, this);
36248 if(typeof this.config.minSize != "undefined"){
36249 this.split.minSize = this.config.minSize;
36251 if(typeof this.config.maxSize != "undefined"){
36252 this.split.maxSize = this.config.maxSize;
36254 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36255 this.hideSplitter();
36260 getHMaxSize : function(){
36261 var cmax = this.config.maxSize || 10000;
36262 var center = this.mgr.getRegion("center");
36263 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36266 getVMaxSize : function(){
36267 var cmax = this.config.maxSize || 10000;
36268 var center = this.mgr.getRegion("center");
36269 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36272 onSplitMove : function(split, newSize){
36273 this.fireEvent("resized", this, newSize);
36277 * Returns the {@link Roo.SplitBar} for this region.
36278 * @return {Roo.SplitBar}
36280 getSplitBar : function(){
36285 this.hideSplitter();
36286 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36289 hideSplitter : function(){
36291 this.split.el.setLocation(-2000,-2000);
36292 this.split.el.hide();
36298 this.split.el.show();
36300 Roo.bootstrap.layout.Split.superclass.show.call(this);
36303 beforeSlide: function(){
36304 if(Roo.isGecko){// firefox overflow auto bug workaround
36305 this.bodyEl.clip();
36307 this.tabs.bodyEl.clip();
36309 if(this.activePanel){
36310 this.activePanel.getEl().clip();
36312 if(this.activePanel.beforeSlide){
36313 this.activePanel.beforeSlide();
36319 afterSlide : function(){
36320 if(Roo.isGecko){// firefox overflow auto bug workaround
36321 this.bodyEl.unclip();
36323 this.tabs.bodyEl.unclip();
36325 if(this.activePanel){
36326 this.activePanel.getEl().unclip();
36327 if(this.activePanel.afterSlide){
36328 this.activePanel.afterSlide();
36334 initAutoHide : function(){
36335 if(this.autoHide !== false){
36336 if(!this.autoHideHd){
36337 var st = new Roo.util.DelayedTask(this.slideIn, this);
36338 this.autoHideHd = {
36339 "mouseout": function(e){
36340 if(!e.within(this.el, true)){
36344 "mouseover" : function(e){
36350 this.el.on(this.autoHideHd);
36354 clearAutoHide : function(){
36355 if(this.autoHide !== false){
36356 this.el.un("mouseout", this.autoHideHd.mouseout);
36357 this.el.un("mouseover", this.autoHideHd.mouseover);
36361 clearMonitor : function(){
36362 Roo.get(document).un("click", this.slideInIf, this);
36365 // these names are backwards but not changed for compat
36366 slideOut : function(){
36367 if(this.isSlid || this.el.hasActiveFx()){
36370 this.isSlid = true;
36371 if(this.collapseBtn){
36372 this.collapseBtn.hide();
36374 this.closeBtnState = this.closeBtn.getStyle('display');
36375 this.closeBtn.hide();
36377 this.stickBtn.show();
36380 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36381 this.beforeSlide();
36382 this.el.setStyle("z-index", 10001);
36383 this.el.slideIn(this.getSlideAnchor(), {
36384 callback: function(){
36386 this.initAutoHide();
36387 Roo.get(document).on("click", this.slideInIf, this);
36388 this.fireEvent("slideshow", this);
36395 afterSlideIn : function(){
36396 this.clearAutoHide();
36397 this.isSlid = false;
36398 this.clearMonitor();
36399 this.el.setStyle("z-index", "");
36400 if(this.collapseBtn){
36401 this.collapseBtn.show();
36403 this.closeBtn.setStyle('display', this.closeBtnState);
36405 this.stickBtn.hide();
36407 this.fireEvent("slidehide", this);
36410 slideIn : function(cb){
36411 if(!this.isSlid || this.el.hasActiveFx()){
36415 this.isSlid = false;
36416 this.beforeSlide();
36417 this.el.slideOut(this.getSlideAnchor(), {
36418 callback: function(){
36419 this.el.setLeftTop(-10000, -10000);
36421 this.afterSlideIn();
36429 slideInIf : function(e){
36430 if(!e.within(this.el)){
36435 animateCollapse : function(){
36436 this.beforeSlide();
36437 this.el.setStyle("z-index", 20000);
36438 var anchor = this.getSlideAnchor();
36439 this.el.slideOut(anchor, {
36440 callback : function(){
36441 this.el.setStyle("z-index", "");
36442 this.collapsedEl.slideIn(anchor, {duration:.3});
36444 this.el.setLocation(-10000,-10000);
36446 this.fireEvent("collapsed", this);
36453 animateExpand : function(){
36454 this.beforeSlide();
36455 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36456 this.el.setStyle("z-index", 20000);
36457 this.collapsedEl.hide({
36460 this.el.slideIn(this.getSlideAnchor(), {
36461 callback : function(){
36462 this.el.setStyle("z-index", "");
36465 this.split.el.show();
36467 this.fireEvent("invalidated", this);
36468 this.fireEvent("expanded", this);
36496 getAnchor : function(){
36497 return this.anchors[this.position];
36500 getCollapseAnchor : function(){
36501 return this.canchors[this.position];
36504 getSlideAnchor : function(){
36505 return this.sanchors[this.position];
36508 getAlignAdj : function(){
36509 var cm = this.cmargins;
36510 switch(this.position){
36526 getExpandAdj : function(){
36527 var c = this.collapsedEl, cm = this.cmargins;
36528 switch(this.position){
36530 return [-(cm.right+c.getWidth()+cm.left), 0];
36533 return [cm.right+c.getWidth()+cm.left, 0];
36536 return [0, -(cm.top+cm.bottom+c.getHeight())];
36539 return [0, cm.top+cm.bottom+c.getHeight()];
36545 * Ext JS Library 1.1.1
36546 * Copyright(c) 2006-2007, Ext JS, LLC.
36548 * Originally Released Under LGPL - original licence link has changed is not relivant.
36551 * <script type="text/javascript">
36554 * These classes are private internal classes
36556 Roo.bootstrap.layout.Center = function(config){
36557 config.region = "center";
36558 Roo.bootstrap.layout.Region.call(this, config);
36559 this.visible = true;
36560 this.minWidth = config.minWidth || 20;
36561 this.minHeight = config.minHeight || 20;
36564 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36566 // center panel can't be hidden
36570 // center panel can't be hidden
36573 getMinWidth: function(){
36574 return this.minWidth;
36577 getMinHeight: function(){
36578 return this.minHeight;
36591 Roo.bootstrap.layout.North = function(config)
36593 config.region = 'north';
36594 config.cursor = 'n-resize';
36596 Roo.bootstrap.layout.Split.call(this, config);
36600 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36601 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36602 this.split.el.addClass("roo-layout-split-v");
36604 var size = config.initialSize || config.height;
36605 if(typeof size != "undefined"){
36606 this.el.setHeight(size);
36609 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36611 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36615 getBox : function(){
36616 if(this.collapsed){
36617 return this.collapsedEl.getBox();
36619 var box = this.el.getBox();
36621 box.height += this.split.el.getHeight();
36626 updateBox : function(box){
36627 if(this.split && !this.collapsed){
36628 box.height -= this.split.el.getHeight();
36629 this.split.el.setLeft(box.x);
36630 this.split.el.setTop(box.y+box.height);
36631 this.split.el.setWidth(box.width);
36633 if(this.collapsed){
36634 this.updateBody(box.width, null);
36636 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36644 Roo.bootstrap.layout.South = function(config){
36645 config.region = 'south';
36646 config.cursor = 's-resize';
36647 Roo.bootstrap.layout.Split.call(this, config);
36649 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36650 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36651 this.split.el.addClass("roo-layout-split-v");
36653 var size = config.initialSize || config.height;
36654 if(typeof size != "undefined"){
36655 this.el.setHeight(size);
36659 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36660 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36661 getBox : function(){
36662 if(this.collapsed){
36663 return this.collapsedEl.getBox();
36665 var box = this.el.getBox();
36667 var sh = this.split.el.getHeight();
36674 updateBox : function(box){
36675 if(this.split && !this.collapsed){
36676 var sh = this.split.el.getHeight();
36679 this.split.el.setLeft(box.x);
36680 this.split.el.setTop(box.y-sh);
36681 this.split.el.setWidth(box.width);
36683 if(this.collapsed){
36684 this.updateBody(box.width, null);
36686 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36690 Roo.bootstrap.layout.East = function(config){
36691 config.region = "east";
36692 config.cursor = "e-resize";
36693 Roo.bootstrap.layout.Split.call(this, config);
36695 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36696 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36697 this.split.el.addClass("roo-layout-split-h");
36699 var size = config.initialSize || config.width;
36700 if(typeof size != "undefined"){
36701 this.el.setWidth(size);
36704 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36705 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36706 getBox : function(){
36707 if(this.collapsed){
36708 return this.collapsedEl.getBox();
36710 var box = this.el.getBox();
36712 var sw = this.split.el.getWidth();
36719 updateBox : function(box){
36720 if(this.split && !this.collapsed){
36721 var sw = this.split.el.getWidth();
36723 this.split.el.setLeft(box.x);
36724 this.split.el.setTop(box.y);
36725 this.split.el.setHeight(box.height);
36728 if(this.collapsed){
36729 this.updateBody(null, box.height);
36731 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36735 Roo.bootstrap.layout.West = function(config){
36736 config.region = "west";
36737 config.cursor = "w-resize";
36739 Roo.bootstrap.layout.Split.call(this, config);
36741 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36742 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36743 this.split.el.addClass("roo-layout-split-h");
36747 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36748 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36750 onRender: function(ctr, pos)
36752 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36753 var size = this.config.initialSize || this.config.width;
36754 if(typeof size != "undefined"){
36755 this.el.setWidth(size);
36759 getBox : function(){
36760 if(this.collapsed){
36761 return this.collapsedEl.getBox();
36763 var box = this.el.getBox();
36765 box.width += this.split.el.getWidth();
36770 updateBox : function(box){
36771 if(this.split && !this.collapsed){
36772 var sw = this.split.el.getWidth();
36774 this.split.el.setLeft(box.x+box.width);
36775 this.split.el.setTop(box.y);
36776 this.split.el.setHeight(box.height);
36778 if(this.collapsed){
36779 this.updateBody(null, box.height);
36781 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36784 Roo.namespace("Roo.bootstrap.panel");/*
36786 * Ext JS Library 1.1.1
36787 * Copyright(c) 2006-2007, Ext JS, LLC.
36789 * Originally Released Under LGPL - original licence link has changed is not relivant.
36792 * <script type="text/javascript">
36795 * @class Roo.ContentPanel
36796 * @extends Roo.util.Observable
36797 * A basic ContentPanel element.
36798 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36799 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36800 * @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
36801 * @cfg {Boolean} closable True if the panel can be closed/removed
36802 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36803 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36804 * @cfg {Toolbar} toolbar A toolbar for this panel
36805 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36806 * @cfg {String} title The title for this panel
36807 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36808 * @cfg {String} url Calls {@link #setUrl} with this value
36809 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36810 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36811 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36812 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36813 * @cfg {Boolean} badges render the badges
36816 * Create a new ContentPanel.
36817 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36818 * @param {String/Object} config A string to set only the title or a config object
36819 * @param {String} content (optional) Set the HTML content for this panel
36820 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36822 Roo.bootstrap.panel.Content = function( config){
36824 this.tpl = config.tpl || false;
36826 var el = config.el;
36827 var content = config.content;
36829 if(config.autoCreate){ // xtype is available if this is called from factory
36832 this.el = Roo.get(el);
36833 if(!this.el && config && config.autoCreate){
36834 if(typeof config.autoCreate == "object"){
36835 if(!config.autoCreate.id){
36836 config.autoCreate.id = config.id||el;
36838 this.el = Roo.DomHelper.append(document.body,
36839 config.autoCreate, true);
36841 var elcfg = { tag: "div",
36842 cls: "roo-layout-inactive-content",
36846 elcfg.html = config.html;
36850 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36853 this.closable = false;
36854 this.loaded = false;
36855 this.active = false;
36858 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36860 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36862 this.wrapEl = this.el; //this.el.wrap();
36864 if (config.toolbar.items) {
36865 ti = config.toolbar.items ;
36866 delete config.toolbar.items ;
36870 this.toolbar.render(this.wrapEl, 'before');
36871 for(var i =0;i < ti.length;i++) {
36872 // Roo.log(['add child', items[i]]);
36873 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36875 this.toolbar.items = nitems;
36876 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36877 delete config.toolbar;
36881 // xtype created footer. - not sure if will work as we normally have to render first..
36882 if (this.footer && !this.footer.el && this.footer.xtype) {
36883 if (!this.wrapEl) {
36884 this.wrapEl = this.el.wrap();
36887 this.footer.container = this.wrapEl.createChild();
36889 this.footer = Roo.factory(this.footer, Roo);
36894 if(typeof config == "string"){
36895 this.title = config;
36897 Roo.apply(this, config);
36901 this.resizeEl = Roo.get(this.resizeEl, true);
36903 this.resizeEl = this.el;
36905 // handle view.xtype
36913 * Fires when this panel is activated.
36914 * @param {Roo.ContentPanel} this
36918 * @event deactivate
36919 * Fires when this panel is activated.
36920 * @param {Roo.ContentPanel} this
36922 "deactivate" : true,
36926 * Fires when this panel is resized if fitToFrame is true.
36927 * @param {Roo.ContentPanel} this
36928 * @param {Number} width The width after any component adjustments
36929 * @param {Number} height The height after any component adjustments
36935 * Fires when this tab is created
36936 * @param {Roo.ContentPanel} this
36947 if(this.autoScroll){
36948 this.resizeEl.setStyle("overflow", "auto");
36950 // fix randome scrolling
36951 //this.el.on('scroll', function() {
36952 // Roo.log('fix random scolling');
36953 // this.scrollTo('top',0);
36956 content = content || this.content;
36958 this.setContent(content);
36960 if(config && config.url){
36961 this.setUrl(this.url, this.params, this.loadOnce);
36966 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36968 if (this.view && typeof(this.view.xtype) != 'undefined') {
36969 this.view.el = this.el.appendChild(document.createElement("div"));
36970 this.view = Roo.factory(this.view);
36971 this.view.render && this.view.render(false, '');
36975 this.fireEvent('render', this);
36978 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36982 setRegion : function(region){
36983 this.region = region;
36984 this.setActiveClass(region && !this.background);
36988 setActiveClass: function(state)
36991 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36992 this.el.setStyle('position','relative');
36994 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36995 this.el.setStyle('position', 'absolute');
37000 * Returns the toolbar for this Panel if one was configured.
37001 * @return {Roo.Toolbar}
37003 getToolbar : function(){
37004 return this.toolbar;
37007 setActiveState : function(active)
37009 this.active = active;
37010 this.setActiveClass(active);
37012 if(this.fireEvent("deactivate", this) === false){
37017 this.fireEvent("activate", this);
37021 * Updates this panel's element
37022 * @param {String} content The new content
37023 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37025 setContent : function(content, loadScripts){
37026 this.el.update(content, loadScripts);
37029 ignoreResize : function(w, h){
37030 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37033 this.lastSize = {width: w, height: h};
37038 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37039 * @return {Roo.UpdateManager} The UpdateManager
37041 getUpdateManager : function(){
37042 return this.el.getUpdateManager();
37045 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37046 * @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:
37049 url: "your-url.php",
37050 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37051 callback: yourFunction,
37052 scope: yourObject, //(optional scope)
37055 text: "Loading...",
37060 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37061 * 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.
37062 * @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}
37063 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37064 * @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.
37065 * @return {Roo.ContentPanel} this
37068 var um = this.el.getUpdateManager();
37069 um.update.apply(um, arguments);
37075 * 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.
37076 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37077 * @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)
37078 * @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)
37079 * @return {Roo.UpdateManager} The UpdateManager
37081 setUrl : function(url, params, loadOnce){
37082 if(this.refreshDelegate){
37083 this.removeListener("activate", this.refreshDelegate);
37085 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37086 this.on("activate", this.refreshDelegate);
37087 return this.el.getUpdateManager();
37090 _handleRefresh : function(url, params, loadOnce){
37091 if(!loadOnce || !this.loaded){
37092 var updater = this.el.getUpdateManager();
37093 updater.update(url, params, this._setLoaded.createDelegate(this));
37097 _setLoaded : function(){
37098 this.loaded = true;
37102 * Returns this panel's id
37105 getId : function(){
37110 * Returns this panel's element - used by regiosn to add.
37111 * @return {Roo.Element}
37113 getEl : function(){
37114 return this.wrapEl || this.el;
37119 adjustForComponents : function(width, height)
37121 //Roo.log('adjustForComponents ');
37122 if(this.resizeEl != this.el){
37123 width -= this.el.getFrameWidth('lr');
37124 height -= this.el.getFrameWidth('tb');
37127 var te = this.toolbar.getEl();
37128 te.setWidth(width);
37129 height -= te.getHeight();
37132 var te = this.footer.getEl();
37133 te.setWidth(width);
37134 height -= te.getHeight();
37138 if(this.adjustments){
37139 width += this.adjustments[0];
37140 height += this.adjustments[1];
37142 return {"width": width, "height": height};
37145 setSize : function(width, height){
37146 if(this.fitToFrame && !this.ignoreResize(width, height)){
37147 if(this.fitContainer && this.resizeEl != this.el){
37148 this.el.setSize(width, height);
37150 var size = this.adjustForComponents(width, height);
37151 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37152 this.fireEvent('resize', this, size.width, size.height);
37157 * Returns this panel's title
37160 getTitle : function(){
37162 if (typeof(this.title) != 'object') {
37167 for (var k in this.title) {
37168 if (!this.title.hasOwnProperty(k)) {
37172 if (k.indexOf('-') >= 0) {
37173 var s = k.split('-');
37174 for (var i = 0; i<s.length; i++) {
37175 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37178 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37185 * Set this panel's title
37186 * @param {String} title
37188 setTitle : function(title){
37189 this.title = title;
37191 this.region.updatePanelTitle(this, title);
37196 * Returns true is this panel was configured to be closable
37197 * @return {Boolean}
37199 isClosable : function(){
37200 return this.closable;
37203 beforeSlide : function(){
37205 this.resizeEl.clip();
37208 afterSlide : function(){
37210 this.resizeEl.unclip();
37214 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37215 * Will fail silently if the {@link #setUrl} method has not been called.
37216 * This does not activate the panel, just updates its content.
37218 refresh : function(){
37219 if(this.refreshDelegate){
37220 this.loaded = false;
37221 this.refreshDelegate();
37226 * Destroys this panel
37228 destroy : function(){
37229 this.el.removeAllListeners();
37230 var tempEl = document.createElement("span");
37231 tempEl.appendChild(this.el.dom);
37232 tempEl.innerHTML = "";
37238 * form - if the content panel contains a form - this is a reference to it.
37239 * @type {Roo.form.Form}
37243 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37244 * This contains a reference to it.
37250 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37260 * @param {Object} cfg Xtype definition of item to add.
37264 getChildContainer: function () {
37265 return this.getEl();
37270 var ret = new Roo.factory(cfg);
37275 if (cfg.xtype.match(/^Form$/)) {
37278 //if (this.footer) {
37279 // el = this.footer.container.insertSibling(false, 'before');
37281 el = this.el.createChild();
37284 this.form = new Roo.form.Form(cfg);
37287 if ( this.form.allItems.length) {
37288 this.form.render(el.dom);
37292 // should only have one of theses..
37293 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37294 // views.. should not be just added - used named prop 'view''
37296 cfg.el = this.el.appendChild(document.createElement("div"));
37299 var ret = new Roo.factory(cfg);
37301 ret.render && ret.render(false, ''); // render blank..
37311 * @class Roo.bootstrap.panel.Grid
37312 * @extends Roo.bootstrap.panel.Content
37314 * Create a new GridPanel.
37315 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37316 * @param {Object} config A the config object
37322 Roo.bootstrap.panel.Grid = function(config)
37326 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37327 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37329 config.el = this.wrapper;
37330 //this.el = this.wrapper;
37332 if (config.container) {
37333 // ctor'ed from a Border/panel.grid
37336 this.wrapper.setStyle("overflow", "hidden");
37337 this.wrapper.addClass('roo-grid-container');
37342 if(config.toolbar){
37343 var tool_el = this.wrapper.createChild();
37344 this.toolbar = Roo.factory(config.toolbar);
37346 if (config.toolbar.items) {
37347 ti = config.toolbar.items ;
37348 delete config.toolbar.items ;
37352 this.toolbar.render(tool_el);
37353 for(var i =0;i < ti.length;i++) {
37354 // Roo.log(['add child', items[i]]);
37355 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37357 this.toolbar.items = nitems;
37359 delete config.toolbar;
37362 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37363 config.grid.scrollBody = true;;
37364 config.grid.monitorWindowResize = false; // turn off autosizing
37365 config.grid.autoHeight = false;
37366 config.grid.autoWidth = false;
37368 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37370 if (config.background) {
37371 // render grid on panel activation (if panel background)
37372 this.on('activate', function(gp) {
37373 if (!gp.grid.rendered) {
37374 gp.grid.render(this.wrapper);
37375 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37380 this.grid.render(this.wrapper);
37381 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37384 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37385 // ??? needed ??? config.el = this.wrapper;
37390 // xtype created footer. - not sure if will work as we normally have to render first..
37391 if (this.footer && !this.footer.el && this.footer.xtype) {
37393 var ctr = this.grid.getView().getFooterPanel(true);
37394 this.footer.dataSource = this.grid.dataSource;
37395 this.footer = Roo.factory(this.footer, Roo);
37396 this.footer.render(ctr);
37406 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37407 getId : function(){
37408 return this.grid.id;
37412 * Returns the grid for this panel
37413 * @return {Roo.bootstrap.Table}
37415 getGrid : function(){
37419 setSize : function(width, height){
37420 if(!this.ignoreResize(width, height)){
37421 var grid = this.grid;
37422 var size = this.adjustForComponents(width, height);
37423 var gridel = grid.getGridEl();
37424 gridel.setSize(size.width, size.height);
37426 var thd = grid.getGridEl().select('thead',true).first();
37427 var tbd = grid.getGridEl().select('tbody', true).first();
37429 tbd.setSize(width, height - thd.getHeight());
37438 beforeSlide : function(){
37439 this.grid.getView().scroller.clip();
37442 afterSlide : function(){
37443 this.grid.getView().scroller.unclip();
37446 destroy : function(){
37447 this.grid.destroy();
37449 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37454 * @class Roo.bootstrap.panel.Nest
37455 * @extends Roo.bootstrap.panel.Content
37457 * Create a new Panel, that can contain a layout.Border.
37460 * @param {Roo.BorderLayout} layout The layout for this panel
37461 * @param {String/Object} config A string to set only the title or a config object
37463 Roo.bootstrap.panel.Nest = function(config)
37465 // construct with only one argument..
37466 /* FIXME - implement nicer consturctors
37467 if (layout.layout) {
37469 layout = config.layout;
37470 delete config.layout;
37472 if (layout.xtype && !layout.getEl) {
37473 // then layout needs constructing..
37474 layout = Roo.factory(layout, Roo);
37478 config.el = config.layout.getEl();
37480 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37482 config.layout.monitorWindowResize = false; // turn off autosizing
37483 this.layout = config.layout;
37484 this.layout.getEl().addClass("roo-layout-nested-layout");
37491 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37493 setSize : function(width, height){
37494 if(!this.ignoreResize(width, height)){
37495 var size = this.adjustForComponents(width, height);
37496 var el = this.layout.getEl();
37497 if (size.height < 1) {
37498 el.setWidth(size.width);
37500 el.setSize(size.width, size.height);
37502 var touch = el.dom.offsetWidth;
37503 this.layout.layout();
37504 // ie requires a double layout on the first pass
37505 if(Roo.isIE && !this.initialized){
37506 this.initialized = true;
37507 this.layout.layout();
37512 // activate all subpanels if not currently active..
37514 setActiveState : function(active){
37515 this.active = active;
37516 this.setActiveClass(active);
37519 this.fireEvent("deactivate", this);
37523 this.fireEvent("activate", this);
37524 // not sure if this should happen before or after..
37525 if (!this.layout) {
37526 return; // should not happen..
37529 for (var r in this.layout.regions) {
37530 reg = this.layout.getRegion(r);
37531 if (reg.getActivePanel()) {
37532 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37533 reg.setActivePanel(reg.getActivePanel());
37536 if (!reg.panels.length) {
37539 reg.showPanel(reg.getPanel(0));
37548 * Returns the nested BorderLayout for this panel
37549 * @return {Roo.BorderLayout}
37551 getLayout : function(){
37552 return this.layout;
37556 * Adds a xtype elements to the layout of the nested panel
37560 xtype : 'ContentPanel',
37567 xtype : 'NestedLayoutPanel',
37573 items : [ ... list of content panels or nested layout panels.. ]
37577 * @param {Object} cfg Xtype definition of item to add.
37579 addxtype : function(cfg) {
37580 return this.layout.addxtype(cfg);
37585 * Ext JS Library 1.1.1
37586 * Copyright(c) 2006-2007, Ext JS, LLC.
37588 * Originally Released Under LGPL - original licence link has changed is not relivant.
37591 * <script type="text/javascript">
37594 * @class Roo.TabPanel
37595 * @extends Roo.util.Observable
37596 * A lightweight tab container.
37600 // basic tabs 1, built from existing content
37601 var tabs = new Roo.TabPanel("tabs1");
37602 tabs.addTab("script", "View Script");
37603 tabs.addTab("markup", "View Markup");
37604 tabs.activate("script");
37606 // more advanced tabs, built from javascript
37607 var jtabs = new Roo.TabPanel("jtabs");
37608 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37610 // set up the UpdateManager
37611 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37612 var updater = tab2.getUpdateManager();
37613 updater.setDefaultUrl("ajax1.htm");
37614 tab2.on('activate', updater.refresh, updater, true);
37616 // Use setUrl for Ajax loading
37617 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37618 tab3.setUrl("ajax2.htm", null, true);
37621 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37624 jtabs.activate("jtabs-1");
37627 * Create a new TabPanel.
37628 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37629 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37631 Roo.bootstrap.panel.Tabs = function(config){
37633 * The container element for this TabPanel.
37634 * @type Roo.Element
37636 this.el = Roo.get(config.el);
37639 if(typeof config == "boolean"){
37640 this.tabPosition = config ? "bottom" : "top";
37642 Roo.apply(this, config);
37646 if(this.tabPosition == "bottom"){
37647 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37648 this.el.addClass("roo-tabs-bottom");
37650 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37651 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37652 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37654 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37656 if(this.tabPosition != "bottom"){
37657 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37658 * @type Roo.Element
37660 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37661 this.el.addClass("roo-tabs-top");
37665 this.bodyEl.setStyle("position", "relative");
37667 this.active = null;
37668 this.activateDelegate = this.activate.createDelegate(this);
37673 * Fires when the active tab changes
37674 * @param {Roo.TabPanel} this
37675 * @param {Roo.TabPanelItem} activePanel The new active tab
37679 * @event beforetabchange
37680 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37681 * @param {Roo.TabPanel} this
37682 * @param {Object} e Set cancel to true on this object to cancel the tab change
37683 * @param {Roo.TabPanelItem} tab The tab being changed to
37685 "beforetabchange" : true
37688 Roo.EventManager.onWindowResize(this.onResize, this);
37689 this.cpad = this.el.getPadding("lr");
37690 this.hiddenCount = 0;
37693 // toolbar on the tabbar support...
37694 if (this.toolbar) {
37695 alert("no toolbar support yet");
37696 this.toolbar = false;
37698 var tcfg = this.toolbar;
37699 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37700 this.toolbar = new Roo.Toolbar(tcfg);
37701 if (Roo.isSafari) {
37702 var tbl = tcfg.container.child('table', true);
37703 tbl.setAttribute('width', '100%');
37711 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37714 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37716 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37718 tabPosition : "top",
37720 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37722 currentTabWidth : 0,
37724 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37728 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37732 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37734 preferredTabWidth : 175,
37736 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37738 resizeTabs : false,
37740 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37742 monitorResize : true,
37744 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37749 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37750 * @param {String} id The id of the div to use <b>or create</b>
37751 * @param {String} text The text for the tab
37752 * @param {String} content (optional) Content to put in the TabPanelItem body
37753 * @param {Boolean} closable (optional) True to create a close icon on the tab
37754 * @return {Roo.TabPanelItem} The created TabPanelItem
37756 addTab : function(id, text, content, closable, tpl)
37758 var item = new Roo.bootstrap.panel.TabItem({
37762 closable : closable,
37765 this.addTabItem(item);
37767 item.setContent(content);
37773 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37774 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37775 * @return {Roo.TabPanelItem}
37777 getTab : function(id){
37778 return this.items[id];
37782 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37783 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37785 hideTab : function(id){
37786 var t = this.items[id];
37789 this.hiddenCount++;
37790 this.autoSizeTabs();
37795 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37796 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37798 unhideTab : function(id){
37799 var t = this.items[id];
37801 t.setHidden(false);
37802 this.hiddenCount--;
37803 this.autoSizeTabs();
37808 * Adds an existing {@link Roo.TabPanelItem}.
37809 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37811 addTabItem : function(item){
37812 this.items[item.id] = item;
37813 this.items.push(item);
37814 // if(this.resizeTabs){
37815 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37816 // this.autoSizeTabs();
37818 // item.autoSize();
37823 * Removes a {@link Roo.TabPanelItem}.
37824 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37826 removeTab : function(id){
37827 var items = this.items;
37828 var tab = items[id];
37829 if(!tab) { return; }
37830 var index = items.indexOf(tab);
37831 if(this.active == tab && items.length > 1){
37832 var newTab = this.getNextAvailable(index);
37837 this.stripEl.dom.removeChild(tab.pnode.dom);
37838 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37839 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37841 items.splice(index, 1);
37842 delete this.items[tab.id];
37843 tab.fireEvent("close", tab);
37844 tab.purgeListeners();
37845 this.autoSizeTabs();
37848 getNextAvailable : function(start){
37849 var items = this.items;
37851 // look for a next tab that will slide over to
37852 // replace the one being removed
37853 while(index < items.length){
37854 var item = items[++index];
37855 if(item && !item.isHidden()){
37859 // if one isn't found select the previous tab (on the left)
37862 var item = items[--index];
37863 if(item && !item.isHidden()){
37871 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37872 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37874 disableTab : function(id){
37875 var tab = this.items[id];
37876 if(tab && this.active != tab){
37882 * Enables a {@link Roo.TabPanelItem} that is disabled.
37883 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37885 enableTab : function(id){
37886 var tab = this.items[id];
37891 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37892 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37893 * @return {Roo.TabPanelItem} The TabPanelItem.
37895 activate : function(id){
37896 var tab = this.items[id];
37900 if(tab == this.active || tab.disabled){
37904 this.fireEvent("beforetabchange", this, e, tab);
37905 if(e.cancel !== true && !tab.disabled){
37907 this.active.hide();
37909 this.active = this.items[id];
37910 this.active.show();
37911 this.fireEvent("tabchange", this, this.active);
37917 * Gets the active {@link Roo.TabPanelItem}.
37918 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37920 getActiveTab : function(){
37921 return this.active;
37925 * Updates the tab body element to fit the height of the container element
37926 * for overflow scrolling
37927 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37929 syncHeight : function(targetHeight){
37930 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37931 var bm = this.bodyEl.getMargins();
37932 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37933 this.bodyEl.setHeight(newHeight);
37937 onResize : function(){
37938 if(this.monitorResize){
37939 this.autoSizeTabs();
37944 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37946 beginUpdate : function(){
37947 this.updating = true;
37951 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37953 endUpdate : function(){
37954 this.updating = false;
37955 this.autoSizeTabs();
37959 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37961 autoSizeTabs : function(){
37962 var count = this.items.length;
37963 var vcount = count - this.hiddenCount;
37964 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37967 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37968 var availWidth = Math.floor(w / vcount);
37969 var b = this.stripBody;
37970 if(b.getWidth() > w){
37971 var tabs = this.items;
37972 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37973 if(availWidth < this.minTabWidth){
37974 /*if(!this.sleft){ // incomplete scrolling code
37975 this.createScrollButtons();
37978 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37981 if(this.currentTabWidth < this.preferredTabWidth){
37982 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37988 * Returns the number of tabs in this TabPanel.
37991 getCount : function(){
37992 return this.items.length;
37996 * Resizes all the tabs to the passed width
37997 * @param {Number} The new width
37999 setTabWidth : function(width){
38000 this.currentTabWidth = width;
38001 for(var i = 0, len = this.items.length; i < len; i++) {
38002 if(!this.items[i].isHidden()) {
38003 this.items[i].setWidth(width);
38009 * Destroys this TabPanel
38010 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38012 destroy : function(removeEl){
38013 Roo.EventManager.removeResizeListener(this.onResize, this);
38014 for(var i = 0, len = this.items.length; i < len; i++){
38015 this.items[i].purgeListeners();
38017 if(removeEl === true){
38018 this.el.update("");
38023 createStrip : function(container)
38025 var strip = document.createElement("nav");
38026 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38027 container.appendChild(strip);
38031 createStripList : function(strip)
38033 // div wrapper for retard IE
38034 // returns the "tr" element.
38035 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38036 //'<div class="x-tabs-strip-wrap">'+
38037 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38038 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38039 return strip.firstChild; //.firstChild.firstChild.firstChild;
38041 createBody : function(container)
38043 var body = document.createElement("div");
38044 Roo.id(body, "tab-body");
38045 //Roo.fly(body).addClass("x-tabs-body");
38046 Roo.fly(body).addClass("tab-content");
38047 container.appendChild(body);
38050 createItemBody :function(bodyEl, id){
38051 var body = Roo.getDom(id);
38053 body = document.createElement("div");
38056 //Roo.fly(body).addClass("x-tabs-item-body");
38057 Roo.fly(body).addClass("tab-pane");
38058 bodyEl.insertBefore(body, bodyEl.firstChild);
38062 createStripElements : function(stripEl, text, closable, tpl)
38064 var td = document.createElement("li"); // was td..
38067 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38070 stripEl.appendChild(td);
38072 td.className = "x-tabs-closable";
38073 if(!this.closeTpl){
38074 this.closeTpl = new Roo.Template(
38075 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38076 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38077 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38080 var el = this.closeTpl.overwrite(td, {"text": text});
38081 var close = el.getElementsByTagName("div")[0];
38082 var inner = el.getElementsByTagName("em")[0];
38083 return {"el": el, "close": close, "inner": inner};
38086 // not sure what this is..
38087 // if(!this.tabTpl){
38088 //this.tabTpl = new Roo.Template(
38089 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38090 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38092 // this.tabTpl = new Roo.Template(
38093 // '<a href="#">' +
38094 // '<span unselectable="on"' +
38095 // (this.disableTooltips ? '' : ' title="{text}"') +
38096 // ' >{text}</span></a>'
38102 var template = tpl || this.tabTpl || false;
38106 template = new Roo.Template(
38108 '<span unselectable="on"' +
38109 (this.disableTooltips ? '' : ' title="{text}"') +
38110 ' >{text}</span></a>'
38114 switch (typeof(template)) {
38118 template = new Roo.Template(template);
38124 var el = template.overwrite(td, {"text": text});
38126 var inner = el.getElementsByTagName("span")[0];
38128 return {"el": el, "inner": inner};
38136 * @class Roo.TabPanelItem
38137 * @extends Roo.util.Observable
38138 * Represents an individual item (tab plus body) in a TabPanel.
38139 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38140 * @param {String} id The id of this TabPanelItem
38141 * @param {String} text The text for the tab of this TabPanelItem
38142 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38144 Roo.bootstrap.panel.TabItem = function(config){
38146 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38147 * @type Roo.TabPanel
38149 this.tabPanel = config.panel;
38151 * The id for this TabPanelItem
38154 this.id = config.id;
38156 this.disabled = false;
38158 this.text = config.text;
38160 this.loaded = false;
38161 this.closable = config.closable;
38164 * The body element for this TabPanelItem.
38165 * @type Roo.Element
38167 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38168 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38169 this.bodyEl.setStyle("display", "block");
38170 this.bodyEl.setStyle("zoom", "1");
38171 //this.hideAction();
38173 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38175 this.el = Roo.get(els.el);
38176 this.inner = Roo.get(els.inner, true);
38177 this.textEl = Roo.get(this.el.dom.firstChild, true);
38178 this.pnode = Roo.get(els.el.parentNode, true);
38179 // this.el.on("mousedown", this.onTabMouseDown, this);
38180 this.el.on("click", this.onTabClick, this);
38182 if(config.closable){
38183 var c = Roo.get(els.close, true);
38184 c.dom.title = this.closeText;
38185 c.addClassOnOver("close-over");
38186 c.on("click", this.closeClick, this);
38192 * Fires when this tab becomes the active tab.
38193 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38194 * @param {Roo.TabPanelItem} this
38198 * @event beforeclose
38199 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38200 * @param {Roo.TabPanelItem} this
38201 * @param {Object} e Set cancel to true on this object to cancel the close.
38203 "beforeclose": true,
38206 * Fires when this tab is closed.
38207 * @param {Roo.TabPanelItem} this
38211 * @event deactivate
38212 * Fires when this tab is no longer the active tab.
38213 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38214 * @param {Roo.TabPanelItem} this
38216 "deactivate" : true
38218 this.hidden = false;
38220 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38223 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38225 purgeListeners : function(){
38226 Roo.util.Observable.prototype.purgeListeners.call(this);
38227 this.el.removeAllListeners();
38230 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38233 this.pnode.addClass("active");
38236 this.tabPanel.stripWrap.repaint();
38238 this.fireEvent("activate", this.tabPanel, this);
38242 * Returns true if this tab is the active tab.
38243 * @return {Boolean}
38245 isActive : function(){
38246 return this.tabPanel.getActiveTab() == this;
38250 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38253 this.pnode.removeClass("active");
38255 this.fireEvent("deactivate", this.tabPanel, this);
38258 hideAction : function(){
38259 this.bodyEl.hide();
38260 this.bodyEl.setStyle("position", "absolute");
38261 this.bodyEl.setLeft("-20000px");
38262 this.bodyEl.setTop("-20000px");
38265 showAction : function(){
38266 this.bodyEl.setStyle("position", "relative");
38267 this.bodyEl.setTop("");
38268 this.bodyEl.setLeft("");
38269 this.bodyEl.show();
38273 * Set the tooltip for the tab.
38274 * @param {String} tooltip The tab's tooltip
38276 setTooltip : function(text){
38277 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38278 this.textEl.dom.qtip = text;
38279 this.textEl.dom.removeAttribute('title');
38281 this.textEl.dom.title = text;
38285 onTabClick : function(e){
38286 e.preventDefault();
38287 this.tabPanel.activate(this.id);
38290 onTabMouseDown : function(e){
38291 e.preventDefault();
38292 this.tabPanel.activate(this.id);
38295 getWidth : function(){
38296 return this.inner.getWidth();
38299 setWidth : function(width){
38300 var iwidth = width - this.pnode.getPadding("lr");
38301 this.inner.setWidth(iwidth);
38302 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38303 this.pnode.setWidth(width);
38307 * Show or hide the tab
38308 * @param {Boolean} hidden True to hide or false to show.
38310 setHidden : function(hidden){
38311 this.hidden = hidden;
38312 this.pnode.setStyle("display", hidden ? "none" : "");
38316 * Returns true if this tab is "hidden"
38317 * @return {Boolean}
38319 isHidden : function(){
38320 return this.hidden;
38324 * Returns the text for this tab
38327 getText : function(){
38331 autoSize : function(){
38332 //this.el.beginMeasure();
38333 this.textEl.setWidth(1);
38335 * #2804 [new] Tabs in Roojs
38336 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38338 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38339 //this.el.endMeasure();
38343 * Sets the text for the tab (Note: this also sets the tooltip text)
38344 * @param {String} text The tab's text and tooltip
38346 setText : function(text){
38348 this.textEl.update(text);
38349 this.setTooltip(text);
38350 //if(!this.tabPanel.resizeTabs){
38351 // this.autoSize();
38355 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38357 activate : function(){
38358 this.tabPanel.activate(this.id);
38362 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38364 disable : function(){
38365 if(this.tabPanel.active != this){
38366 this.disabled = true;
38367 this.pnode.addClass("disabled");
38372 * Enables this TabPanelItem if it was previously disabled.
38374 enable : function(){
38375 this.disabled = false;
38376 this.pnode.removeClass("disabled");
38380 * Sets the content for this TabPanelItem.
38381 * @param {String} content The content
38382 * @param {Boolean} loadScripts true to look for and load scripts
38384 setContent : function(content, loadScripts){
38385 this.bodyEl.update(content, loadScripts);
38389 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38390 * @return {Roo.UpdateManager} The UpdateManager
38392 getUpdateManager : function(){
38393 return this.bodyEl.getUpdateManager();
38397 * Set a URL to be used to load the content for this TabPanelItem.
38398 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38399 * @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)
38400 * @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)
38401 * @return {Roo.UpdateManager} The UpdateManager
38403 setUrl : function(url, params, loadOnce){
38404 if(this.refreshDelegate){
38405 this.un('activate', this.refreshDelegate);
38407 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38408 this.on("activate", this.refreshDelegate);
38409 return this.bodyEl.getUpdateManager();
38413 _handleRefresh : function(url, params, loadOnce){
38414 if(!loadOnce || !this.loaded){
38415 var updater = this.bodyEl.getUpdateManager();
38416 updater.update(url, params, this._setLoaded.createDelegate(this));
38421 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38422 * Will fail silently if the setUrl method has not been called.
38423 * This does not activate the panel, just updates its content.
38425 refresh : function(){
38426 if(this.refreshDelegate){
38427 this.loaded = false;
38428 this.refreshDelegate();
38433 _setLoaded : function(){
38434 this.loaded = true;
38438 closeClick : function(e){
38441 this.fireEvent("beforeclose", this, o);
38442 if(o.cancel !== true){
38443 this.tabPanel.removeTab(this.id);
38447 * The text displayed in the tooltip for the close icon.
38450 closeText : "Close this tab"
38453 * This script refer to:
38454 * Title: International Telephone Input
38455 * Author: Jack O'Connor
38456 * Code version: v12.1.12
38457 * Availability: https://github.com/jackocnr/intl-tel-input.git
38460 Roo.bootstrap.PhoneInputData = function() {
38463 "Afghanistan (افغانستان)",
38468 "Albania (Shqipëri)",
38473 "Algeria (الجزائر)",
38498 "Antigua and Barbuda",
38508 "Armenia (Հայաստան)",
38524 "Austria (Österreich)",
38529 "Azerbaijan (Azərbaycan)",
38539 "Bahrain (البحرين)",
38544 "Bangladesh (বাংলাদেশ)",
38554 "Belarus (Беларусь)",
38559 "Belgium (België)",
38589 "Bosnia and Herzegovina (Босна и Херцеговина)",
38604 "British Indian Ocean Territory",
38609 "British Virgin Islands",
38619 "Bulgaria (България)",
38629 "Burundi (Uburundi)",
38634 "Cambodia (កម្ពុជា)",
38639 "Cameroon (Cameroun)",
38648 ["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"]
38651 "Cape Verde (Kabu Verdi)",
38656 "Caribbean Netherlands",
38667 "Central African Republic (République centrafricaine)",
38687 "Christmas Island",
38693 "Cocos (Keeling) Islands",
38704 "Comoros (جزر القمر)",
38709 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38714 "Congo (Republic) (Congo-Brazzaville)",
38734 "Croatia (Hrvatska)",
38755 "Czech Republic (Česká republika)",
38760 "Denmark (Danmark)",
38775 "Dominican Republic (República Dominicana)",
38779 ["809", "829", "849"]
38797 "Equatorial Guinea (Guinea Ecuatorial)",
38817 "Falkland Islands (Islas Malvinas)",
38822 "Faroe Islands (Føroyar)",
38843 "French Guiana (Guyane française)",
38848 "French Polynesia (Polynésie française)",
38863 "Georgia (საქართველო)",
38868 "Germany (Deutschland)",
38888 "Greenland (Kalaallit Nunaat)",
38925 "Guinea-Bissau (Guiné Bissau)",
38950 "Hungary (Magyarország)",
38955 "Iceland (Ísland)",
38975 "Iraq (العراق)",
38991 "Israel (ישראל)",
39018 "Jordan (الأردن)",
39023 "Kazakhstan (Казахстан)",
39044 "Kuwait (الكويت)",
39049 "Kyrgyzstan (Кыргызстан)",
39059 "Latvia (Latvija)",
39064 "Lebanon (لبنان)",
39079 "Libya (ليبيا)",
39089 "Lithuania (Lietuva)",
39104 "Macedonia (FYROM) (Македонија)",
39109 "Madagascar (Madagasikara)",
39139 "Marshall Islands",
39149 "Mauritania (موريتانيا)",
39154 "Mauritius (Moris)",
39175 "Moldova (Republica Moldova)",
39185 "Mongolia (Монгол)",
39190 "Montenegro (Crna Gora)",
39200 "Morocco (المغرب)",
39206 "Mozambique (Moçambique)",
39211 "Myanmar (Burma) (မြန်မာ)",
39216 "Namibia (Namibië)",
39231 "Netherlands (Nederland)",
39236 "New Caledonia (Nouvelle-Calédonie)",
39271 "North Korea (조선 민주주의 인민 공화국)",
39276 "Northern Mariana Islands",
39292 "Pakistan (پاکستان)",
39302 "Palestine (فلسطين)",
39312 "Papua New Guinea",
39354 "Réunion (La Réunion)",
39360 "Romania (România)",
39376 "Saint Barthélemy",
39387 "Saint Kitts and Nevis",
39397 "Saint Martin (Saint-Martin (partie française))",
39403 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39408 "Saint Vincent and the Grenadines",
39423 "São Tomé and Príncipe (São Tomé e Príncipe)",
39428 "Saudi Arabia (المملكة العربية السعودية)",
39433 "Senegal (Sénégal)",
39463 "Slovakia (Slovensko)",
39468 "Slovenia (Slovenija)",
39478 "Somalia (Soomaaliya)",
39488 "South Korea (대한민국)",
39493 "South Sudan (جنوب السودان)",
39503 "Sri Lanka (ශ්රී ලංකාව)",
39508 "Sudan (السودان)",
39518 "Svalbard and Jan Mayen",
39529 "Sweden (Sverige)",
39534 "Switzerland (Schweiz)",
39539 "Syria (سوريا)",
39584 "Trinidad and Tobago",
39589 "Tunisia (تونس)",
39594 "Turkey (Türkiye)",
39604 "Turks and Caicos Islands",
39614 "U.S. Virgin Islands",
39624 "Ukraine (Україна)",
39629 "United Arab Emirates (الإمارات العربية المتحدة)",
39651 "Uzbekistan (Oʻzbekiston)",
39661 "Vatican City (Città del Vaticano)",
39672 "Vietnam (Việt Nam)",
39677 "Wallis and Futuna (Wallis-et-Futuna)",
39682 "Western Sahara (الصحراء الغربية)",
39688 "Yemen (اليمن)",
39712 * This script refer to:
39713 * Title: International Telephone Input
39714 * Author: Jack O'Connor
39715 * Code version: v12.1.12
39716 * Availability: https://github.com/jackocnr/intl-tel-input.git
39720 * @class Roo.bootstrap.PhoneInput
39721 * @extends Roo.bootstrap.TriggerField
39722 * An input with International dial-code selection
39724 * @cfg {String} defaultDialCode default '+852'
39725 * @cfg {Array} preferedCountries default []
39728 * Create a new PhoneInput.
39729 * @param {Object} config Configuration options
39732 Roo.bootstrap.PhoneInput = function(config) {
39733 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39736 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39738 listWidth: undefined,
39740 selectedClass: 'active',
39742 invalidClass : "has-warning",
39744 validClass: 'has-success',
39746 allowed: '0123456789',
39749 * @cfg {String} defaultDialCode The default dial code when initializing the input
39751 defaultDialCode: '+852',
39754 * @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
39756 preferedCountries: false,
39758 getAutoCreate : function()
39760 var data = Roo.bootstrap.PhoneInputData();
39761 var align = this.labelAlign || this.parentLabelAlign();
39764 this.allCountries = [];
39765 this.dialCodeMapping = [];
39767 for (var i = 0; i < data.length; i++) {
39769 this.allCountries[i] = {
39773 priority: c[3] || 0,
39774 areaCodes: c[4] || null
39776 this.dialCodeMapping[c[2]] = {
39779 priority: c[3] || 0,
39780 areaCodes: c[4] || null
39792 cls : 'form-control tel-input',
39793 autocomplete: 'new-password'
39796 var hiddenInput = {
39799 cls: 'hidden-tel-input'
39803 hiddenInput.name = this.name;
39806 if (this.disabled) {
39807 input.disabled = true;
39810 var flag_container = {
39827 cls: this.hasFeedback ? 'has-feedback' : '',
39833 cls: 'dial-code-holder',
39840 cls: 'roo-select2-container input-group',
39847 if (this.fieldLabel.length) {
39850 tooltip: 'This field is required'
39856 cls: 'control-label',
39862 html: this.fieldLabel
39865 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39871 if(this.indicatorpos == 'right') {
39872 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39879 if(align == 'left') {
39887 if(this.labelWidth > 12){
39888 label.style = "width: " + this.labelWidth + 'px';
39890 if(this.labelWidth < 13 && this.labelmd == 0){
39891 this.labelmd = this.labelWidth;
39893 if(this.labellg > 0){
39894 label.cls += ' col-lg-' + this.labellg;
39895 input.cls += ' col-lg-' + (12 - this.labellg);
39897 if(this.labelmd > 0){
39898 label.cls += ' col-md-' + this.labelmd;
39899 container.cls += ' col-md-' + (12 - this.labelmd);
39901 if(this.labelsm > 0){
39902 label.cls += ' col-sm-' + this.labelsm;
39903 container.cls += ' col-sm-' + (12 - this.labelsm);
39905 if(this.labelxs > 0){
39906 label.cls += ' col-xs-' + this.labelxs;
39907 container.cls += ' col-xs-' + (12 - this.labelxs);
39917 var settings = this;
39919 ['xs','sm','md','lg'].map(function(size){
39920 if (settings[size]) {
39921 cfg.cls += ' col-' + size + '-' + settings[size];
39925 this.store = new Roo.data.Store({
39926 proxy : new Roo.data.MemoryProxy({}),
39927 reader : new Roo.data.JsonReader({
39938 'name' : 'dialCode',
39942 'name' : 'priority',
39946 'name' : 'areaCodes',
39953 if(!this.preferedCountries) {
39954 this.preferedCountries = [
39961 var p = this.preferedCountries.reverse();
39964 for (var i = 0; i < p.length; i++) {
39965 for (var j = 0; j < this.allCountries.length; j++) {
39966 if(this.allCountries[j].iso2 == p[i]) {
39967 var t = this.allCountries[j];
39968 this.allCountries.splice(j,1);
39969 this.allCountries.unshift(t);
39975 this.store.proxy.data = {
39977 data: this.allCountries
39983 initEvents : function()
39986 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39988 this.indicator = this.indicatorEl();
39989 this.flag = this.flagEl();
39990 this.dialCodeHolder = this.dialCodeHolderEl();
39992 this.trigger = this.el.select('div.flag-box',true).first();
39993 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39998 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39999 _this.list.setWidth(lw);
40002 this.list.on('mouseover', this.onViewOver, this);
40003 this.list.on('mousemove', this.onViewMove, this);
40004 this.inputEl().on("keyup", this.onKeyUp, this);
40006 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40008 this.view = new Roo.View(this.list, this.tpl, {
40009 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40012 this.view.on('click', this.onViewClick, this);
40013 this.setValue(this.defaultDialCode);
40016 onTriggerClick : function(e)
40018 Roo.log('trigger click');
40023 if(this.isExpanded()){
40025 this.hasFocus = false;
40027 this.store.load({});
40028 this.hasFocus = true;
40033 isExpanded : function()
40035 return this.list.isVisible();
40038 collapse : function()
40040 if(!this.isExpanded()){
40044 Roo.get(document).un('mousedown', this.collapseIf, this);
40045 Roo.get(document).un('mousewheel', this.collapseIf, this);
40046 this.fireEvent('collapse', this);
40050 expand : function()
40054 if(this.isExpanded() || !this.hasFocus){
40058 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40059 this.list.setWidth(lw);
40062 this.restrictHeight();
40064 Roo.get(document).on('mousedown', this.collapseIf, this);
40065 Roo.get(document).on('mousewheel', this.collapseIf, this);
40067 this.fireEvent('expand', this);
40070 restrictHeight : function()
40072 this.list.alignTo(this.inputEl(), this.listAlign);
40073 this.list.alignTo(this.inputEl(), this.listAlign);
40076 onViewOver : function(e, t)
40078 if(this.inKeyMode){
40081 var item = this.view.findItemFromChild(t);
40084 var index = this.view.indexOf(item);
40085 this.select(index, false);
40090 onViewClick : function(view, doFocus, el, e)
40092 var index = this.view.getSelectedIndexes()[0];
40094 var r = this.store.getAt(index);
40097 this.onSelect(r, index);
40099 if(doFocus !== false && !this.blockFocus){
40100 this.inputEl().focus();
40104 onViewMove : function(e, t)
40106 this.inKeyMode = false;
40109 select : function(index, scrollIntoView)
40111 this.selectedIndex = index;
40112 this.view.select(index);
40113 if(scrollIntoView !== false){
40114 var el = this.view.getNode(index);
40116 this.list.scrollChildIntoView(el, false);
40121 createList : function()
40123 this.list = Roo.get(document.body).createChild({
40125 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40126 style: 'display:none'
40129 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40132 collapseIf : function(e)
40134 var in_combo = e.within(this.el);
40135 var in_list = e.within(this.list);
40136 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40138 if (in_combo || in_list || is_list) {
40144 onSelect : function(record, index)
40146 if(this.fireEvent('beforeselect', this, record, index) !== false){
40148 this.setFlagClass(record.data.iso2);
40149 this.setDialCode(record.data.dialCode);
40150 this.hasFocus = false;
40152 this.fireEvent('select', this, record, index);
40156 flagEl : function()
40158 var flag = this.el.select('div.flag',true).first();
40165 dialCodeHolderEl : function()
40167 var d = this.el.select('input.dial-code-holder',true).first();
40174 setDialCode : function(v)
40176 this.dialCodeHolder.dom.value = '+'+v;
40179 setFlagClass : function(n)
40181 this.flag.dom.className = 'flag '+n;
40184 getValue : function()
40186 var v = this.inputEl().getValue();
40187 if(this.dialCodeHolder) {
40188 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40193 setValue : function(v)
40195 var d = this.getDialCode(v);
40197 //invalid dial code
40198 if(v.length == 0 || !d || d.length == 0) {
40200 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40201 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40207 this.setFlagClass(this.dialCodeMapping[d].iso2);
40208 this.setDialCode(d);
40209 this.inputEl().dom.value = v.replace('+'+d,'');
40210 this.hiddenEl().dom.value = this.getValue();
40215 getDialCode : function(v)
40219 if (v.length == 0) {
40220 return this.dialCodeHolder.dom.value;
40224 if (v.charAt(0) != "+") {
40227 var numericChars = "";
40228 for (var i = 1; i < v.length; i++) {
40229 var c = v.charAt(i);
40232 if (this.dialCodeMapping[numericChars]) {
40233 dialCode = v.substr(1, i);
40235 if (numericChars.length == 4) {
40245 this.setValue(this.defaultDialCode);
40249 hiddenEl : function()
40251 return this.el.select('input.hidden-tel-input',true).first();
40254 onKeyUp : function(e){
40256 var k = e.getKey();
40257 var c = e.getCharCode();
40260 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40261 this.allowed.indexOf(String.fromCharCode(c)) === -1
40266 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40269 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40273 this.setValue(this.getValue());
40278 * @class Roo.bootstrap.MoneyField
40279 * @extends Roo.bootstrap.ComboBox
40280 * Bootstrap MoneyField class
40283 * Create a new MoneyField.
40284 * @param {Object} config Configuration options
40287 Roo.bootstrap.MoneyField = function(config) {
40289 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40293 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40296 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40298 allowDecimals : true,
40300 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40302 decimalSeparator : ".",
40304 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40306 decimalPrecision : 0,
40308 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40310 allowNegative : true,
40312 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40316 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40318 minValue : Number.NEGATIVE_INFINITY,
40320 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40322 maxValue : Number.MAX_VALUE,
40324 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40326 minText : "The minimum value for this field is {0}",
40328 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40330 maxText : "The maximum value for this field is {0}",
40332 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40333 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40335 nanText : "{0} is not a valid number",
40337 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40341 * @cfg {String} defaults currency of the MoneyField
40342 * value should be in lkey
40344 defaultCurrency : false,
40346 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40348 thousandsDelimiter : false,
40358 getAutoCreate : function()
40360 var align = this.labelAlign || this.parentLabelAlign();
40372 cls : 'form-control roo-money-amount-input',
40373 autocomplete: 'new-password'
40376 var hiddenInput = {
40380 cls: 'hidden-number-input'
40384 hiddenInput.name = this.name;
40387 if (this.disabled) {
40388 input.disabled = true;
40391 var clg = 12 - this.inputlg;
40392 var cmd = 12 - this.inputmd;
40393 var csm = 12 - this.inputsm;
40394 var cxs = 12 - this.inputxs;
40398 cls : 'row roo-money-field',
40402 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40406 cls: 'roo-select2-container input-group',
40410 cls : 'form-control roo-money-currency-input',
40411 autocomplete: 'new-password',
40413 name : this.currencyName
40417 cls : 'input-group-addon',
40431 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40435 cls: this.hasFeedback ? 'has-feedback' : '',
40446 if (this.fieldLabel.length) {
40449 tooltip: 'This field is required'
40455 cls: 'control-label',
40461 html: this.fieldLabel
40464 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40470 if(this.indicatorpos == 'right') {
40471 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40478 if(align == 'left') {
40486 if(this.labelWidth > 12){
40487 label.style = "width: " + this.labelWidth + 'px';
40489 if(this.labelWidth < 13 && this.labelmd == 0){
40490 this.labelmd = this.labelWidth;
40492 if(this.labellg > 0){
40493 label.cls += ' col-lg-' + this.labellg;
40494 input.cls += ' col-lg-' + (12 - this.labellg);
40496 if(this.labelmd > 0){
40497 label.cls += ' col-md-' + this.labelmd;
40498 container.cls += ' col-md-' + (12 - this.labelmd);
40500 if(this.labelsm > 0){
40501 label.cls += ' col-sm-' + this.labelsm;
40502 container.cls += ' col-sm-' + (12 - this.labelsm);
40504 if(this.labelxs > 0){
40505 label.cls += ' col-xs-' + this.labelxs;
40506 container.cls += ' col-xs-' + (12 - this.labelxs);
40517 var settings = this;
40519 ['xs','sm','md','lg'].map(function(size){
40520 if (settings[size]) {
40521 cfg.cls += ' col-' + size + '-' + settings[size];
40528 initEvents : function()
40530 this.indicator = this.indicatorEl();
40532 this.initCurrencyEvent();
40534 this.initNumberEvent();
40537 initCurrencyEvent : function()
40540 throw "can not find store for combo";
40543 this.store = Roo.factory(this.store, Roo.data);
40544 this.store.parent = this;
40548 this.triggerEl = this.el.select('.input-group-addon', true).first();
40550 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40555 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40556 _this.list.setWidth(lw);
40559 this.list.on('mouseover', this.onViewOver, this);
40560 this.list.on('mousemove', this.onViewMove, this);
40561 this.list.on('scroll', this.onViewScroll, this);
40564 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40567 this.view = new Roo.View(this.list, this.tpl, {
40568 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40571 this.view.on('click', this.onViewClick, this);
40573 this.store.on('beforeload', this.onBeforeLoad, this);
40574 this.store.on('load', this.onLoad, this);
40575 this.store.on('loadexception', this.onLoadException, this);
40577 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40578 "up" : function(e){
40579 this.inKeyMode = true;
40583 "down" : function(e){
40584 if(!this.isExpanded()){
40585 this.onTriggerClick();
40587 this.inKeyMode = true;
40592 "enter" : function(e){
40595 if(this.fireEvent("specialkey", this, e)){
40596 this.onViewClick(false);
40602 "esc" : function(e){
40606 "tab" : function(e){
40609 if(this.fireEvent("specialkey", this, e)){
40610 this.onViewClick(false);
40618 doRelay : function(foo, bar, hname){
40619 if(hname == 'down' || this.scope.isExpanded()){
40620 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40628 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40632 initNumberEvent : function(e)
40634 this.inputEl().on("keydown" , this.fireKey, this);
40635 this.inputEl().on("focus", this.onFocus, this);
40636 this.inputEl().on("blur", this.onBlur, this);
40638 this.inputEl().relayEvent('keyup', this);
40640 if(this.indicator){
40641 this.indicator.addClass('invisible');
40644 this.originalValue = this.getValue();
40646 if(this.validationEvent == 'keyup'){
40647 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40648 this.inputEl().on('keyup', this.filterValidation, this);
40650 else if(this.validationEvent !== false){
40651 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40654 if(this.selectOnFocus){
40655 this.on("focus", this.preFocus, this);
40658 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40659 this.inputEl().on("keypress", this.filterKeys, this);
40661 this.inputEl().relayEvent('keypress', this);
40664 var allowed = "0123456789";
40666 if(this.allowDecimals){
40667 allowed += this.decimalSeparator;
40670 if(this.allowNegative){
40674 if(this.thousandsDelimiter) {
40678 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40680 var keyPress = function(e){
40682 var k = e.getKey();
40684 var c = e.getCharCode();
40687 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40688 allowed.indexOf(String.fromCharCode(c)) === -1
40694 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40698 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40703 this.inputEl().on("keypress", keyPress, this);
40707 onTriggerClick : function(e)
40714 this.loadNext = false;
40716 if(this.isExpanded()){
40721 this.hasFocus = true;
40723 if(this.triggerAction == 'all') {
40724 this.doQuery(this.allQuery, true);
40728 this.doQuery(this.getRawValue());
40731 getCurrency : function()
40733 var v = this.currencyEl().getValue();
40738 restrictHeight : function()
40740 this.list.alignTo(this.currencyEl(), this.listAlign);
40741 this.list.alignTo(this.currencyEl(), this.listAlign);
40744 onViewClick : function(view, doFocus, el, e)
40746 var index = this.view.getSelectedIndexes()[0];
40748 var r = this.store.getAt(index);
40751 this.onSelect(r, index);
40755 onSelect : function(record, index){
40757 if(this.fireEvent('beforeselect', this, record, index) !== false){
40759 this.setFromCurrencyData(index > -1 ? record.data : false);
40763 this.fireEvent('select', this, record, index);
40767 setFromCurrencyData : function(o)
40771 this.lastCurrency = o;
40773 if (this.currencyField) {
40774 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40776 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40779 this.lastSelectionText = currency;
40781 //setting default currency
40782 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40783 this.setCurrency(this.defaultCurrency);
40787 this.setCurrency(currency);
40790 setFromData : function(o)
40794 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40796 this.setFromCurrencyData(c);
40801 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40803 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40806 this.setValue(value);
40810 setCurrency : function(v)
40812 this.currencyValue = v;
40815 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40820 setValue : function(v)
40822 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40828 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40830 this.inputEl().dom.value = (v == '') ? '' :
40831 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40833 if(!this.allowZero && v === '0') {
40834 this.hiddenEl().dom.value = '';
40835 this.inputEl().dom.value = '';
40842 getRawValue : function()
40844 var v = this.inputEl().getValue();
40849 getValue : function()
40851 return this.fixPrecision(this.parseValue(this.getRawValue()));
40854 parseValue : function(value)
40856 if(this.thousandsDelimiter) {
40858 r = new RegExp(",", "g");
40859 value = value.replace(r, "");
40862 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40863 return isNaN(value) ? '' : value;
40867 fixPrecision : function(value)
40869 if(this.thousandsDelimiter) {
40871 r = new RegExp(",", "g");
40872 value = value.replace(r, "");
40875 var nan = isNaN(value);
40877 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40878 return nan ? '' : value;
40880 return parseFloat(value).toFixed(this.decimalPrecision);
40883 decimalPrecisionFcn : function(v)
40885 return Math.floor(v);
40888 validateValue : function(value)
40890 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40894 var num = this.parseValue(value);
40897 this.markInvalid(String.format(this.nanText, value));
40901 if(num < this.minValue){
40902 this.markInvalid(String.format(this.minText, this.minValue));
40906 if(num > this.maxValue){
40907 this.markInvalid(String.format(this.maxText, this.maxValue));
40914 validate : function()
40916 if(this.disabled || this.allowBlank){
40921 var currency = this.getCurrency();
40923 if(this.validateValue(this.getRawValue()) && currency.length){
40928 this.markInvalid();
40932 getName: function()
40937 beforeBlur : function()
40943 var v = this.parseValue(this.getRawValue());
40950 onBlur : function()
40954 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40955 //this.el.removeClass(this.focusClass);
40958 this.hasFocus = false;
40960 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40964 var v = this.getValue();
40966 if(String(v) !== String(this.startValue)){
40967 this.fireEvent('change', this, v, this.startValue);
40970 this.fireEvent("blur", this);
40973 inputEl : function()
40975 return this.el.select('.roo-money-amount-input', true).first();
40978 currencyEl : function()
40980 return this.el.select('.roo-money-currency-input', true).first();
40983 hiddenEl : function()
40985 return this.el.select('input.hidden-number-input',true).first();