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 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2867 setSize : function(w,h)
2877 if (!this.rendered) {
2881 //this.el.setStyle('display', 'block');
2882 this.el.removeClass('hideing');
2883 this.el.addClass('show');
2885 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2888 this.el.addClass('in');
2891 this.el.addClass('in');
2894 // not sure how we can show data in here..
2896 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2899 Roo.get(document.body).addClass("x-body-masked");
2901 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2902 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2903 this.maskEl.addClass('show');
2907 this.fireEvent('show', this);
2909 // set zindex here - otherwise it appears to be ignored...
2910 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2913 this.items.forEach( function(e) {
2914 e.layout ? e.layout() : false;
2922 if(this.fireEvent("beforehide", this) !== false){
2923 this.maskEl.removeClass('show');
2924 Roo.get(document.body).removeClass("x-body-masked");
2925 this.el.removeClass('in');
2926 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2928 if(this.animate){ // why
2929 this.el.addClass('hideing');
2931 if (!this.el.hasClass('hideing')) {
2932 return; // it's been shown again...
2934 this.el.removeClass('show');
2935 this.el.removeClass('hideing');
2939 this.el.removeClass('show');
2941 this.fireEvent('hide', this);
2944 isVisible : function()
2947 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2951 addButton : function(str, cb)
2955 var b = Roo.apply({}, { html : str } );
2956 b.xns = b.xns || Roo.bootstrap;
2957 b.xtype = b.xtype || 'Button';
2958 if (typeof(b.listeners) == 'undefined') {
2959 b.listeners = { click : cb.createDelegate(this) };
2962 var btn = Roo.factory(b);
2964 btn.render(this.el.select('.modal-footer div').first());
2970 setDefaultButton : function(btn)
2972 //this.el.select('.modal-footer').()
2976 resizeTo: function(w,h)
2980 this.dialogEl.setWidth(w);
2981 if (this.diff === false) {
2982 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2985 this.bodyEl.setHeight(h-this.diff);
2987 this.fireEvent('resize', this);
2990 setContentSize : function(w, h)
2994 onButtonClick: function(btn,e)
2997 this.fireEvent('btnclick', btn.name, e);
3000 * Set the title of the Dialog
3001 * @param {String} str new Title
3003 setTitle: function(str) {
3004 this.titleEl.dom.innerHTML = str;
3007 * Set the body of the Dialog
3008 * @param {String} str new Title
3010 setBody: function(str) {
3011 this.bodyEl.dom.innerHTML = str;
3014 * Set the body of the Dialog using the template
3015 * @param {Obj} data - apply this data to the template and replace the body contents.
3017 applyBody: function(obj)
3020 Roo.log("Error - using apply Body without a template");
3023 this.tmpl.overwrite(this.bodyEl, obj);
3029 Roo.apply(Roo.bootstrap.Modal, {
3031 * Button config that displays a single OK button
3040 * Button config that displays Yes and No buttons
3056 * Button config that displays OK and Cancel buttons
3071 * Button config that displays Yes, No and Cancel buttons
3095 * messagebox - can be used as a replace
3099 * @class Roo.MessageBox
3100 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3104 Roo.Msg.alert('Status', 'Changes saved successfully.');
3106 // Prompt for user data:
3107 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3109 // process text value...
3113 // Show a dialog using config options:
3115 title:'Save Changes?',
3116 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3117 buttons: Roo.Msg.YESNOCANCEL,
3124 Roo.bootstrap.MessageBox = function(){
3125 var dlg, opt, mask, waitTimer;
3126 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3127 var buttons, activeTextEl, bwidth;
3131 var handleButton = function(button){
3133 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3137 var handleHide = function(){
3139 dlg.el.removeClass(opt.cls);
3142 // Roo.TaskMgr.stop(waitTimer);
3143 // waitTimer = null;
3148 var updateButtons = function(b){
3151 buttons["ok"].hide();
3152 buttons["cancel"].hide();
3153 buttons["yes"].hide();
3154 buttons["no"].hide();
3155 //dlg.footer.dom.style.display = 'none';
3158 dlg.footerEl.dom.style.display = '';
3159 for(var k in buttons){
3160 if(typeof buttons[k] != "function"){
3163 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3164 width += buttons[k].el.getWidth()+15;
3174 var handleEsc = function(d, k, e){
3175 if(opt && opt.closable !== false){
3185 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3186 * @return {Roo.BasicDialog} The BasicDialog element
3188 getDialog : function(){
3190 dlg = new Roo.bootstrap.Modal( {
3193 //constraintoviewport:false,
3195 //collapsible : false,
3200 //buttonAlign:"center",
3201 closeClick : function(){
3202 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3205 handleButton("cancel");
3210 dlg.on("hide", handleHide);
3212 //dlg.addKeyListener(27, handleEsc);
3214 this.buttons = buttons;
3215 var bt = this.buttonText;
3216 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3217 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3218 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3219 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3221 bodyEl = dlg.bodyEl.createChild({
3223 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3224 '<textarea class="roo-mb-textarea"></textarea>' +
3225 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3227 msgEl = bodyEl.dom.firstChild;
3228 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3229 textboxEl.enableDisplayMode();
3230 textboxEl.addKeyListener([10,13], function(){
3231 if(dlg.isVisible() && opt && opt.buttons){
3234 }else if(opt.buttons.yes){
3235 handleButton("yes");
3239 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3240 textareaEl.enableDisplayMode();
3241 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3242 progressEl.enableDisplayMode();
3244 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3245 var pf = progressEl.dom.firstChild;
3247 pp = Roo.get(pf.firstChild);
3248 pp.setHeight(pf.offsetHeight);
3256 * Updates the message box body text
3257 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3258 * the XHTML-compliant non-breaking space character '&#160;')
3259 * @return {Roo.MessageBox} This message box
3261 updateText : function(text)
3263 if(!dlg.isVisible() && !opt.width){
3264 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3265 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3267 msgEl.innerHTML = text || ' ';
3269 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3270 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3272 Math.min(opt.width || cw , this.maxWidth),
3273 Math.max(opt.minWidth || this.minWidth, bwidth)
3276 activeTextEl.setWidth(w);
3278 if(dlg.isVisible()){
3279 dlg.fixedcenter = false;
3281 // to big, make it scroll. = But as usual stupid IE does not support
3284 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3285 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3286 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3288 bodyEl.dom.style.height = '';
3289 bodyEl.dom.style.overflowY = '';
3292 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3294 bodyEl.dom.style.overflowX = '';
3297 dlg.setContentSize(w, bodyEl.getHeight());
3298 if(dlg.isVisible()){
3299 dlg.fixedcenter = true;
3305 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3306 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3307 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3308 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3309 * @return {Roo.MessageBox} This message box
3311 updateProgress : function(value, text){
3313 this.updateText(text);
3316 if (pp) { // weird bug on my firefox - for some reason this is not defined
3317 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3318 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3324 * Returns true if the message box is currently displayed
3325 * @return {Boolean} True if the message box is visible, else false
3327 isVisible : function(){
3328 return dlg && dlg.isVisible();
3332 * Hides the message box if it is displayed
3335 if(this.isVisible()){
3341 * Displays a new message box, or reinitializes an existing message box, based on the config options
3342 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3343 * The following config object properties are supported:
3345 Property Type Description
3346 ---------- --------------- ------------------------------------------------------------------------------------
3347 animEl String/Element An id or Element from which the message box should animate as it opens and
3348 closes (defaults to undefined)
3349 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3350 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3351 closable Boolean False to hide the top-right close button (defaults to true). Note that
3352 progress and wait dialogs will ignore this property and always hide the
3353 close button as they can only be closed programmatically.
3354 cls String A custom CSS class to apply to the message box element
3355 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3356 displayed (defaults to 75)
3357 fn Function A callback function to execute after closing the dialog. The arguments to the
3358 function will be btn (the name of the button that was clicked, if applicable,
3359 e.g. "ok"), and text (the value of the active text field, if applicable).
3360 Progress and wait dialogs will ignore this option since they do not respond to
3361 user actions and can only be closed programmatically, so any required function
3362 should be called by the same code after it closes the dialog.
3363 icon String A CSS class that provides a background image to be used as an icon for
3364 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3365 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3366 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3367 modal Boolean False to allow user interaction with the page while the message box is
3368 displayed (defaults to true)
3369 msg String A string that will replace the existing message box body text (defaults
3370 to the XHTML-compliant non-breaking space character ' ')
3371 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3372 progress Boolean True to display a progress bar (defaults to false)
3373 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3374 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3375 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3376 title String The title text
3377 value String The string value to set into the active textbox element if displayed
3378 wait Boolean True to display a progress bar (defaults to false)
3379 width Number The width of the dialog in pixels
3386 msg: 'Please enter your address:',
3388 buttons: Roo.MessageBox.OKCANCEL,
3391 animEl: 'addAddressBtn'
3394 * @param {Object} config Configuration options
3395 * @return {Roo.MessageBox} This message box
3397 show : function(options)
3400 // this causes nightmares if you show one dialog after another
3401 // especially on callbacks..
3403 if(this.isVisible()){
3406 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3407 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3408 Roo.log("New Dialog Message:" + options.msg )
3409 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3410 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3413 var d = this.getDialog();
3415 d.setTitle(opt.title || " ");
3416 d.closeEl.setDisplayed(opt.closable !== false);
3417 activeTextEl = textboxEl;
3418 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3423 textareaEl.setHeight(typeof opt.multiline == "number" ?
3424 opt.multiline : this.defaultTextHeight);
3425 activeTextEl = textareaEl;
3434 progressEl.setDisplayed(opt.progress === true);
3435 this.updateProgress(0);
3436 activeTextEl.dom.value = opt.value || "";
3438 dlg.setDefaultButton(activeTextEl);
3440 var bs = opt.buttons;
3444 }else if(bs && bs.yes){
3445 db = buttons["yes"];
3447 dlg.setDefaultButton(db);
3449 bwidth = updateButtons(opt.buttons);
3450 this.updateText(opt.msg);
3452 d.el.addClass(opt.cls);
3454 d.proxyDrag = opt.proxyDrag === true;
3455 d.modal = opt.modal !== false;
3456 d.mask = opt.modal !== false ? mask : false;
3458 // force it to the end of the z-index stack so it gets a cursor in FF
3459 document.body.appendChild(dlg.el.dom);
3460 d.animateTarget = null;
3461 d.show(options.animEl);
3467 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3468 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3469 * and closing the message box when the process is complete.
3470 * @param {String} title The title bar text
3471 * @param {String} msg The message box body text
3472 * @return {Roo.MessageBox} This message box
3474 progress : function(title, msg){
3481 minWidth: this.minProgressWidth,
3488 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3489 * If a callback function is passed it will be called after the user clicks the button, and the
3490 * id of the button that was clicked will be passed as the only parameter to the callback
3491 * (could also be the top-right close button).
3492 * @param {String} title The title bar text
3493 * @param {String} msg The message box body text
3494 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3495 * @param {Object} scope (optional) The scope of the callback function
3496 * @return {Roo.MessageBox} This message box
3498 alert : function(title, msg, fn, scope)
3513 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3514 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3515 * You are responsible for closing the message box when the process is complete.
3516 * @param {String} msg The message box body text
3517 * @param {String} title (optional) The title bar text
3518 * @return {Roo.MessageBox} This message box
3520 wait : function(msg, title){
3531 waitTimer = Roo.TaskMgr.start({
3533 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3541 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3542 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3543 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3544 * @param {String} title The title bar text
3545 * @param {String} msg The message box body text
3546 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3547 * @param {Object} scope (optional) The scope of the callback function
3548 * @return {Roo.MessageBox} This message box
3550 confirm : function(title, msg, fn, scope){
3554 buttons: this.YESNO,
3563 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3564 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3565 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3566 * (could also be the top-right close button) and the text that was entered will be passed as the two
3567 * parameters to the callback.
3568 * @param {String} title The title bar text
3569 * @param {String} msg The message box body text
3570 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3571 * @param {Object} scope (optional) The scope of the callback function
3572 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3573 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3574 * @return {Roo.MessageBox} This message box
3576 prompt : function(title, msg, fn, scope, multiline){
3580 buttons: this.OKCANCEL,
3585 multiline: multiline,
3592 * Button config that displays a single OK button
3597 * Button config that displays Yes and No buttons
3600 YESNO : {yes:true, no:true},
3602 * Button config that displays OK and Cancel buttons
3605 OKCANCEL : {ok:true, cancel:true},
3607 * Button config that displays Yes, No and Cancel buttons
3610 YESNOCANCEL : {yes:true, no:true, cancel:true},
3613 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3616 defaultTextHeight : 75,
3618 * The maximum width in pixels of the message box (defaults to 600)
3623 * The minimum width in pixels of the message box (defaults to 100)
3628 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3629 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3632 minProgressWidth : 250,
3634 * An object containing the default button text strings that can be overriden for localized language support.
3635 * Supported properties are: ok, cancel, yes and no.
3636 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3649 * Shorthand for {@link Roo.MessageBox}
3651 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3652 Roo.Msg = Roo.Msg || Roo.MessageBox;
3661 * @class Roo.bootstrap.Navbar
3662 * @extends Roo.bootstrap.Component
3663 * Bootstrap Navbar class
3666 * Create a new Navbar
3667 * @param {Object} config The config object
3671 Roo.bootstrap.Navbar = function(config){
3672 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3676 * @event beforetoggle
3677 * Fire before toggle the menu
3678 * @param {Roo.EventObject} e
3680 "beforetoggle" : true
3684 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3693 getAutoCreate : function(){
3696 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3700 initEvents :function ()
3702 //Roo.log(this.el.select('.navbar-toggle',true));
3703 this.el.select('.navbar-toggle',true).on('click', function() {
3704 if(this.fireEvent('beforetoggle', this) !== false){
3705 this.el.select('.navbar-collapse',true).toggleClass('in');
3715 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3717 var size = this.el.getSize();
3718 this.maskEl.setSize(size.width, size.height);
3719 this.maskEl.enableDisplayMode("block");
3728 getChildContainer : function()
3730 if (this.el.select('.collapse').getCount()) {
3731 return this.el.select('.collapse',true).first();
3764 * @class Roo.bootstrap.NavSimplebar
3765 * @extends Roo.bootstrap.Navbar
3766 * Bootstrap Sidebar class
3768 * @cfg {Boolean} inverse is inverted color
3770 * @cfg {String} type (nav | pills | tabs)
3771 * @cfg {Boolean} arrangement stacked | justified
3772 * @cfg {String} align (left | right) alignment
3774 * @cfg {Boolean} main (true|false) main nav bar? default false
3775 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3777 * @cfg {String} tag (header|footer|nav|div) default is nav
3783 * Create a new Sidebar
3784 * @param {Object} config The config object
3788 Roo.bootstrap.NavSimplebar = function(config){
3789 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3792 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3808 getAutoCreate : function(){
3812 tag : this.tag || 'div',
3825 this.type = this.type || 'nav';
3826 if (['tabs','pills'].indexOf(this.type)!==-1) {
3827 cfg.cn[0].cls += ' nav-' + this.type
3831 if (this.type!=='nav') {
3832 Roo.log('nav type must be nav/tabs/pills')
3834 cfg.cn[0].cls += ' navbar-nav'
3840 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3841 cfg.cn[0].cls += ' nav-' + this.arrangement;
3845 if (this.align === 'right') {
3846 cfg.cn[0].cls += ' navbar-right';
3850 cfg.cls += ' navbar-inverse';
3877 * @class Roo.bootstrap.NavHeaderbar
3878 * @extends Roo.bootstrap.NavSimplebar
3879 * Bootstrap Sidebar class
3881 * @cfg {String} brand what is brand
3882 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3883 * @cfg {String} brand_href href of the brand
3884 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3885 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3886 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3887 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3890 * Create a new Sidebar
3891 * @param {Object} config The config object
3895 Roo.bootstrap.NavHeaderbar = function(config){
3896 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3900 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3907 desktopCenter : false,
3910 getAutoCreate : function(){
3913 tag: this.nav || 'nav',
3920 if (this.desktopCenter) {
3921 cn.push({cls : 'container', cn : []});
3928 cls: 'navbar-header',
3933 cls: 'navbar-toggle',
3934 'data-toggle': 'collapse',
3939 html: 'Toggle navigation'
3961 cls: 'collapse navbar-collapse',
3965 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3967 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3968 cfg.cls += ' navbar-' + this.position;
3970 // tag can override this..
3972 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3975 if (this.brand !== '') {
3978 href: this.brand_href ? this.brand_href : '#',
3979 cls: 'navbar-brand',
3987 cfg.cls += ' main-nav';
3995 getHeaderChildContainer : function()
3997 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3998 return this.el.select('.navbar-header',true).first();
4001 return this.getChildContainer();
4005 initEvents : function()
4007 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4009 if (this.autohide) {
4014 Roo.get(document).on('scroll',function(e) {
4015 var ns = Roo.get(document).getScroll().top;
4016 var os = prevScroll;
4020 ft.removeClass('slideDown');
4021 ft.addClass('slideUp');
4024 ft.removeClass('slideUp');
4025 ft.addClass('slideDown');
4046 * @class Roo.bootstrap.NavSidebar
4047 * @extends Roo.bootstrap.Navbar
4048 * Bootstrap Sidebar class
4051 * Create a new Sidebar
4052 * @param {Object} config The config object
4056 Roo.bootstrap.NavSidebar = function(config){
4057 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4060 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4062 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4064 getAutoCreate : function(){
4069 cls: 'sidebar sidebar-nav'
4091 * @class Roo.bootstrap.NavGroup
4092 * @extends Roo.bootstrap.Component
4093 * Bootstrap NavGroup class
4094 * @cfg {String} align (left|right)
4095 * @cfg {Boolean} inverse
4096 * @cfg {String} type (nav|pills|tab) default nav
4097 * @cfg {String} navId - reference Id for navbar.
4101 * Create a new nav group
4102 * @param {Object} config The config object
4105 Roo.bootstrap.NavGroup = function(config){
4106 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4109 Roo.bootstrap.NavGroup.register(this);
4113 * Fires when the active item changes
4114 * @param {Roo.bootstrap.NavGroup} this
4115 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4116 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4123 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4134 getAutoCreate : function()
4136 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4143 if (['tabs','pills'].indexOf(this.type)!==-1) {
4144 cfg.cls += ' nav-' + this.type
4146 if (this.type!=='nav') {
4147 Roo.log('nav type must be nav/tabs/pills')
4149 cfg.cls += ' navbar-nav'
4152 if (this.parent() && this.parent().sidebar) {
4155 cls: 'dashboard-menu sidebar-menu'
4161 if (this.form === true) {
4167 if (this.align === 'right') {
4168 cfg.cls += ' navbar-right';
4170 cfg.cls += ' navbar-left';
4174 if (this.align === 'right') {
4175 cfg.cls += ' navbar-right';
4179 cfg.cls += ' navbar-inverse';
4187 * sets the active Navigation item
4188 * @param {Roo.bootstrap.NavItem} the new current navitem
4190 setActiveItem : function(item)
4193 Roo.each(this.navItems, function(v){
4198 v.setActive(false, true);
4205 item.setActive(true, true);
4206 this.fireEvent('changed', this, item, prev);
4211 * gets the active Navigation item
4212 * @return {Roo.bootstrap.NavItem} the current navitem
4214 getActive : function()
4218 Roo.each(this.navItems, function(v){
4229 indexOfNav : function()
4233 Roo.each(this.navItems, function(v,i){
4244 * adds a Navigation item
4245 * @param {Roo.bootstrap.NavItem} the navitem to add
4247 addItem : function(cfg)
4249 var cn = new Roo.bootstrap.NavItem(cfg);
4251 cn.parentId = this.id;
4252 cn.onRender(this.el, null);
4256 * register a Navigation item
4257 * @param {Roo.bootstrap.NavItem} the navitem to add
4259 register : function(item)
4261 this.navItems.push( item);
4262 item.navId = this.navId;
4267 * clear all the Navigation item
4270 clearAll : function()
4273 this.el.dom.innerHTML = '';
4276 getNavItem: function(tabId)
4279 Roo.each(this.navItems, function(e) {
4280 if (e.tabId == tabId) {
4290 setActiveNext : function()
4292 var i = this.indexOfNav(this.getActive());
4293 if (i > this.navItems.length) {
4296 this.setActiveItem(this.navItems[i+1]);
4298 setActivePrev : function()
4300 var i = this.indexOfNav(this.getActive());
4304 this.setActiveItem(this.navItems[i-1]);
4306 clearWasActive : function(except) {
4307 Roo.each(this.navItems, function(e) {
4308 if (e.tabId != except.tabId && e.was_active) {
4309 e.was_active = false;
4316 getWasActive : function ()
4319 Roo.each(this.navItems, function(e) {
4334 Roo.apply(Roo.bootstrap.NavGroup, {
4338 * register a Navigation Group
4339 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4341 register : function(navgrp)
4343 this.groups[navgrp.navId] = navgrp;
4347 * fetch a Navigation Group based on the navigation ID
4348 * @param {string} the navgroup to add
4349 * @returns {Roo.bootstrap.NavGroup} the navgroup
4351 get: function(navId) {
4352 if (typeof(this.groups[navId]) == 'undefined') {
4354 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4356 return this.groups[navId] ;
4371 * @class Roo.bootstrap.NavItem
4372 * @extends Roo.bootstrap.Component
4373 * Bootstrap Navbar.NavItem class
4374 * @cfg {String} href link to
4375 * @cfg {String} html content of button
4376 * @cfg {String} badge text inside badge
4377 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4378 * @cfg {String} glyphicon name of glyphicon
4379 * @cfg {String} icon name of font awesome icon
4380 * @cfg {Boolean} active Is item active
4381 * @cfg {Boolean} disabled Is item disabled
4383 * @cfg {Boolean} preventDefault (true | false) default false
4384 * @cfg {String} tabId the tab that this item activates.
4385 * @cfg {String} tagtype (a|span) render as a href or span?
4386 * @cfg {Boolean} animateRef (true|false) link to element default false
4389 * Create a new Navbar Item
4390 * @param {Object} config The config object
4392 Roo.bootstrap.NavItem = function(config){
4393 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4398 * The raw click event for the entire grid.
4399 * @param {Roo.EventObject} e
4404 * Fires when the active item active state changes
4405 * @param {Roo.bootstrap.NavItem} this
4406 * @param {boolean} state the new state
4412 * Fires when scroll to element
4413 * @param {Roo.bootstrap.NavItem} this
4414 * @param {Object} options
4415 * @param {Roo.EventObject} e
4423 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4431 preventDefault : false,
4438 getAutoCreate : function(){
4447 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4449 if (this.disabled) {
4450 cfg.cls += ' disabled';
4453 if (this.href || this.html || this.glyphicon || this.icon) {
4457 href : this.href || "#",
4458 html: this.html || ''
4463 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4466 if(this.glyphicon) {
4467 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4472 cfg.cn[0].html += " <span class='caret'></span>";
4476 if (this.badge !== '') {
4478 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4486 initEvents: function()
4488 if (typeof (this.menu) != 'undefined') {
4489 this.menu.parentType = this.xtype;
4490 this.menu.triggerEl = this.el;
4491 this.menu = this.addxtype(Roo.apply({}, this.menu));
4494 this.el.select('a',true).on('click', this.onClick, this);
4496 if(this.tagtype == 'span'){
4497 this.el.select('span',true).on('click', this.onClick, this);
4500 // at this point parent should be available..
4501 this.parent().register(this);
4504 onClick : function(e)
4506 if (e.getTarget('.dropdown-menu-item')) {
4507 // did you click on a menu itemm.... - then don't trigger onclick..
4512 this.preventDefault ||
4515 Roo.log("NavItem - prevent Default?");
4519 if (this.disabled) {
4523 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4524 if (tg && tg.transition) {
4525 Roo.log("waiting for the transitionend");
4531 //Roo.log("fire event clicked");
4532 if(this.fireEvent('click', this, e) === false){
4536 if(this.tagtype == 'span'){
4540 //Roo.log(this.href);
4541 var ael = this.el.select('a',true).first();
4544 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4545 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4546 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4547 return; // ignore... - it's a 'hash' to another page.
4549 Roo.log("NavItem - prevent Default?");
4551 this.scrollToElement(e);
4555 var p = this.parent();
4557 if (['tabs','pills'].indexOf(p.type)!==-1) {
4558 if (typeof(p.setActiveItem) !== 'undefined') {
4559 p.setActiveItem(this);
4563 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4564 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4565 // remove the collapsed menu expand...
4566 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4570 isActive: function () {
4573 setActive : function(state, fire, is_was_active)
4575 if (this.active && !state && this.navId) {
4576 this.was_active = true;
4577 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4579 nv.clearWasActive(this);
4583 this.active = state;
4586 this.el.removeClass('active');
4587 } else if (!this.el.hasClass('active')) {
4588 this.el.addClass('active');
4591 this.fireEvent('changed', this, state);
4594 // show a panel if it's registered and related..
4596 if (!this.navId || !this.tabId || !state || is_was_active) {
4600 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4604 var pan = tg.getPanelByName(this.tabId);
4608 // if we can not flip to new panel - go back to old nav highlight..
4609 if (false == tg.showPanel(pan)) {
4610 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4612 var onav = nv.getWasActive();
4614 onav.setActive(true, false, true);
4623 // this should not be here...
4624 setDisabled : function(state)
4626 this.disabled = state;
4628 this.el.removeClass('disabled');
4629 } else if (!this.el.hasClass('disabled')) {
4630 this.el.addClass('disabled');
4636 * Fetch the element to display the tooltip on.
4637 * @return {Roo.Element} defaults to this.el
4639 tooltipEl : function()
4641 return this.el.select('' + this.tagtype + '', true).first();
4644 scrollToElement : function(e)
4646 var c = document.body;
4649 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4651 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4652 c = document.documentElement;
4655 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4661 var o = target.calcOffsetsTo(c);
4668 this.fireEvent('scrollto', this, options, e);
4670 Roo.get(c).scrollTo('top', options.value, true);
4683 * <span> icon </span>
4684 * <span> text </span>
4685 * <span>badge </span>
4689 * @class Roo.bootstrap.NavSidebarItem
4690 * @extends Roo.bootstrap.NavItem
4691 * Bootstrap Navbar.NavSidebarItem class
4692 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4693 * {Boolean} open is the menu open
4694 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4695 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4696 * {String} buttonSize (sm|md|lg)the extra classes for the button
4697 * {Boolean} showArrow show arrow next to the text (default true)
4699 * Create a new Navbar Button
4700 * @param {Object} config The config object
4702 Roo.bootstrap.NavSidebarItem = function(config){
4703 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4708 * The raw click event for the entire grid.
4709 * @param {Roo.EventObject} e
4714 * Fires when the active item active state changes
4715 * @param {Roo.bootstrap.NavSidebarItem} this
4716 * @param {boolean} state the new state
4724 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4726 badgeWeight : 'default',
4732 buttonWeight : 'default',
4738 getAutoCreate : function(){
4743 href : this.href || '#',
4749 if(this.buttonView){
4752 href : this.href || '#',
4753 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4766 cfg.cls += ' active';
4769 if (this.disabled) {
4770 cfg.cls += ' disabled';
4773 cfg.cls += ' open x-open';
4776 if (this.glyphicon || this.icon) {
4777 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4778 a.cn.push({ tag : 'i', cls : c }) ;
4781 if(!this.buttonView){
4784 html : this.html || ''
4791 if (this.badge !== '') {
4792 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4798 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4801 a.cls += ' dropdown-toggle treeview' ;
4807 initEvents : function()
4809 if (typeof (this.menu) != 'undefined') {
4810 this.menu.parentType = this.xtype;
4811 this.menu.triggerEl = this.el;
4812 this.menu = this.addxtype(Roo.apply({}, this.menu));
4815 this.el.on('click', this.onClick, this);
4817 if(this.badge !== ''){
4818 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4823 onClick : function(e)
4830 if(this.preventDefault){
4834 this.fireEvent('click', this);
4837 disable : function()
4839 this.setDisabled(true);
4844 this.setDisabled(false);
4847 setDisabled : function(state)
4849 if(this.disabled == state){
4853 this.disabled = state;
4856 this.el.addClass('disabled');
4860 this.el.removeClass('disabled');
4865 setActive : function(state)
4867 if(this.active == state){
4871 this.active = state;
4874 this.el.addClass('active');
4878 this.el.removeClass('active');
4883 isActive: function ()
4888 setBadge : function(str)
4894 this.badgeEl.dom.innerHTML = str;
4911 * @class Roo.bootstrap.Row
4912 * @extends Roo.bootstrap.Component
4913 * Bootstrap Row class (contains columns...)
4917 * @param {Object} config The config object
4920 Roo.bootstrap.Row = function(config){
4921 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4924 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4926 getAutoCreate : function(){
4945 * @class Roo.bootstrap.Element
4946 * @extends Roo.bootstrap.Component
4947 * Bootstrap Element class
4948 * @cfg {String} html contents of the element
4949 * @cfg {String} tag tag of the element
4950 * @cfg {String} cls class of the element
4951 * @cfg {Boolean} preventDefault (true|false) default false
4952 * @cfg {Boolean} clickable (true|false) default false
4955 * Create a new Element
4956 * @param {Object} config The config object
4959 Roo.bootstrap.Element = function(config){
4960 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4966 * When a element is chick
4967 * @param {Roo.bootstrap.Element} this
4968 * @param {Roo.EventObject} e
4974 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4979 preventDefault: false,
4982 getAutoCreate : function(){
4986 // cls: this.cls, double assign in parent class Component.js :: onRender
4993 initEvents: function()
4995 Roo.bootstrap.Element.superclass.initEvents.call(this);
4998 this.el.on('click', this.onClick, this);
5003 onClick : function(e)
5005 if(this.preventDefault){
5009 this.fireEvent('click', this, e);
5012 getValue : function()
5014 return this.el.dom.innerHTML;
5017 setValue : function(value)
5019 this.el.dom.innerHTML = value;
5034 * @class Roo.bootstrap.Pagination
5035 * @extends Roo.bootstrap.Component
5036 * Bootstrap Pagination class
5037 * @cfg {String} size xs | sm | md | lg
5038 * @cfg {Boolean} inverse false | true
5041 * Create a new Pagination
5042 * @param {Object} config The config object
5045 Roo.bootstrap.Pagination = function(config){
5046 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5049 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5055 getAutoCreate : function(){
5061 cfg.cls += ' inverse';
5067 cfg.cls += " " + this.cls;
5085 * @class Roo.bootstrap.PaginationItem
5086 * @extends Roo.bootstrap.Component
5087 * Bootstrap PaginationItem class
5088 * @cfg {String} html text
5089 * @cfg {String} href the link
5090 * @cfg {Boolean} preventDefault (true | false) default true
5091 * @cfg {Boolean} active (true | false) default false
5092 * @cfg {Boolean} disabled default false
5096 * Create a new PaginationItem
5097 * @param {Object} config The config object
5101 Roo.bootstrap.PaginationItem = function(config){
5102 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5107 * The raw click event for the entire grid.
5108 * @param {Roo.EventObject} e
5114 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5118 preventDefault: true,
5123 getAutoCreate : function(){
5129 href : this.href ? this.href : '#',
5130 html : this.html ? this.html : ''
5140 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5144 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5150 initEvents: function() {
5152 this.el.on('click', this.onClick, this);
5155 onClick : function(e)
5157 Roo.log('PaginationItem on click ');
5158 if(this.preventDefault){
5166 this.fireEvent('click', this, e);
5182 * @class Roo.bootstrap.Slider
5183 * @extends Roo.bootstrap.Component
5184 * Bootstrap Slider class
5187 * Create a new Slider
5188 * @param {Object} config The config object
5191 Roo.bootstrap.Slider = function(config){
5192 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5195 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5197 getAutoCreate : function(){
5201 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5205 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5217 * Ext JS Library 1.1.1
5218 * Copyright(c) 2006-2007, Ext JS, LLC.
5220 * Originally Released Under LGPL - original licence link has changed is not relivant.
5223 * <script type="text/javascript">
5228 * @class Roo.grid.ColumnModel
5229 * @extends Roo.util.Observable
5230 * This is the default implementation of a ColumnModel used by the Grid. It defines
5231 * the columns in the grid.
5234 var colModel = new Roo.grid.ColumnModel([
5235 {header: "Ticker", width: 60, sortable: true, locked: true},
5236 {header: "Company Name", width: 150, sortable: true},
5237 {header: "Market Cap.", width: 100, sortable: true},
5238 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5239 {header: "Employees", width: 100, sortable: true, resizable: false}
5244 * The config options listed for this class are options which may appear in each
5245 * individual column definition.
5246 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5248 * @param {Object} config An Array of column config objects. See this class's
5249 * config objects for details.
5251 Roo.grid.ColumnModel = function(config){
5253 * The config passed into the constructor
5255 this.config = config;
5258 // if no id, create one
5259 // if the column does not have a dataIndex mapping,
5260 // map it to the order it is in the config
5261 for(var i = 0, len = config.length; i < len; i++){
5263 if(typeof c.dataIndex == "undefined"){
5266 if(typeof c.renderer == "string"){
5267 c.renderer = Roo.util.Format[c.renderer];
5269 if(typeof c.id == "undefined"){
5272 if(c.editor && c.editor.xtype){
5273 c.editor = Roo.factory(c.editor, Roo.grid);
5275 if(c.editor && c.editor.isFormField){
5276 c.editor = new Roo.grid.GridEditor(c.editor);
5278 this.lookup[c.id] = c;
5282 * The width of columns which have no width specified (defaults to 100)
5285 this.defaultWidth = 100;
5288 * Default sortable of columns which have no sortable specified (defaults to false)
5291 this.defaultSortable = false;
5295 * @event widthchange
5296 * Fires when the width of a column changes.
5297 * @param {ColumnModel} this
5298 * @param {Number} columnIndex The column index
5299 * @param {Number} newWidth The new width
5301 "widthchange": true,
5303 * @event headerchange
5304 * Fires when the text of a header changes.
5305 * @param {ColumnModel} this
5306 * @param {Number} columnIndex The column index
5307 * @param {Number} newText The new header text
5309 "headerchange": true,
5311 * @event hiddenchange
5312 * Fires when a column is hidden or "unhidden".
5313 * @param {ColumnModel} this
5314 * @param {Number} columnIndex The column index
5315 * @param {Boolean} hidden true if hidden, false otherwise
5317 "hiddenchange": true,
5319 * @event columnmoved
5320 * Fires when a column is moved.
5321 * @param {ColumnModel} this
5322 * @param {Number} oldIndex
5323 * @param {Number} newIndex
5325 "columnmoved" : true,
5327 * @event columlockchange
5328 * Fires when a column's locked state is changed
5329 * @param {ColumnModel} this
5330 * @param {Number} colIndex
5331 * @param {Boolean} locked true if locked
5333 "columnlockchange" : true
5335 Roo.grid.ColumnModel.superclass.constructor.call(this);
5337 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5339 * @cfg {String} header The header text to display in the Grid view.
5342 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5343 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5344 * specified, the column's index is used as an index into the Record's data Array.
5347 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5348 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5351 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5352 * Defaults to the value of the {@link #defaultSortable} property.
5353 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5356 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5359 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5362 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5365 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5368 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5369 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5370 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5371 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5374 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5377 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5380 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5383 * @cfg {String} cursor (Optional)
5386 * @cfg {String} tooltip (Optional)
5389 * @cfg {Number} xs (Optional)
5392 * @cfg {Number} sm (Optional)
5395 * @cfg {Number} md (Optional)
5398 * @cfg {Number} lg (Optional)
5401 * Returns the id of the column at the specified index.
5402 * @param {Number} index The column index
5403 * @return {String} the id
5405 getColumnId : function(index){
5406 return this.config[index].id;
5410 * Returns the column for a specified id.
5411 * @param {String} id The column id
5412 * @return {Object} the column
5414 getColumnById : function(id){
5415 return this.lookup[id];
5420 * Returns the column for a specified dataIndex.
5421 * @param {String} dataIndex The column dataIndex
5422 * @return {Object|Boolean} the column or false if not found
5424 getColumnByDataIndex: function(dataIndex){
5425 var index = this.findColumnIndex(dataIndex);
5426 return index > -1 ? this.config[index] : false;
5430 * Returns the index for a specified column id.
5431 * @param {String} id The column id
5432 * @return {Number} the index, or -1 if not found
5434 getIndexById : function(id){
5435 for(var i = 0, len = this.config.length; i < len; i++){
5436 if(this.config[i].id == id){
5444 * Returns the index for a specified column dataIndex.
5445 * @param {String} dataIndex The column dataIndex
5446 * @return {Number} the index, or -1 if not found
5449 findColumnIndex : function(dataIndex){
5450 for(var i = 0, len = this.config.length; i < len; i++){
5451 if(this.config[i].dataIndex == dataIndex){
5459 moveColumn : function(oldIndex, newIndex){
5460 var c = this.config[oldIndex];
5461 this.config.splice(oldIndex, 1);
5462 this.config.splice(newIndex, 0, c);
5463 this.dataMap = null;
5464 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5467 isLocked : function(colIndex){
5468 return this.config[colIndex].locked === true;
5471 setLocked : function(colIndex, value, suppressEvent){
5472 if(this.isLocked(colIndex) == value){
5475 this.config[colIndex].locked = value;
5477 this.fireEvent("columnlockchange", this, colIndex, value);
5481 getTotalLockedWidth : function(){
5483 for(var i = 0; i < this.config.length; i++){
5484 if(this.isLocked(i) && !this.isHidden(i)){
5485 this.totalWidth += this.getColumnWidth(i);
5491 getLockedCount : function(){
5492 for(var i = 0, len = this.config.length; i < len; i++){
5493 if(!this.isLocked(i)){
5498 return this.config.length;
5502 * Returns the number of columns.
5505 getColumnCount : function(visibleOnly){
5506 if(visibleOnly === true){
5508 for(var i = 0, len = this.config.length; i < len; i++){
5509 if(!this.isHidden(i)){
5515 return this.config.length;
5519 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5520 * @param {Function} fn
5521 * @param {Object} scope (optional)
5522 * @return {Array} result
5524 getColumnsBy : function(fn, scope){
5526 for(var i = 0, len = this.config.length; i < len; i++){
5527 var c = this.config[i];
5528 if(fn.call(scope||this, c, i) === true){
5536 * Returns true if the specified column is sortable.
5537 * @param {Number} col The column index
5540 isSortable : function(col){
5541 if(typeof this.config[col].sortable == "undefined"){
5542 return this.defaultSortable;
5544 return this.config[col].sortable;
5548 * Returns the rendering (formatting) function defined for the column.
5549 * @param {Number} col The column index.
5550 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5552 getRenderer : function(col){
5553 if(!this.config[col].renderer){
5554 return Roo.grid.ColumnModel.defaultRenderer;
5556 return this.config[col].renderer;
5560 * Sets the rendering (formatting) function for a column.
5561 * @param {Number} col The column index
5562 * @param {Function} fn The function to use to process the cell's raw data
5563 * to return HTML markup for the grid view. The render function is called with
5564 * the following parameters:<ul>
5565 * <li>Data value.</li>
5566 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5567 * <li>css A CSS style string to apply to the table cell.</li>
5568 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5569 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5570 * <li>Row index</li>
5571 * <li>Column index</li>
5572 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5574 setRenderer : function(col, fn){
5575 this.config[col].renderer = fn;
5579 * Returns the width for the specified column.
5580 * @param {Number} col The column index
5583 getColumnWidth : function(col){
5584 return this.config[col].width * 1 || this.defaultWidth;
5588 * Sets the width for a column.
5589 * @param {Number} col The column index
5590 * @param {Number} width The new width
5592 setColumnWidth : function(col, width, suppressEvent){
5593 this.config[col].width = width;
5594 this.totalWidth = null;
5596 this.fireEvent("widthchange", this, col, width);
5601 * Returns the total width of all columns.
5602 * @param {Boolean} includeHidden True to include hidden column widths
5605 getTotalWidth : function(includeHidden){
5606 if(!this.totalWidth){
5607 this.totalWidth = 0;
5608 for(var i = 0, len = this.config.length; i < len; i++){
5609 if(includeHidden || !this.isHidden(i)){
5610 this.totalWidth += this.getColumnWidth(i);
5614 return this.totalWidth;
5618 * Returns the header for the specified column.
5619 * @param {Number} col The column index
5622 getColumnHeader : function(col){
5623 return this.config[col].header;
5627 * Sets the header for a column.
5628 * @param {Number} col The column index
5629 * @param {String} header The new header
5631 setColumnHeader : function(col, header){
5632 this.config[col].header = header;
5633 this.fireEvent("headerchange", this, col, header);
5637 * Returns the tooltip for the specified column.
5638 * @param {Number} col The column index
5641 getColumnTooltip : function(col){
5642 return this.config[col].tooltip;
5645 * Sets the tooltip for a column.
5646 * @param {Number} col The column index
5647 * @param {String} tooltip The new tooltip
5649 setColumnTooltip : function(col, tooltip){
5650 this.config[col].tooltip = tooltip;
5654 * Returns the dataIndex for the specified column.
5655 * @param {Number} col The column index
5658 getDataIndex : function(col){
5659 return this.config[col].dataIndex;
5663 * Sets the dataIndex for a column.
5664 * @param {Number} col The column index
5665 * @param {Number} dataIndex The new dataIndex
5667 setDataIndex : function(col, dataIndex){
5668 this.config[col].dataIndex = dataIndex;
5674 * Returns true if the cell is editable.
5675 * @param {Number} colIndex The column index
5676 * @param {Number} rowIndex The row index - this is nto actually used..?
5679 isCellEditable : function(colIndex, rowIndex){
5680 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5684 * Returns the editor defined for the cell/column.
5685 * return false or null to disable editing.
5686 * @param {Number} colIndex The column index
5687 * @param {Number} rowIndex The row index
5690 getCellEditor : function(colIndex, rowIndex){
5691 return this.config[colIndex].editor;
5695 * Sets if a column is editable.
5696 * @param {Number} col The column index
5697 * @param {Boolean} editable True if the column is editable
5699 setEditable : function(col, editable){
5700 this.config[col].editable = editable;
5705 * Returns true if the column is hidden.
5706 * @param {Number} colIndex The column index
5709 isHidden : function(colIndex){
5710 return this.config[colIndex].hidden;
5715 * Returns true if the column width cannot be changed
5717 isFixed : function(colIndex){
5718 return this.config[colIndex].fixed;
5722 * Returns true if the column can be resized
5725 isResizable : function(colIndex){
5726 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5729 * Sets if a column is hidden.
5730 * @param {Number} colIndex The column index
5731 * @param {Boolean} hidden True if the column is hidden
5733 setHidden : function(colIndex, hidden){
5734 this.config[colIndex].hidden = hidden;
5735 this.totalWidth = null;
5736 this.fireEvent("hiddenchange", this, colIndex, hidden);
5740 * Sets the editor for a column.
5741 * @param {Number} col The column index
5742 * @param {Object} editor The editor object
5744 setEditor : function(col, editor){
5745 this.config[col].editor = editor;
5749 Roo.grid.ColumnModel.defaultRenderer = function(value)
5751 if(typeof value == "object") {
5754 if(typeof value == "string" && value.length < 1){
5758 return String.format("{0}", value);
5761 // Alias for backwards compatibility
5762 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5765 * Ext JS Library 1.1.1
5766 * Copyright(c) 2006-2007, Ext JS, LLC.
5768 * Originally Released Under LGPL - original licence link has changed is not relivant.
5771 * <script type="text/javascript">
5775 * @class Roo.LoadMask
5776 * A simple utility class for generically masking elements while loading data. If the element being masked has
5777 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5778 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5779 * element's UpdateManager load indicator and will be destroyed after the initial load.
5781 * Create a new LoadMask
5782 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5783 * @param {Object} config The config object
5785 Roo.LoadMask = function(el, config){
5786 this.el = Roo.get(el);
5787 Roo.apply(this, config);
5789 this.store.on('beforeload', this.onBeforeLoad, this);
5790 this.store.on('load', this.onLoad, this);
5791 this.store.on('loadexception', this.onLoadException, this);
5792 this.removeMask = false;
5794 var um = this.el.getUpdateManager();
5795 um.showLoadIndicator = false; // disable the default indicator
5796 um.on('beforeupdate', this.onBeforeLoad, this);
5797 um.on('update', this.onLoad, this);
5798 um.on('failure', this.onLoad, this);
5799 this.removeMask = true;
5803 Roo.LoadMask.prototype = {
5805 * @cfg {Boolean} removeMask
5806 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5807 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5811 * The text to display in a centered loading message box (defaults to 'Loading...')
5815 * @cfg {String} msgCls
5816 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5818 msgCls : 'x-mask-loading',
5821 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5827 * Disables the mask to prevent it from being displayed
5829 disable : function(){
5830 this.disabled = true;
5834 * Enables the mask so that it can be displayed
5836 enable : function(){
5837 this.disabled = false;
5840 onLoadException : function()
5844 if (typeof(arguments[3]) != 'undefined') {
5845 Roo.MessageBox.alert("Error loading",arguments[3]);
5849 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5850 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5857 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5862 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5866 onBeforeLoad : function(){
5868 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5873 destroy : function(){
5875 this.store.un('beforeload', this.onBeforeLoad, this);
5876 this.store.un('load', this.onLoad, this);
5877 this.store.un('loadexception', this.onLoadException, this);
5879 var um = this.el.getUpdateManager();
5880 um.un('beforeupdate', this.onBeforeLoad, this);
5881 um.un('update', this.onLoad, this);
5882 um.un('failure', this.onLoad, this);
5893 * @class Roo.bootstrap.Table
5894 * @extends Roo.bootstrap.Component
5895 * Bootstrap Table class
5896 * @cfg {String} cls table class
5897 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5898 * @cfg {String} bgcolor Specifies the background color for a table
5899 * @cfg {Number} border Specifies whether the table cells should have borders or not
5900 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5901 * @cfg {Number} cellspacing Specifies the space between cells
5902 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5903 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5904 * @cfg {String} sortable Specifies that the table should be sortable
5905 * @cfg {String} summary Specifies a summary of the content of a table
5906 * @cfg {Number} width Specifies the width of a table
5907 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5909 * @cfg {boolean} striped Should the rows be alternative striped
5910 * @cfg {boolean} bordered Add borders to the table
5911 * @cfg {boolean} hover Add hover highlighting
5912 * @cfg {boolean} condensed Format condensed
5913 * @cfg {boolean} responsive Format condensed
5914 * @cfg {Boolean} loadMask (true|false) default false
5915 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5916 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5917 * @cfg {Boolean} rowSelection (true|false) default false
5918 * @cfg {Boolean} cellSelection (true|false) default false
5919 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5920 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5921 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5922 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5926 * Create a new Table
5927 * @param {Object} config The config object
5930 Roo.bootstrap.Table = function(config){
5931 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5936 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5937 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5938 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5939 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5941 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5943 this.sm.grid = this;
5944 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5945 this.sm = this.selModel;
5946 this.sm.xmodule = this.xmodule || false;
5949 if (this.cm && typeof(this.cm.config) == 'undefined') {
5950 this.colModel = new Roo.grid.ColumnModel(this.cm);
5951 this.cm = this.colModel;
5952 this.cm.xmodule = this.xmodule || false;
5955 this.store= Roo.factory(this.store, Roo.data);
5956 this.ds = this.store;
5957 this.ds.xmodule = this.xmodule || false;
5960 if (this.footer && this.store) {
5961 this.footer.dataSource = this.ds;
5962 this.footer = Roo.factory(this.footer);
5969 * Fires when a cell is clicked
5970 * @param {Roo.bootstrap.Table} this
5971 * @param {Roo.Element} el
5972 * @param {Number} rowIndex
5973 * @param {Number} columnIndex
5974 * @param {Roo.EventObject} e
5978 * @event celldblclick
5979 * Fires when a cell is double clicked
5980 * @param {Roo.bootstrap.Table} this
5981 * @param {Roo.Element} el
5982 * @param {Number} rowIndex
5983 * @param {Number} columnIndex
5984 * @param {Roo.EventObject} e
5986 "celldblclick" : true,
5989 * Fires when a row is clicked
5990 * @param {Roo.bootstrap.Table} this
5991 * @param {Roo.Element} el
5992 * @param {Number} rowIndex
5993 * @param {Roo.EventObject} e
5997 * @event rowdblclick
5998 * Fires when a row is double clicked
5999 * @param {Roo.bootstrap.Table} this
6000 * @param {Roo.Element} el
6001 * @param {Number} rowIndex
6002 * @param {Roo.EventObject} e
6004 "rowdblclick" : true,
6007 * Fires when a mouseover occur
6008 * @param {Roo.bootstrap.Table} this
6009 * @param {Roo.Element} el
6010 * @param {Number} rowIndex
6011 * @param {Number} columnIndex
6012 * @param {Roo.EventObject} e
6017 * Fires when a mouseout occur
6018 * @param {Roo.bootstrap.Table} this
6019 * @param {Roo.Element} el
6020 * @param {Number} rowIndex
6021 * @param {Number} columnIndex
6022 * @param {Roo.EventObject} e
6027 * Fires when a row is rendered, so you can change add a style to it.
6028 * @param {Roo.bootstrap.Table} this
6029 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6033 * @event rowsrendered
6034 * Fires when all the rows have been rendered
6035 * @param {Roo.bootstrap.Table} this
6037 'rowsrendered' : true,
6039 * @event contextmenu
6040 * The raw contextmenu event for the entire grid.
6041 * @param {Roo.EventObject} e
6043 "contextmenu" : true,
6045 * @event rowcontextmenu
6046 * Fires when a row is right clicked
6047 * @param {Roo.bootstrap.Table} this
6048 * @param {Number} rowIndex
6049 * @param {Roo.EventObject} e
6051 "rowcontextmenu" : true,
6053 * @event cellcontextmenu
6054 * Fires when a cell is right clicked
6055 * @param {Roo.bootstrap.Table} this
6056 * @param {Number} rowIndex
6057 * @param {Number} cellIndex
6058 * @param {Roo.EventObject} e
6060 "cellcontextmenu" : true,
6062 * @event headercontextmenu
6063 * Fires when a header is right clicked
6064 * @param {Roo.bootstrap.Table} this
6065 * @param {Number} columnIndex
6066 * @param {Roo.EventObject} e
6068 "headercontextmenu" : true
6072 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6098 rowSelection : false,
6099 cellSelection : false,
6102 // Roo.Element - the tbody
6104 // Roo.Element - thead element
6107 container: false, // used by gridpanel...
6113 auto_hide_footer : false,
6115 getAutoCreate : function()
6117 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6124 if (this.scrollBody) {
6125 cfg.cls += ' table-body-fixed';
6128 cfg.cls += ' table-striped';
6132 cfg.cls += ' table-hover';
6134 if (this.bordered) {
6135 cfg.cls += ' table-bordered';
6137 if (this.condensed) {
6138 cfg.cls += ' table-condensed';
6140 if (this.responsive) {
6141 cfg.cls += ' table-responsive';
6145 cfg.cls+= ' ' +this.cls;
6148 // this lot should be simplifed...
6161 ].forEach(function(k) {
6169 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6172 if(this.store || this.cm){
6173 if(this.headerShow){
6174 cfg.cn.push(this.renderHeader());
6177 cfg.cn.push(this.renderBody());
6179 if(this.footerShow){
6180 cfg.cn.push(this.renderFooter());
6182 // where does this come from?
6183 //cfg.cls+= ' TableGrid';
6186 return { cn : [ cfg ] };
6189 initEvents : function()
6191 if(!this.store || !this.cm){
6194 if (this.selModel) {
6195 this.selModel.initEvents();
6199 //Roo.log('initEvents with ds!!!!');
6201 this.mainBody = this.el.select('tbody', true).first();
6202 this.mainHead = this.el.select('thead', true).first();
6203 this.mainFoot = this.el.select('tfoot', true).first();
6209 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6210 e.on('click', _this.sort, _this);
6213 this.mainBody.on("click", this.onClick, this);
6214 this.mainBody.on("dblclick", this.onDblClick, this);
6216 // why is this done????? = it breaks dialogs??
6217 //this.parent().el.setStyle('position', 'relative');
6221 this.footer.parentId = this.id;
6222 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6225 this.el.select('tfoot tr td').first().addClass('hide');
6230 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6233 this.store.on('load', this.onLoad, this);
6234 this.store.on('beforeload', this.onBeforeLoad, this);
6235 this.store.on('update', this.onUpdate, this);
6236 this.store.on('add', this.onAdd, this);
6237 this.store.on("clear", this.clear, this);
6239 this.el.on("contextmenu", this.onContextMenu, this);
6241 this.mainBody.on('scroll', this.onBodyScroll, this);
6243 this.cm.on("headerchange", this.onHeaderChange, this);
6245 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6249 onContextMenu : function(e, t)
6251 this.processEvent("contextmenu", e);
6254 processEvent : function(name, e)
6256 if (name != 'touchstart' ) {
6257 this.fireEvent(name, e);
6260 var t = e.getTarget();
6262 var cell = Roo.get(t);
6268 if(cell.findParent('tfoot', false, true)){
6272 if(cell.findParent('thead', false, true)){
6274 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6275 cell = Roo.get(t).findParent('th', false, true);
6277 Roo.log("failed to find th in thead?");
6278 Roo.log(e.getTarget());
6283 var cellIndex = cell.dom.cellIndex;
6285 var ename = name == 'touchstart' ? 'click' : name;
6286 this.fireEvent("header" + ename, this, cellIndex, e);
6291 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6292 cell = Roo.get(t).findParent('td', false, true);
6294 Roo.log("failed to find th in tbody?");
6295 Roo.log(e.getTarget());
6300 var row = cell.findParent('tr', false, true);
6301 var cellIndex = cell.dom.cellIndex;
6302 var rowIndex = row.dom.rowIndex - 1;
6306 this.fireEvent("row" + name, this, rowIndex, e);
6310 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6316 onMouseover : function(e, el)
6318 var cell = Roo.get(el);
6324 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6325 cell = cell.findParent('td', false, true);
6328 var row = cell.findParent('tr', false, true);
6329 var cellIndex = cell.dom.cellIndex;
6330 var rowIndex = row.dom.rowIndex - 1; // start from 0
6332 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6336 onMouseout : function(e, el)
6338 var cell = Roo.get(el);
6344 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6345 cell = cell.findParent('td', false, true);
6348 var row = cell.findParent('tr', false, true);
6349 var cellIndex = cell.dom.cellIndex;
6350 var rowIndex = row.dom.rowIndex - 1; // start from 0
6352 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6356 onClick : function(e, el)
6358 var cell = Roo.get(el);
6360 if(!cell || (!this.cellSelection && !this.rowSelection)){
6364 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6365 cell = cell.findParent('td', false, true);
6368 if(!cell || typeof(cell) == 'undefined'){
6372 var row = cell.findParent('tr', false, true);
6374 if(!row || typeof(row) == 'undefined'){
6378 var cellIndex = cell.dom.cellIndex;
6379 var rowIndex = this.getRowIndex(row);
6381 // why??? - should these not be based on SelectionModel?
6382 if(this.cellSelection){
6383 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6386 if(this.rowSelection){
6387 this.fireEvent('rowclick', this, row, rowIndex, e);
6393 onDblClick : function(e,el)
6395 var cell = Roo.get(el);
6397 if(!cell || (!this.cellSelection && !this.rowSelection)){
6401 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6402 cell = cell.findParent('td', false, true);
6405 if(!cell || typeof(cell) == 'undefined'){
6409 var row = cell.findParent('tr', false, true);
6411 if(!row || typeof(row) == 'undefined'){
6415 var cellIndex = cell.dom.cellIndex;
6416 var rowIndex = this.getRowIndex(row);
6418 if(this.cellSelection){
6419 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6422 if(this.rowSelection){
6423 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6427 sort : function(e,el)
6429 var col = Roo.get(el);
6431 if(!col.hasClass('sortable')){
6435 var sort = col.attr('sort');
6438 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6442 this.store.sortInfo = {field : sort, direction : dir};
6445 Roo.log("calling footer first");
6446 this.footer.onClick('first');
6449 this.store.load({ params : { start : 0 } });
6453 renderHeader : function()
6461 this.totalWidth = 0;
6463 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6465 var config = cm.config[i];
6469 cls : 'x-hcol-' + i,
6471 html: cm.getColumnHeader(i)
6476 if(typeof(config.sortable) != 'undefined' && config.sortable){
6478 c.html = '<i class="glyphicon"></i>' + c.html;
6481 if(typeof(config.lgHeader) != 'undefined'){
6482 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6485 if(typeof(config.mdHeader) != 'undefined'){
6486 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6489 if(typeof(config.smHeader) != 'undefined'){
6490 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6493 if(typeof(config.xsHeader) != 'undefined'){
6494 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6501 if(typeof(config.tooltip) != 'undefined'){
6502 c.tooltip = config.tooltip;
6505 if(typeof(config.colspan) != 'undefined'){
6506 c.colspan = config.colspan;
6509 if(typeof(config.hidden) != 'undefined' && config.hidden){
6510 c.style += ' display:none;';
6513 if(typeof(config.dataIndex) != 'undefined'){
6514 c.sort = config.dataIndex;
6519 if(typeof(config.align) != 'undefined' && config.align.length){
6520 c.style += ' text-align:' + config.align + ';';
6523 if(typeof(config.width) != 'undefined'){
6524 c.style += ' width:' + config.width + 'px;';
6525 this.totalWidth += config.width;
6527 this.totalWidth += 100; // assume minimum of 100 per column?
6530 if(typeof(config.cls) != 'undefined'){
6531 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6534 ['xs','sm','md','lg'].map(function(size){
6536 if(typeof(config[size]) == 'undefined'){
6540 if (!config[size]) { // 0 = hidden
6541 c.cls += ' hidden-' + size;
6545 c.cls += ' col-' + size + '-' + config[size];
6555 renderBody : function()
6565 colspan : this.cm.getColumnCount()
6575 renderFooter : function()
6585 colspan : this.cm.getColumnCount()
6599 // Roo.log('ds onload');
6604 var ds = this.store;
6606 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6607 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6608 if (_this.store.sortInfo) {
6610 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6611 e.select('i', true).addClass(['glyphicon-arrow-up']);
6614 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6615 e.select('i', true).addClass(['glyphicon-arrow-down']);
6620 var tbody = this.mainBody;
6622 if(ds.getCount() > 0){
6623 ds.data.each(function(d,rowIndex){
6624 var row = this.renderRow(cm, ds, rowIndex);
6626 tbody.createChild(row);
6630 if(row.cellObjects.length){
6631 Roo.each(row.cellObjects, function(r){
6632 _this.renderCellObject(r);
6639 var tfoot = this.el.select('tfoot', true).first();
6641 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6643 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6645 var total = this.ds.getTotalCount();
6647 if(this.footer.pageSize < total){
6648 this.mainFoot.show();
6652 Roo.each(this.el.select('tbody td', true).elements, function(e){
6653 e.on('mouseover', _this.onMouseover, _this);
6656 Roo.each(this.el.select('tbody td', true).elements, function(e){
6657 e.on('mouseout', _this.onMouseout, _this);
6659 this.fireEvent('rowsrendered', this);
6665 onUpdate : function(ds,record)
6667 this.refreshRow(record);
6671 onRemove : function(ds, record, index, isUpdate){
6672 if(isUpdate !== true){
6673 this.fireEvent("beforerowremoved", this, index, record);
6675 var bt = this.mainBody.dom;
6677 var rows = this.el.select('tbody > tr', true).elements;
6679 if(typeof(rows[index]) != 'undefined'){
6680 bt.removeChild(rows[index].dom);
6683 // if(bt.rows[index]){
6684 // bt.removeChild(bt.rows[index]);
6687 if(isUpdate !== true){
6688 //this.stripeRows(index);
6689 //this.syncRowHeights(index, index);
6691 this.fireEvent("rowremoved", this, index, record);
6695 onAdd : function(ds, records, rowIndex)
6697 //Roo.log('on Add called');
6698 // - note this does not handle multiple adding very well..
6699 var bt = this.mainBody.dom;
6700 for (var i =0 ; i < records.length;i++) {
6701 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6702 //Roo.log(records[i]);
6703 //Roo.log(this.store.getAt(rowIndex+i));
6704 this.insertRow(this.store, rowIndex + i, false);
6711 refreshRow : function(record){
6712 var ds = this.store, index;
6713 if(typeof record == 'number'){
6715 record = ds.getAt(index);
6717 index = ds.indexOf(record);
6719 this.insertRow(ds, index, true);
6721 this.onRemove(ds, record, index+1, true);
6723 //this.syncRowHeights(index, index);
6725 this.fireEvent("rowupdated", this, index, record);
6728 insertRow : function(dm, rowIndex, isUpdate){
6731 this.fireEvent("beforerowsinserted", this, rowIndex);
6733 //var s = this.getScrollState();
6734 var row = this.renderRow(this.cm, this.store, rowIndex);
6735 // insert before rowIndex..
6736 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6740 if(row.cellObjects.length){
6741 Roo.each(row.cellObjects, function(r){
6742 _this.renderCellObject(r);
6747 this.fireEvent("rowsinserted", this, rowIndex);
6748 //this.syncRowHeights(firstRow, lastRow);
6749 //this.stripeRows(firstRow);
6756 getRowDom : function(rowIndex)
6758 var rows = this.el.select('tbody > tr', true).elements;
6760 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6763 // returns the object tree for a tr..
6766 renderRow : function(cm, ds, rowIndex)
6768 var d = ds.getAt(rowIndex);
6772 cls : 'x-row-' + rowIndex,
6776 var cellObjects = [];
6778 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6779 var config = cm.config[i];
6781 var renderer = cm.getRenderer(i);
6785 if(typeof(renderer) !== 'undefined'){
6786 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6788 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6789 // and are rendered into the cells after the row is rendered - using the id for the element.
6791 if(typeof(value) === 'object'){
6801 rowIndex : rowIndex,
6806 this.fireEvent('rowclass', this, rowcfg);
6810 cls : rowcfg.rowClass + ' x-col-' + i,
6812 html: (typeof(value) === 'object') ? '' : value
6819 if(typeof(config.colspan) != 'undefined'){
6820 td.colspan = config.colspan;
6823 if(typeof(config.hidden) != 'undefined' && config.hidden){
6824 td.style += ' display:none;';
6827 if(typeof(config.align) != 'undefined' && config.align.length){
6828 td.style += ' text-align:' + config.align + ';';
6830 if(typeof(config.valign) != 'undefined' && config.valign.length){
6831 td.style += ' vertical-align:' + config.valign + ';';
6834 if(typeof(config.width) != 'undefined'){
6835 td.style += ' width:' + config.width + 'px;';
6838 if(typeof(config.cursor) != 'undefined'){
6839 td.style += ' cursor:' + config.cursor + ';';
6842 if(typeof(config.cls) != 'undefined'){
6843 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6846 ['xs','sm','md','lg'].map(function(size){
6848 if(typeof(config[size]) == 'undefined'){
6852 if (!config[size]) { // 0 = hidden
6853 td.cls += ' hidden-' + size;
6857 td.cls += ' col-' + size + '-' + config[size];
6865 row.cellObjects = cellObjects;
6873 onBeforeLoad : function()
6882 this.el.select('tbody', true).first().dom.innerHTML = '';
6885 * Show or hide a row.
6886 * @param {Number} rowIndex to show or hide
6887 * @param {Boolean} state hide
6889 setRowVisibility : function(rowIndex, state)
6891 var bt = this.mainBody.dom;
6893 var rows = this.el.select('tbody > tr', true).elements;
6895 if(typeof(rows[rowIndex]) == 'undefined'){
6898 rows[rowIndex].dom.style.display = state ? '' : 'none';
6902 getSelectionModel : function(){
6904 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6906 return this.selModel;
6909 * Render the Roo.bootstrap object from renderder
6911 renderCellObject : function(r)
6915 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6917 var t = r.cfg.render(r.container);
6920 Roo.each(r.cfg.cn, function(c){
6922 container: t.getChildContainer(),
6925 _this.renderCellObject(child);
6930 getRowIndex : function(row)
6934 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6945 * Returns the grid's underlying element = used by panel.Grid
6946 * @return {Element} The element
6948 getGridEl : function(){
6952 * Forces a resize - used by panel.Grid
6953 * @return {Element} The element
6955 autoSize : function()
6957 //var ctr = Roo.get(this.container.dom.parentElement);
6958 var ctr = Roo.get(this.el.dom);
6960 var thd = this.getGridEl().select('thead',true).first();
6961 var tbd = this.getGridEl().select('tbody', true).first();
6962 var tfd = this.getGridEl().select('tfoot', true).first();
6964 var cw = ctr.getWidth();
6968 tbd.setSize(ctr.getWidth(),
6969 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6971 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6974 cw = Math.max(cw, this.totalWidth);
6975 this.getGridEl().select('tr',true).setWidth(cw);
6976 // resize 'expandable coloumn?
6978 return; // we doe not have a view in this design..
6981 onBodyScroll: function()
6983 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6985 this.mainHead.setStyle({
6986 'position' : 'relative',
6987 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6993 var scrollHeight = this.mainBody.dom.scrollHeight;
6995 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6997 var height = this.mainBody.getHeight();
6999 if(scrollHeight - height == scrollTop) {
7001 var total = this.ds.getTotalCount();
7003 if(this.footer.cursor + this.footer.pageSize < total){
7005 this.footer.ds.load({
7007 start : this.footer.cursor + this.footer.pageSize,
7008 limit : this.footer.pageSize
7018 onHeaderChange : function()
7020 var header = this.renderHeader();
7021 var table = this.el.select('table', true).first();
7023 this.mainHead.remove();
7024 this.mainHead = table.createChild(header, this.mainBody, false);
7027 onHiddenChange : function(colModel, colIndex, hidden)
7029 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7030 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7032 this.CSS.updateRule(thSelector, "display", "");
7033 this.CSS.updateRule(tdSelector, "display", "");
7036 this.CSS.updateRule(thSelector, "display", "none");
7037 this.CSS.updateRule(tdSelector, "display", "none");
7040 this.onHeaderChange();
7057 * @class Roo.bootstrap.TableCell
7058 * @extends Roo.bootstrap.Component
7059 * Bootstrap TableCell class
7060 * @cfg {String} html cell contain text
7061 * @cfg {String} cls cell class
7062 * @cfg {String} tag cell tag (td|th) default td
7063 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7064 * @cfg {String} align Aligns the content in a cell
7065 * @cfg {String} axis Categorizes cells
7066 * @cfg {String} bgcolor Specifies the background color of a cell
7067 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7068 * @cfg {Number} colspan Specifies the number of columns a cell should span
7069 * @cfg {String} headers Specifies one or more header cells a cell is related to
7070 * @cfg {Number} height Sets the height of a cell
7071 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7072 * @cfg {Number} rowspan Sets the number of rows a cell should span
7073 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7074 * @cfg {String} valign Vertical aligns the content in a cell
7075 * @cfg {Number} width Specifies the width of a cell
7078 * Create a new TableCell
7079 * @param {Object} config The config object
7082 Roo.bootstrap.TableCell = function(config){
7083 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7086 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7106 getAutoCreate : function(){
7107 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7127 cfg.align=this.align
7133 cfg.bgcolor=this.bgcolor
7136 cfg.charoff=this.charoff
7139 cfg.colspan=this.colspan
7142 cfg.headers=this.headers
7145 cfg.height=this.height
7148 cfg.nowrap=this.nowrap
7151 cfg.rowspan=this.rowspan
7154 cfg.scope=this.scope
7157 cfg.valign=this.valign
7160 cfg.width=this.width
7179 * @class Roo.bootstrap.TableRow
7180 * @extends Roo.bootstrap.Component
7181 * Bootstrap TableRow class
7182 * @cfg {String} cls row class
7183 * @cfg {String} align Aligns the content in a table row
7184 * @cfg {String} bgcolor Specifies a background color for a table row
7185 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7186 * @cfg {String} valign Vertical aligns the content in a table row
7189 * Create a new TableRow
7190 * @param {Object} config The config object
7193 Roo.bootstrap.TableRow = function(config){
7194 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7197 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7205 getAutoCreate : function(){
7206 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7216 cfg.align = this.align;
7219 cfg.bgcolor = this.bgcolor;
7222 cfg.charoff = this.charoff;
7225 cfg.valign = this.valign;
7243 * @class Roo.bootstrap.TableBody
7244 * @extends Roo.bootstrap.Component
7245 * Bootstrap TableBody class
7246 * @cfg {String} cls element class
7247 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7248 * @cfg {String} align Aligns the content inside the element
7249 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7250 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7253 * Create a new TableBody
7254 * @param {Object} config The config object
7257 Roo.bootstrap.TableBody = function(config){
7258 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7261 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7269 getAutoCreate : function(){
7270 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7284 cfg.align = this.align;
7287 cfg.charoff = this.charoff;
7290 cfg.valign = this.valign;
7297 // initEvents : function()
7304 // this.store = Roo.factory(this.store, Roo.data);
7305 // this.store.on('load', this.onLoad, this);
7307 // this.store.load();
7311 // onLoad: function ()
7313 // this.fireEvent('load', this);
7323 * Ext JS Library 1.1.1
7324 * Copyright(c) 2006-2007, Ext JS, LLC.
7326 * Originally Released Under LGPL - original licence link has changed is not relivant.
7329 * <script type="text/javascript">
7332 // as we use this in bootstrap.
7333 Roo.namespace('Roo.form');
7335 * @class Roo.form.Action
7336 * Internal Class used to handle form actions
7338 * @param {Roo.form.BasicForm} el The form element or its id
7339 * @param {Object} config Configuration options
7344 // define the action interface
7345 Roo.form.Action = function(form, options){
7347 this.options = options || {};
7350 * Client Validation Failed
7353 Roo.form.Action.CLIENT_INVALID = 'client';
7355 * Server Validation Failed
7358 Roo.form.Action.SERVER_INVALID = 'server';
7360 * Connect to Server Failed
7363 Roo.form.Action.CONNECT_FAILURE = 'connect';
7365 * Reading Data from Server Failed
7368 Roo.form.Action.LOAD_FAILURE = 'load';
7370 Roo.form.Action.prototype = {
7372 failureType : undefined,
7373 response : undefined,
7377 run : function(options){
7382 success : function(response){
7387 handleResponse : function(response){
7391 // default connection failure
7392 failure : function(response){
7394 this.response = response;
7395 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7396 this.form.afterAction(this, false);
7399 processResponse : function(response){
7400 this.response = response;
7401 if(!response.responseText){
7404 this.result = this.handleResponse(response);
7408 // utility functions used internally
7409 getUrl : function(appendParams){
7410 var url = this.options.url || this.form.url || this.form.el.dom.action;
7412 var p = this.getParams();
7414 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7420 getMethod : function(){
7421 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7424 getParams : function(){
7425 var bp = this.form.baseParams;
7426 var p = this.options.params;
7428 if(typeof p == "object"){
7429 p = Roo.urlEncode(Roo.applyIf(p, bp));
7430 }else if(typeof p == 'string' && bp){
7431 p += '&' + Roo.urlEncode(bp);
7434 p = Roo.urlEncode(bp);
7439 createCallback : function(){
7441 success: this.success,
7442 failure: this.failure,
7444 timeout: (this.form.timeout*1000),
7445 upload: this.form.fileUpload ? this.success : undefined
7450 Roo.form.Action.Submit = function(form, options){
7451 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7454 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7457 haveProgress : false,
7458 uploadComplete : false,
7460 // uploadProgress indicator.
7461 uploadProgress : function()
7463 if (!this.form.progressUrl) {
7467 if (!this.haveProgress) {
7468 Roo.MessageBox.progress("Uploading", "Uploading");
7470 if (this.uploadComplete) {
7471 Roo.MessageBox.hide();
7475 this.haveProgress = true;
7477 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7479 var c = new Roo.data.Connection();
7481 url : this.form.progressUrl,
7486 success : function(req){
7487 //console.log(data);
7491 rdata = Roo.decode(req.responseText)
7493 Roo.log("Invalid data from server..");
7497 if (!rdata || !rdata.success) {
7499 Roo.MessageBox.alert(Roo.encode(rdata));
7502 var data = rdata.data;
7504 if (this.uploadComplete) {
7505 Roo.MessageBox.hide();
7510 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7511 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7514 this.uploadProgress.defer(2000,this);
7517 failure: function(data) {
7518 Roo.log('progress url failed ');
7529 // run get Values on the form, so it syncs any secondary forms.
7530 this.form.getValues();
7532 var o = this.options;
7533 var method = this.getMethod();
7534 var isPost = method == 'POST';
7535 if(o.clientValidation === false || this.form.isValid()){
7537 if (this.form.progressUrl) {
7538 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7539 (new Date() * 1) + '' + Math.random());
7544 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7545 form:this.form.el.dom,
7546 url:this.getUrl(!isPost),
7548 params:isPost ? this.getParams() : null,
7549 isUpload: this.form.fileUpload
7552 this.uploadProgress();
7554 }else if (o.clientValidation !== false){ // client validation failed
7555 this.failureType = Roo.form.Action.CLIENT_INVALID;
7556 this.form.afterAction(this, false);
7560 success : function(response)
7562 this.uploadComplete= true;
7563 if (this.haveProgress) {
7564 Roo.MessageBox.hide();
7568 var result = this.processResponse(response);
7569 if(result === true || result.success){
7570 this.form.afterAction(this, true);
7574 this.form.markInvalid(result.errors);
7575 this.failureType = Roo.form.Action.SERVER_INVALID;
7577 this.form.afterAction(this, false);
7579 failure : function(response)
7581 this.uploadComplete= true;
7582 if (this.haveProgress) {
7583 Roo.MessageBox.hide();
7586 this.response = response;
7587 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7588 this.form.afterAction(this, false);
7591 handleResponse : function(response){
7592 if(this.form.errorReader){
7593 var rs = this.form.errorReader.read(response);
7596 for(var i = 0, len = rs.records.length; i < len; i++) {
7597 var r = rs.records[i];
7601 if(errors.length < 1){
7605 success : rs.success,
7611 ret = Roo.decode(response.responseText);
7615 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7625 Roo.form.Action.Load = function(form, options){
7626 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7627 this.reader = this.form.reader;
7630 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7635 Roo.Ajax.request(Roo.apply(
7636 this.createCallback(), {
7637 method:this.getMethod(),
7638 url:this.getUrl(false),
7639 params:this.getParams()
7643 success : function(response){
7645 var result = this.processResponse(response);
7646 if(result === true || !result.success || !result.data){
7647 this.failureType = Roo.form.Action.LOAD_FAILURE;
7648 this.form.afterAction(this, false);
7651 this.form.clearInvalid();
7652 this.form.setValues(result.data);
7653 this.form.afterAction(this, true);
7656 handleResponse : function(response){
7657 if(this.form.reader){
7658 var rs = this.form.reader.read(response);
7659 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7661 success : rs.success,
7665 return Roo.decode(response.responseText);
7669 Roo.form.Action.ACTION_TYPES = {
7670 'load' : Roo.form.Action.Load,
7671 'submit' : Roo.form.Action.Submit
7680 * @class Roo.bootstrap.Form
7681 * @extends Roo.bootstrap.Component
7682 * Bootstrap Form class
7683 * @cfg {String} method GET | POST (default POST)
7684 * @cfg {String} labelAlign top | left (default top)
7685 * @cfg {String} align left | right - for navbars
7686 * @cfg {Boolean} loadMask load mask when submit (default true)
7691 * @param {Object} config The config object
7695 Roo.bootstrap.Form = function(config){
7697 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7699 Roo.bootstrap.Form.popover.apply();
7703 * @event clientvalidation
7704 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7705 * @param {Form} this
7706 * @param {Boolean} valid true if the form has passed client-side validation
7708 clientvalidation: true,
7710 * @event beforeaction
7711 * Fires before any action is performed. Return false to cancel the action.
7712 * @param {Form} this
7713 * @param {Action} action The action to be performed
7717 * @event actionfailed
7718 * Fires when an action fails.
7719 * @param {Form} this
7720 * @param {Action} action The action that failed
7722 actionfailed : true,
7724 * @event actioncomplete
7725 * Fires when an action is completed.
7726 * @param {Form} this
7727 * @param {Action} action The action that completed
7729 actioncomplete : true
7733 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7736 * @cfg {String} method
7737 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7742 * The URL to use for form actions if one isn't supplied in the action options.
7745 * @cfg {Boolean} fileUpload
7746 * Set to true if this form is a file upload.
7750 * @cfg {Object} baseParams
7751 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7755 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7759 * @cfg {Sting} align (left|right) for navbar forms
7764 activeAction : null,
7767 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7768 * element by passing it or its id or mask the form itself by passing in true.
7771 waitMsgTarget : false,
7776 * @cfg {Boolean} errorMask (true|false) default false
7781 * @cfg {Number} maskOffset Default 100
7786 * @cfg {Boolean} maskBody
7790 getAutoCreate : function(){
7794 method : this.method || 'POST',
7795 id : this.id || Roo.id(),
7798 if (this.parent().xtype.match(/^Nav/)) {
7799 cfg.cls = 'navbar-form navbar-' + this.align;
7803 if (this.labelAlign == 'left' ) {
7804 cfg.cls += ' form-horizontal';
7810 initEvents : function()
7812 this.el.on('submit', this.onSubmit, this);
7813 // this was added as random key presses on the form where triggering form submit.
7814 this.el.on('keypress', function(e) {
7815 if (e.getCharCode() != 13) {
7818 // we might need to allow it for textareas.. and some other items.
7819 // check e.getTarget().
7821 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7825 Roo.log("keypress blocked");
7833 onSubmit : function(e){
7838 * Returns true if client-side validation on the form is successful.
7841 isValid : function(){
7842 var items = this.getItems();
7846 items.each(function(f){
7852 Roo.log('invalid field: ' + f.name);
7856 if(!target && f.el.isVisible(true)){
7862 if(this.errorMask && !valid){
7863 Roo.bootstrap.Form.popover.mask(this, target);
7870 * Returns true if any fields in this form have changed since their original load.
7873 isDirty : function(){
7875 var items = this.getItems();
7876 items.each(function(f){
7886 * Performs a predefined action (submit or load) or custom actions you define on this form.
7887 * @param {String} actionName The name of the action type
7888 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7889 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7890 * accept other config options):
7892 Property Type Description
7893 ---------------- --------------- ----------------------------------------------------------------------------------
7894 url String The url for the action (defaults to the form's url)
7895 method String The form method to use (defaults to the form's method, or POST if not defined)
7896 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7897 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7898 validate the form on the client (defaults to false)
7900 * @return {BasicForm} this
7902 doAction : function(action, options){
7903 if(typeof action == 'string'){
7904 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7906 if(this.fireEvent('beforeaction', this, action) !== false){
7907 this.beforeAction(action);
7908 action.run.defer(100, action);
7914 beforeAction : function(action){
7915 var o = action.options;
7920 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7922 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7925 // not really supported yet.. ??
7927 //if(this.waitMsgTarget === true){
7928 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7929 //}else if(this.waitMsgTarget){
7930 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7931 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7933 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7939 afterAction : function(action, success){
7940 this.activeAction = null;
7941 var o = action.options;
7946 Roo.get(document.body).unmask();
7952 //if(this.waitMsgTarget === true){
7953 // this.el.unmask();
7954 //}else if(this.waitMsgTarget){
7955 // this.waitMsgTarget.unmask();
7957 // Roo.MessageBox.updateProgress(1);
7958 // Roo.MessageBox.hide();
7965 Roo.callback(o.success, o.scope, [this, action]);
7966 this.fireEvent('actioncomplete', this, action);
7970 // failure condition..
7971 // we have a scenario where updates need confirming.
7972 // eg. if a locking scenario exists..
7973 // we look for { errors : { needs_confirm : true }} in the response.
7975 (typeof(action.result) != 'undefined') &&
7976 (typeof(action.result.errors) != 'undefined') &&
7977 (typeof(action.result.errors.needs_confirm) != 'undefined')
7980 Roo.log("not supported yet");
7983 Roo.MessageBox.confirm(
7984 "Change requires confirmation",
7985 action.result.errorMsg,
7990 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
8000 Roo.callback(o.failure, o.scope, [this, action]);
8001 // show an error message if no failed handler is set..
8002 if (!this.hasListener('actionfailed')) {
8003 Roo.log("need to add dialog support");
8005 Roo.MessageBox.alert("Error",
8006 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8007 action.result.errorMsg :
8008 "Saving Failed, please check your entries or try again"
8013 this.fireEvent('actionfailed', this, action);
8018 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8019 * @param {String} id The value to search for
8022 findField : function(id){
8023 var items = this.getItems();
8024 var field = items.get(id);
8026 items.each(function(f){
8027 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8034 return field || null;
8037 * Mark fields in this form invalid in bulk.
8038 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8039 * @return {BasicForm} this
8041 markInvalid : function(errors){
8042 if(errors instanceof Array){
8043 for(var i = 0, len = errors.length; i < len; i++){
8044 var fieldError = errors[i];
8045 var f = this.findField(fieldError.id);
8047 f.markInvalid(fieldError.msg);
8053 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8054 field.markInvalid(errors[id]);
8058 //Roo.each(this.childForms || [], function (f) {
8059 // f.markInvalid(errors);
8066 * Set values for fields in this form in bulk.
8067 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8068 * @return {BasicForm} this
8070 setValues : function(values){
8071 if(values instanceof Array){ // array of objects
8072 for(var i = 0, len = values.length; i < len; i++){
8074 var f = this.findField(v.id);
8076 f.setValue(v.value);
8077 if(this.trackResetOnLoad){
8078 f.originalValue = f.getValue();
8082 }else{ // object hash
8085 if(typeof values[id] != 'function' && (field = this.findField(id))){
8087 if (field.setFromData &&
8089 field.displayField &&
8090 // combos' with local stores can
8091 // be queried via setValue()
8092 // to set their value..
8093 (field.store && !field.store.isLocal)
8097 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8098 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8099 field.setFromData(sd);
8101 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8103 field.setFromData(values);
8106 field.setValue(values[id]);
8110 if(this.trackResetOnLoad){
8111 field.originalValue = field.getValue();
8117 //Roo.each(this.childForms || [], function (f) {
8118 // f.setValues(values);
8125 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8126 * they are returned as an array.
8127 * @param {Boolean} asString
8130 getValues : function(asString){
8131 //if (this.childForms) {
8132 // copy values from the child forms
8133 // Roo.each(this.childForms, function (f) {
8134 // this.setValues(f.getValues());
8140 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8141 if(asString === true){
8144 return Roo.urlDecode(fs);
8148 * Returns the fields in this form as an object with key/value pairs.
8149 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8152 getFieldValues : function(with_hidden)
8154 var items = this.getItems();
8156 items.each(function(f){
8162 var v = f.getValue();
8164 if (f.inputType =='radio') {
8165 if (typeof(ret[f.getName()]) == 'undefined') {
8166 ret[f.getName()] = ''; // empty..
8169 if (!f.el.dom.checked) {
8177 if(f.xtype == 'MoneyField'){
8178 ret[f.currencyName] = f.getCurrency();
8181 // not sure if this supported any more..
8182 if ((typeof(v) == 'object') && f.getRawValue) {
8183 v = f.getRawValue() ; // dates..
8185 // combo boxes where name != hiddenName...
8186 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8187 ret[f.name] = f.getRawValue();
8189 ret[f.getName()] = v;
8196 * Clears all invalid messages in this form.
8197 * @return {BasicForm} this
8199 clearInvalid : function(){
8200 var items = this.getItems();
8202 items.each(function(f){
8211 * @return {BasicForm} this
8214 var items = this.getItems();
8215 items.each(function(f){
8219 Roo.each(this.childForms || [], function (f) {
8227 getItems : function()
8229 var r=new Roo.util.MixedCollection(false, function(o){
8230 return o.id || (o.id = Roo.id());
8232 var iter = function(el) {
8239 Roo.each(el.items,function(e) {
8248 hideFields : function(items)
8250 Roo.each(items, function(i){
8252 var f = this.findField(i);
8258 if(f.xtype == 'DateField'){
8259 f.setVisible(false);
8268 showFields : function(items)
8270 Roo.each(items, function(i){
8272 var f = this.findField(i);
8278 if(f.xtype == 'DateField'){
8290 Roo.apply(Roo.bootstrap.Form, {
8317 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8318 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8319 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8320 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8323 this.maskEl.top.enableDisplayMode("block");
8324 this.maskEl.left.enableDisplayMode("block");
8325 this.maskEl.bottom.enableDisplayMode("block");
8326 this.maskEl.right.enableDisplayMode("block");
8328 this.toolTip = new Roo.bootstrap.Tooltip({
8329 cls : 'roo-form-error-popover',
8331 'left' : ['r-l', [-2,0], 'right'],
8332 'right' : ['l-r', [2,0], 'left'],
8333 'bottom' : ['tl-bl', [0,2], 'top'],
8334 'top' : [ 'bl-tl', [0,-2], 'bottom']
8338 this.toolTip.render(Roo.get(document.body));
8340 this.toolTip.el.enableDisplayMode("block");
8342 Roo.get(document.body).on('click', function(){
8346 Roo.get(document.body).on('touchstart', function(){
8350 this.isApplied = true
8353 mask : function(form, target)
8357 this.target = target;
8359 if(!this.form.errorMask || !target.el){
8363 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8365 Roo.log(scrollable);
8367 var ot = this.target.el.calcOffsetsTo(scrollable);
8369 var scrollTo = ot[1] - this.form.maskOffset;
8371 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8373 scrollable.scrollTo('top', scrollTo);
8375 var box = this.target.el.getBox();
8377 var zIndex = Roo.bootstrap.Modal.zIndex++;
8380 this.maskEl.top.setStyle('position', 'absolute');
8381 this.maskEl.top.setStyle('z-index', zIndex);
8382 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8383 this.maskEl.top.setLeft(0);
8384 this.maskEl.top.setTop(0);
8385 this.maskEl.top.show();
8387 this.maskEl.left.setStyle('position', 'absolute');
8388 this.maskEl.left.setStyle('z-index', zIndex);
8389 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8390 this.maskEl.left.setLeft(0);
8391 this.maskEl.left.setTop(box.y - this.padding);
8392 this.maskEl.left.show();
8394 this.maskEl.bottom.setStyle('position', 'absolute');
8395 this.maskEl.bottom.setStyle('z-index', zIndex);
8396 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8397 this.maskEl.bottom.setLeft(0);
8398 this.maskEl.bottom.setTop(box.bottom + this.padding);
8399 this.maskEl.bottom.show();
8401 this.maskEl.right.setStyle('position', 'absolute');
8402 this.maskEl.right.setStyle('z-index', zIndex);
8403 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8404 this.maskEl.right.setLeft(box.right + this.padding);
8405 this.maskEl.right.setTop(box.y - this.padding);
8406 this.maskEl.right.show();
8408 this.toolTip.bindEl = this.target.el;
8410 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8412 var tip = this.target.blankText;
8414 if(this.target.getValue() !== '' ) {
8416 if (this.target.invalidText.length) {
8417 tip = this.target.invalidText;
8418 } else if (this.target.regexText.length){
8419 tip = this.target.regexText;
8423 this.toolTip.show(tip);
8425 this.intervalID = window.setInterval(function() {
8426 Roo.bootstrap.Form.popover.unmask();
8429 window.onwheel = function(){ return false;};
8431 (function(){ this.isMasked = true; }).defer(500, this);
8437 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8441 this.maskEl.top.setStyle('position', 'absolute');
8442 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8443 this.maskEl.top.hide();
8445 this.maskEl.left.setStyle('position', 'absolute');
8446 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8447 this.maskEl.left.hide();
8449 this.maskEl.bottom.setStyle('position', 'absolute');
8450 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8451 this.maskEl.bottom.hide();
8453 this.maskEl.right.setStyle('position', 'absolute');
8454 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8455 this.maskEl.right.hide();
8457 this.toolTip.hide();
8459 this.toolTip.el.hide();
8461 window.onwheel = function(){ return true;};
8463 if(this.intervalID){
8464 window.clearInterval(this.intervalID);
8465 this.intervalID = false;
8468 this.isMasked = false;
8478 * Ext JS Library 1.1.1
8479 * Copyright(c) 2006-2007, Ext JS, LLC.
8481 * Originally Released Under LGPL - original licence link has changed is not relivant.
8484 * <script type="text/javascript">
8487 * @class Roo.form.VTypes
8488 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8491 Roo.form.VTypes = function(){
8492 // closure these in so they are only created once.
8493 var alpha = /^[a-zA-Z_]+$/;
8494 var alphanum = /^[a-zA-Z0-9_]+$/;
8495 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8496 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8498 // All these messages and functions are configurable
8501 * The function used to validate email addresses
8502 * @param {String} value The email address
8504 'email' : function(v){
8505 return email.test(v);
8508 * The error text to display when the email validation function returns false
8511 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8513 * The keystroke filter mask to be applied on email input
8516 'emailMask' : /[a-z0-9_\.\-@]/i,
8519 * The function used to validate URLs
8520 * @param {String} value The URL
8522 'url' : function(v){
8526 * The error text to display when the url validation function returns false
8529 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8532 * The function used to validate alpha values
8533 * @param {String} value The value
8535 'alpha' : function(v){
8536 return alpha.test(v);
8539 * The error text to display when the alpha validation function returns false
8542 'alphaText' : 'This field should only contain letters and _',
8544 * The keystroke filter mask to be applied on alpha input
8547 'alphaMask' : /[a-z_]/i,
8550 * The function used to validate alphanumeric values
8551 * @param {String} value The value
8553 'alphanum' : function(v){
8554 return alphanum.test(v);
8557 * The error text to display when the alphanumeric validation function returns false
8560 'alphanumText' : 'This field should only contain letters, numbers and _',
8562 * The keystroke filter mask to be applied on alphanumeric input
8565 'alphanumMask' : /[a-z0-9_]/i
8575 * @class Roo.bootstrap.Input
8576 * @extends Roo.bootstrap.Component
8577 * Bootstrap Input class
8578 * @cfg {Boolean} disabled is it disabled
8579 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8580 * @cfg {String} name name of the input
8581 * @cfg {string} fieldLabel - the label associated
8582 * @cfg {string} placeholder - placeholder to put in text.
8583 * @cfg {string} before - input group add on before
8584 * @cfg {string} after - input group add on after
8585 * @cfg {string} size - (lg|sm) or leave empty..
8586 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8587 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8588 * @cfg {Number} md colspan out of 12 for computer-sized screens
8589 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8590 * @cfg {string} value default value of the input
8591 * @cfg {Number} labelWidth set the width of label
8592 * @cfg {Number} labellg set the width of label (1-12)
8593 * @cfg {Number} labelmd set the width of label (1-12)
8594 * @cfg {Number} labelsm set the width of label (1-12)
8595 * @cfg {Number} labelxs set the width of label (1-12)
8596 * @cfg {String} labelAlign (top|left)
8597 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8598 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8599 * @cfg {String} indicatorpos (left|right) default left
8600 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8601 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8603 * @cfg {String} align (left|center|right) Default left
8604 * @cfg {Boolean} forceFeedback (true|false) Default false
8607 * Create a new Input
8608 * @param {Object} config The config object
8611 Roo.bootstrap.Input = function(config){
8613 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8618 * Fires when this field receives input focus.
8619 * @param {Roo.form.Field} this
8624 * Fires when this field loses input focus.
8625 * @param {Roo.form.Field} this
8630 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8631 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8632 * @param {Roo.form.Field} this
8633 * @param {Roo.EventObject} e The event object
8638 * Fires just before the field blurs if the field value has changed.
8639 * @param {Roo.form.Field} this
8640 * @param {Mixed} newValue The new value
8641 * @param {Mixed} oldValue The original value
8646 * Fires after the field has been marked as invalid.
8647 * @param {Roo.form.Field} this
8648 * @param {String} msg The validation message
8653 * Fires after the field has been validated with no errors.
8654 * @param {Roo.form.Field} this
8659 * Fires after the key up
8660 * @param {Roo.form.Field} this
8661 * @param {Roo.EventObject} e The event Object
8667 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8669 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8670 automatic validation (defaults to "keyup").
8672 validationEvent : "keyup",
8674 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8676 validateOnBlur : true,
8678 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8680 validationDelay : 250,
8682 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8684 focusClass : "x-form-focus", // not needed???
8688 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8690 invalidClass : "has-warning",
8693 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8695 validClass : "has-success",
8698 * @cfg {Boolean} hasFeedback (true|false) default true
8703 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8705 invalidFeedbackClass : "glyphicon-warning-sign",
8708 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8710 validFeedbackClass : "glyphicon-ok",
8713 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8715 selectOnFocus : false,
8718 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8722 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8727 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8729 disableKeyFilter : false,
8732 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8736 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8740 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8742 blankText : "Please complete this mandatory field",
8745 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8749 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8751 maxLength : Number.MAX_VALUE,
8753 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8755 minLengthText : "The minimum length for this field is {0}",
8757 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8759 maxLengthText : "The maximum length for this field is {0}",
8763 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8764 * If available, this function will be called only after the basic validators all return true, and will be passed the
8765 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8769 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8770 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8771 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8775 * @cfg {String} regexText -- Depricated - use Invalid Text
8780 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8786 autocomplete: false,
8805 formatedValue : false,
8806 forceFeedback : false,
8808 indicatorpos : 'left',
8818 parentLabelAlign : function()
8821 while (parent.parent()) {
8822 parent = parent.parent();
8823 if (typeof(parent.labelAlign) !='undefined') {
8824 return parent.labelAlign;
8831 getAutoCreate : function()
8833 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8839 if(this.inputType != 'hidden'){
8840 cfg.cls = 'form-group' //input-group
8846 type : this.inputType,
8848 cls : 'form-control',
8849 placeholder : this.placeholder || '',
8850 autocomplete : this.autocomplete || 'new-password'
8853 if(this.capture.length){
8854 input.capture = this.capture;
8857 if(this.accept.length){
8858 input.accept = this.accept + "/*";
8862 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8865 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8866 input.maxLength = this.maxLength;
8869 if (this.disabled) {
8870 input.disabled=true;
8873 if (this.readOnly) {
8874 input.readonly=true;
8878 input.name = this.name;
8882 input.cls += ' input-' + this.size;
8886 ['xs','sm','md','lg'].map(function(size){
8887 if (settings[size]) {
8888 cfg.cls += ' col-' + size + '-' + settings[size];
8892 var inputblock = input;
8896 cls: 'glyphicon form-control-feedback'
8899 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8902 cls : 'has-feedback',
8910 if (this.before || this.after) {
8913 cls : 'input-group',
8917 if (this.before && typeof(this.before) == 'string') {
8919 inputblock.cn.push({
8921 cls : 'roo-input-before input-group-addon',
8925 if (this.before && typeof(this.before) == 'object') {
8926 this.before = Roo.factory(this.before);
8928 inputblock.cn.push({
8930 cls : 'roo-input-before input-group-' +
8931 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8935 inputblock.cn.push(input);
8937 if (this.after && typeof(this.after) == 'string') {
8938 inputblock.cn.push({
8940 cls : 'roo-input-after input-group-addon',
8944 if (this.after && typeof(this.after) == 'object') {
8945 this.after = Roo.factory(this.after);
8947 inputblock.cn.push({
8949 cls : 'roo-input-after input-group-' +
8950 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8954 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8955 inputblock.cls += ' has-feedback';
8956 inputblock.cn.push(feedback);
8960 if (align ==='left' && this.fieldLabel.length) {
8962 cfg.cls += ' roo-form-group-label-left';
8967 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8968 tooltip : 'This field is required'
8973 cls : 'control-label',
8974 html : this.fieldLabel
8985 var labelCfg = cfg.cn[1];
8986 var contentCfg = cfg.cn[2];
8988 if(this.indicatorpos == 'right'){
8993 cls : 'control-label',
8997 html : this.fieldLabel
9001 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9002 tooltip : 'This field is required'
9015 labelCfg = cfg.cn[0];
9016 contentCfg = cfg.cn[1];
9020 if(this.labelWidth > 12){
9021 labelCfg.style = "width: " + this.labelWidth + 'px';
9024 if(this.labelWidth < 13 && this.labelmd == 0){
9025 this.labelmd = this.labelWidth;
9028 if(this.labellg > 0){
9029 labelCfg.cls += ' col-lg-' + this.labellg;
9030 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9033 if(this.labelmd > 0){
9034 labelCfg.cls += ' col-md-' + this.labelmd;
9035 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9038 if(this.labelsm > 0){
9039 labelCfg.cls += ' col-sm-' + this.labelsm;
9040 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9043 if(this.labelxs > 0){
9044 labelCfg.cls += ' col-xs-' + this.labelxs;
9045 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9049 } else if ( this.fieldLabel.length) {
9054 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9055 tooltip : 'This field is required'
9059 //cls : 'input-group-addon',
9060 html : this.fieldLabel
9068 if(this.indicatorpos == 'right'){
9073 //cls : 'input-group-addon',
9074 html : this.fieldLabel
9079 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9080 tooltip : 'This field is required'
9100 if (this.parentType === 'Navbar' && this.parent().bar) {
9101 cfg.cls += ' navbar-form';
9104 if (this.parentType === 'NavGroup') {
9105 cfg.cls += ' navbar-form';
9113 * return the real input element.
9115 inputEl: function ()
9117 return this.el.select('input.form-control',true).first();
9120 tooltipEl : function()
9122 return this.inputEl();
9125 indicatorEl : function()
9127 var indicator = this.el.select('i.roo-required-indicator',true).first();
9137 setDisabled : function(v)
9139 var i = this.inputEl().dom;
9141 i.removeAttribute('disabled');
9145 i.setAttribute('disabled','true');
9147 initEvents : function()
9150 this.inputEl().on("keydown" , this.fireKey, this);
9151 this.inputEl().on("focus", this.onFocus, this);
9152 this.inputEl().on("blur", this.onBlur, this);
9154 this.inputEl().relayEvent('keyup', this);
9156 this.indicator = this.indicatorEl();
9159 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9162 // reference to original value for reset
9163 this.originalValue = this.getValue();
9164 //Roo.form.TextField.superclass.initEvents.call(this);
9165 if(this.validationEvent == 'keyup'){
9166 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9167 this.inputEl().on('keyup', this.filterValidation, this);
9169 else if(this.validationEvent !== false){
9170 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9173 if(this.selectOnFocus){
9174 this.on("focus", this.preFocus, this);
9177 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9178 this.inputEl().on("keypress", this.filterKeys, this);
9180 this.inputEl().relayEvent('keypress', this);
9183 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9184 this.el.on("click", this.autoSize, this);
9187 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9188 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9191 if (typeof(this.before) == 'object') {
9192 this.before.render(this.el.select('.roo-input-before',true).first());
9194 if (typeof(this.after) == 'object') {
9195 this.after.render(this.el.select('.roo-input-after',true).first());
9198 this.inputEl().on('change', this.onChange, this);
9201 filterValidation : function(e){
9202 if(!e.isNavKeyPress()){
9203 this.validationTask.delay(this.validationDelay);
9207 * Validates the field value
9208 * @return {Boolean} True if the value is valid, else false
9210 validate : function(){
9211 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9212 if(this.disabled || this.validateValue(this.getRawValue())){
9223 * Validates a value according to the field's validation rules and marks the field as invalid
9224 * if the validation fails
9225 * @param {Mixed} value The value to validate
9226 * @return {Boolean} True if the value is valid, else false
9228 validateValue : function(value)
9230 if(this.getVisibilityEl().hasClass('hidden')){
9234 if(value.length < 1) { // if it's blank
9235 if(this.allowBlank){
9241 if(value.length < this.minLength){
9244 if(value.length > this.maxLength){
9248 var vt = Roo.form.VTypes;
9249 if(!vt[this.vtype](value, this)){
9253 if(typeof this.validator == "function"){
9254 var msg = this.validator(value);
9258 if (typeof(msg) == 'string') {
9259 this.invalidText = msg;
9263 if(this.regex && !this.regex.test(value)){
9271 fireKey : function(e){
9272 //Roo.log('field ' + e.getKey());
9273 if(e.isNavKeyPress()){
9274 this.fireEvent("specialkey", this, e);
9277 focus : function (selectText){
9279 this.inputEl().focus();
9280 if(selectText === true){
9281 this.inputEl().dom.select();
9287 onFocus : function(){
9288 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9289 // this.el.addClass(this.focusClass);
9292 this.hasFocus = true;
9293 this.startValue = this.getValue();
9294 this.fireEvent("focus", this);
9298 beforeBlur : Roo.emptyFn,
9302 onBlur : function(){
9304 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9305 //this.el.removeClass(this.focusClass);
9307 this.hasFocus = false;
9308 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9311 var v = this.getValue();
9312 if(String(v) !== String(this.startValue)){
9313 this.fireEvent('change', this, v, this.startValue);
9315 this.fireEvent("blur", this);
9318 onChange : function(e)
9320 var v = this.getValue();
9321 if(String(v) !== String(this.startValue)){
9322 this.fireEvent('change', this, v, this.startValue);
9328 * Resets the current field value to the originally loaded value and clears any validation messages
9331 this.setValue(this.originalValue);
9335 * Returns the name of the field
9336 * @return {Mixed} name The name field
9338 getName: function(){
9342 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9343 * @return {Mixed} value The field value
9345 getValue : function(){
9347 var v = this.inputEl().getValue();
9352 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9353 * @return {Mixed} value The field value
9355 getRawValue : function(){
9356 var v = this.inputEl().getValue();
9362 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9363 * @param {Mixed} value The value to set
9365 setRawValue : function(v){
9366 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9369 selectText : function(start, end){
9370 var v = this.getRawValue();
9372 start = start === undefined ? 0 : start;
9373 end = end === undefined ? v.length : end;
9374 var d = this.inputEl().dom;
9375 if(d.setSelectionRange){
9376 d.setSelectionRange(start, end);
9377 }else if(d.createTextRange){
9378 var range = d.createTextRange();
9379 range.moveStart("character", start);
9380 range.moveEnd("character", v.length-end);
9387 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9388 * @param {Mixed} value The value to set
9390 setValue : function(v){
9393 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9399 processValue : function(value){
9400 if(this.stripCharsRe){
9401 var newValue = value.replace(this.stripCharsRe, '');
9402 if(newValue !== value){
9403 this.setRawValue(newValue);
9410 preFocus : function(){
9412 if(this.selectOnFocus){
9413 this.inputEl().dom.select();
9416 filterKeys : function(e){
9418 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9421 var c = e.getCharCode(), cc = String.fromCharCode(c);
9422 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9425 if(!this.maskRe.test(cc)){
9430 * Clear any invalid styles/messages for this field
9432 clearInvalid : function(){
9434 if(!this.el || this.preventMark){ // not rendered
9439 this.el.removeClass(this.invalidClass);
9441 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9443 var feedback = this.el.select('.form-control-feedback', true).first();
9446 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9452 this.indicator.removeClass('visible');
9453 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9456 this.fireEvent('valid', this);
9460 * Mark this field as valid
9462 markValid : function()
9464 if(!this.el || this.preventMark){ // not rendered...
9468 this.el.removeClass([this.invalidClass, this.validClass]);
9470 var feedback = this.el.select('.form-control-feedback', true).first();
9473 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9477 this.indicator.removeClass('visible');
9478 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9485 if(this.allowBlank && !this.getRawValue().length){
9489 this.el.addClass(this.validClass);
9491 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9493 var feedback = this.el.select('.form-control-feedback', true).first();
9496 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9497 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9502 this.fireEvent('valid', this);
9506 * Mark this field as invalid
9507 * @param {String} msg The validation message
9509 markInvalid : function(msg)
9511 if(!this.el || this.preventMark){ // not rendered
9515 this.el.removeClass([this.invalidClass, this.validClass]);
9517 var feedback = this.el.select('.form-control-feedback', true).first();
9520 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9527 if(this.allowBlank && !this.getRawValue().length){
9532 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9533 this.indicator.addClass('visible');
9536 this.el.addClass(this.invalidClass);
9538 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9540 var feedback = this.el.select('.form-control-feedback', true).first();
9543 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9545 if(this.getValue().length || this.forceFeedback){
9546 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9553 this.fireEvent('invalid', this, msg);
9556 SafariOnKeyDown : function(event)
9558 // this is a workaround for a password hang bug on chrome/ webkit.
9559 if (this.inputEl().dom.type != 'password') {
9563 var isSelectAll = false;
9565 if(this.inputEl().dom.selectionEnd > 0){
9566 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9568 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9569 event.preventDefault();
9574 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9576 event.preventDefault();
9577 // this is very hacky as keydown always get's upper case.
9579 var cc = String.fromCharCode(event.getCharCode());
9580 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9584 adjustWidth : function(tag, w){
9585 tag = tag.toLowerCase();
9586 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9587 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9591 if(tag == 'textarea'){
9594 }else if(Roo.isOpera){
9598 if(tag == 'textarea'){
9606 setFieldLabel : function(v)
9613 var ar = this.el.select('label > span',true);
9615 if (ar.elements.length) {
9616 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9617 this.fieldLabel = v;
9621 var br = this.el.select('label',true);
9623 if(br.elements.length) {
9624 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9625 this.fieldLabel = v;
9629 Roo.log('Cannot Found any of label > span || label in input');
9633 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9634 this.fieldLabel = v;
9649 * @class Roo.bootstrap.TextArea
9650 * @extends Roo.bootstrap.Input
9651 * Bootstrap TextArea class
9652 * @cfg {Number} cols Specifies the visible width of a text area
9653 * @cfg {Number} rows Specifies the visible number of lines in a text area
9654 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9655 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9656 * @cfg {string} html text
9659 * Create a new TextArea
9660 * @param {Object} config The config object
9663 Roo.bootstrap.TextArea = function(config){
9664 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9668 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9678 getAutoCreate : function(){
9680 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9686 if(this.inputType != 'hidden'){
9687 cfg.cls = 'form-group' //input-group
9695 value : this.value || '',
9696 html: this.html || '',
9697 cls : 'form-control',
9698 placeholder : this.placeholder || ''
9702 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9703 input.maxLength = this.maxLength;
9707 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9711 input.cols = this.cols;
9714 if (this.readOnly) {
9715 input.readonly = true;
9719 input.name = this.name;
9723 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9727 ['xs','sm','md','lg'].map(function(size){
9728 if (settings[size]) {
9729 cfg.cls += ' col-' + size + '-' + settings[size];
9733 var inputblock = input;
9735 if(this.hasFeedback && !this.allowBlank){
9739 cls: 'glyphicon form-control-feedback'
9743 cls : 'has-feedback',
9752 if (this.before || this.after) {
9755 cls : 'input-group',
9759 inputblock.cn.push({
9761 cls : 'input-group-addon',
9766 inputblock.cn.push(input);
9768 if(this.hasFeedback && !this.allowBlank){
9769 inputblock.cls += ' has-feedback';
9770 inputblock.cn.push(feedback);
9774 inputblock.cn.push({
9776 cls : 'input-group-addon',
9783 if (align ==='left' && this.fieldLabel.length) {
9788 cls : 'control-label',
9789 html : this.fieldLabel
9800 if(this.labelWidth > 12){
9801 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9804 if(this.labelWidth < 13 && this.labelmd == 0){
9805 this.labelmd = this.labelWidth;
9808 if(this.labellg > 0){
9809 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9810 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9813 if(this.labelmd > 0){
9814 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9815 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9818 if(this.labelsm > 0){
9819 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9820 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9823 if(this.labelxs > 0){
9824 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9825 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9828 } else if ( this.fieldLabel.length) {
9833 //cls : 'input-group-addon',
9834 html : this.fieldLabel
9852 if (this.disabled) {
9853 input.disabled=true;
9860 * return the real textarea element.
9862 inputEl: function ()
9864 return this.el.select('textarea.form-control',true).first();
9868 * Clear any invalid styles/messages for this field
9870 clearInvalid : function()
9873 if(!this.el || this.preventMark){ // not rendered
9877 var label = this.el.select('label', true).first();
9878 var icon = this.el.select('i.fa-star', true).first();
9884 this.el.removeClass(this.invalidClass);
9886 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9888 var feedback = this.el.select('.form-control-feedback', true).first();
9891 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9896 this.fireEvent('valid', this);
9900 * Mark this field as valid
9902 markValid : function()
9904 if(!this.el || this.preventMark){ // not rendered
9908 this.el.removeClass([this.invalidClass, this.validClass]);
9910 var feedback = this.el.select('.form-control-feedback', true).first();
9913 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9916 if(this.disabled || this.allowBlank){
9920 var label = this.el.select('label', true).first();
9921 var icon = this.el.select('i.fa-star', true).first();
9927 this.el.addClass(this.validClass);
9929 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9931 var feedback = this.el.select('.form-control-feedback', true).first();
9934 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9935 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9940 this.fireEvent('valid', this);
9944 * Mark this field as invalid
9945 * @param {String} msg The validation message
9947 markInvalid : function(msg)
9949 if(!this.el || this.preventMark){ // not rendered
9953 this.el.removeClass([this.invalidClass, this.validClass]);
9955 var feedback = this.el.select('.form-control-feedback', true).first();
9958 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9961 if(this.disabled || this.allowBlank){
9965 var label = this.el.select('label', true).first();
9966 var icon = this.el.select('i.fa-star', true).first();
9968 if(!this.getValue().length && label && !icon){
9969 this.el.createChild({
9971 cls : 'text-danger fa fa-lg fa-star',
9972 tooltip : 'This field is required',
9973 style : 'margin-right:5px;'
9977 this.el.addClass(this.invalidClass);
9979 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9981 var feedback = this.el.select('.form-control-feedback', true).first();
9984 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9986 if(this.getValue().length || this.forceFeedback){
9987 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9994 this.fireEvent('invalid', this, msg);
10002 * trigger field - base class for combo..
10007 * @class Roo.bootstrap.TriggerField
10008 * @extends Roo.bootstrap.Input
10009 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10010 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10011 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10012 * for which you can provide a custom implementation. For example:
10014 var trigger = new Roo.bootstrap.TriggerField();
10015 trigger.onTriggerClick = myTriggerFn;
10016 trigger.applyTo('my-field');
10019 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10020 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10021 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10022 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10023 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10026 * Create a new TriggerField.
10027 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10028 * to the base TextField)
10030 Roo.bootstrap.TriggerField = function(config){
10031 this.mimicing = false;
10032 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10035 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10037 * @cfg {String} triggerClass A CSS class to apply to the trigger
10040 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10045 * @cfg {Boolean} removable (true|false) special filter default false
10049 /** @cfg {Boolean} grow @hide */
10050 /** @cfg {Number} growMin @hide */
10051 /** @cfg {Number} growMax @hide */
10057 autoSize: Roo.emptyFn,
10061 deferHeight : true,
10064 actionMode : 'wrap',
10069 getAutoCreate : function(){
10071 var align = this.labelAlign || this.parentLabelAlign();
10076 cls: 'form-group' //input-group
10083 type : this.inputType,
10084 cls : 'form-control',
10085 autocomplete: 'new-password',
10086 placeholder : this.placeholder || ''
10090 input.name = this.name;
10093 input.cls += ' input-' + this.size;
10096 if (this.disabled) {
10097 input.disabled=true;
10100 var inputblock = input;
10102 if(this.hasFeedback && !this.allowBlank){
10106 cls: 'glyphicon form-control-feedback'
10109 if(this.removable && !this.editable && !this.tickable){
10111 cls : 'has-feedback',
10117 cls : 'roo-combo-removable-btn close'
10124 cls : 'has-feedback',
10133 if(this.removable && !this.editable && !this.tickable){
10135 cls : 'roo-removable',
10141 cls : 'roo-combo-removable-btn close'
10148 if (this.before || this.after) {
10151 cls : 'input-group',
10155 inputblock.cn.push({
10157 cls : 'input-group-addon',
10162 inputblock.cn.push(input);
10164 if(this.hasFeedback && !this.allowBlank){
10165 inputblock.cls += ' has-feedback';
10166 inputblock.cn.push(feedback);
10170 inputblock.cn.push({
10172 cls : 'input-group-addon',
10185 cls: 'form-hidden-field'
10199 cls: 'form-hidden-field'
10203 cls: 'roo-select2-choices',
10207 cls: 'roo-select2-search-field',
10220 cls: 'roo-select2-container input-group',
10225 // cls: 'typeahead typeahead-long dropdown-menu',
10226 // style: 'display:none'
10231 if(!this.multiple && this.showToggleBtn){
10237 if (this.caret != false) {
10240 cls: 'fa fa-' + this.caret
10247 cls : 'input-group-addon btn dropdown-toggle',
10252 cls: 'combobox-clear',
10266 combobox.cls += ' roo-select2-container-multi';
10269 if (align ==='left' && this.fieldLabel.length) {
10271 cfg.cls += ' roo-form-group-label-left';
10276 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10277 tooltip : 'This field is required'
10282 cls : 'control-label',
10283 html : this.fieldLabel
10295 var labelCfg = cfg.cn[1];
10296 var contentCfg = cfg.cn[2];
10298 if(this.indicatorpos == 'right'){
10303 cls : 'control-label',
10307 html : this.fieldLabel
10311 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10312 tooltip : 'This field is required'
10325 labelCfg = cfg.cn[0];
10326 contentCfg = cfg.cn[1];
10329 if(this.labelWidth > 12){
10330 labelCfg.style = "width: " + this.labelWidth + 'px';
10333 if(this.labelWidth < 13 && this.labelmd == 0){
10334 this.labelmd = this.labelWidth;
10337 if(this.labellg > 0){
10338 labelCfg.cls += ' col-lg-' + this.labellg;
10339 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10342 if(this.labelmd > 0){
10343 labelCfg.cls += ' col-md-' + this.labelmd;
10344 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10347 if(this.labelsm > 0){
10348 labelCfg.cls += ' col-sm-' + this.labelsm;
10349 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10352 if(this.labelxs > 0){
10353 labelCfg.cls += ' col-xs-' + this.labelxs;
10354 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10357 } else if ( this.fieldLabel.length) {
10358 // Roo.log(" label");
10362 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10363 tooltip : 'This field is required'
10367 //cls : 'input-group-addon',
10368 html : this.fieldLabel
10376 if(this.indicatorpos == 'right'){
10384 html : this.fieldLabel
10388 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10389 tooltip : 'This field is required'
10402 // Roo.log(" no label && no align");
10409 ['xs','sm','md','lg'].map(function(size){
10410 if (settings[size]) {
10411 cfg.cls += ' col-' + size + '-' + settings[size];
10422 onResize : function(w, h){
10423 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10424 // if(typeof w == 'number'){
10425 // var x = w - this.trigger.getWidth();
10426 // this.inputEl().setWidth(this.adjustWidth('input', x));
10427 // this.trigger.setStyle('left', x+'px');
10432 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10435 getResizeEl : function(){
10436 return this.inputEl();
10440 getPositionEl : function(){
10441 return this.inputEl();
10445 alignErrorIcon : function(){
10446 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10450 initEvents : function(){
10454 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10455 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10456 if(!this.multiple && this.showToggleBtn){
10457 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10458 if(this.hideTrigger){
10459 this.trigger.setDisplayed(false);
10461 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10465 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10468 if(this.removable && !this.editable && !this.tickable){
10469 var close = this.closeTriggerEl();
10472 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10473 close.on('click', this.removeBtnClick, this, close);
10477 //this.trigger.addClassOnOver('x-form-trigger-over');
10478 //this.trigger.addClassOnClick('x-form-trigger-click');
10481 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10485 closeTriggerEl : function()
10487 var close = this.el.select('.roo-combo-removable-btn', true).first();
10488 return close ? close : false;
10491 removeBtnClick : function(e, h, el)
10493 e.preventDefault();
10495 if(this.fireEvent("remove", this) !== false){
10497 this.fireEvent("afterremove", this)
10501 createList : function()
10503 this.list = Roo.get(document.body).createChild({
10505 cls: 'typeahead typeahead-long dropdown-menu',
10506 style: 'display:none'
10509 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10514 initTrigger : function(){
10519 onDestroy : function(){
10521 this.trigger.removeAllListeners();
10522 // this.trigger.remove();
10525 // this.wrap.remove();
10527 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10531 onFocus : function(){
10532 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10534 if(!this.mimicing){
10535 this.wrap.addClass('x-trigger-wrap-focus');
10536 this.mimicing = true;
10537 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10538 if(this.monitorTab){
10539 this.el.on("keydown", this.checkTab, this);
10546 checkTab : function(e){
10547 if(e.getKey() == e.TAB){
10548 this.triggerBlur();
10553 onBlur : function(){
10558 mimicBlur : function(e, t){
10560 if(!this.wrap.contains(t) && this.validateBlur()){
10561 this.triggerBlur();
10567 triggerBlur : function(){
10568 this.mimicing = false;
10569 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10570 if(this.monitorTab){
10571 this.el.un("keydown", this.checkTab, this);
10573 //this.wrap.removeClass('x-trigger-wrap-focus');
10574 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10578 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10579 validateBlur : function(e, t){
10584 onDisable : function(){
10585 this.inputEl().dom.disabled = true;
10586 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10588 // this.wrap.addClass('x-item-disabled');
10593 onEnable : function(){
10594 this.inputEl().dom.disabled = false;
10595 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10597 // this.el.removeClass('x-item-disabled');
10602 onShow : function(){
10603 var ae = this.getActionEl();
10606 ae.dom.style.display = '';
10607 ae.dom.style.visibility = 'visible';
10613 onHide : function(){
10614 var ae = this.getActionEl();
10615 ae.dom.style.display = 'none';
10619 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10620 * by an implementing function.
10622 * @param {EventObject} e
10624 onTriggerClick : Roo.emptyFn
10628 * Ext JS Library 1.1.1
10629 * Copyright(c) 2006-2007, Ext JS, LLC.
10631 * Originally Released Under LGPL - original licence link has changed is not relivant.
10634 * <script type="text/javascript">
10639 * @class Roo.data.SortTypes
10641 * Defines the default sorting (casting?) comparison functions used when sorting data.
10643 Roo.data.SortTypes = {
10645 * Default sort that does nothing
10646 * @param {Mixed} s The value being converted
10647 * @return {Mixed} The comparison value
10649 none : function(s){
10654 * The regular expression used to strip tags
10658 stripTagsRE : /<\/?[^>]+>/gi,
10661 * Strips all HTML tags to sort on text only
10662 * @param {Mixed} s The value being converted
10663 * @return {String} The comparison value
10665 asText : function(s){
10666 return String(s).replace(this.stripTagsRE, "");
10670 * Strips all HTML tags to sort on text only - Case insensitive
10671 * @param {Mixed} s The value being converted
10672 * @return {String} The comparison value
10674 asUCText : function(s){
10675 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10679 * Case insensitive string
10680 * @param {Mixed} s The value being converted
10681 * @return {String} The comparison value
10683 asUCString : function(s) {
10684 return String(s).toUpperCase();
10689 * @param {Mixed} s The value being converted
10690 * @return {Number} The comparison value
10692 asDate : function(s) {
10696 if(s instanceof Date){
10697 return s.getTime();
10699 return Date.parse(String(s));
10704 * @param {Mixed} s The value being converted
10705 * @return {Float} The comparison value
10707 asFloat : function(s) {
10708 var val = parseFloat(String(s).replace(/,/g, ""));
10717 * @param {Mixed} s The value being converted
10718 * @return {Number} The comparison value
10720 asInt : function(s) {
10721 var val = parseInt(String(s).replace(/,/g, ""));
10729 * Ext JS Library 1.1.1
10730 * Copyright(c) 2006-2007, Ext JS, LLC.
10732 * Originally Released Under LGPL - original licence link has changed is not relivant.
10735 * <script type="text/javascript">
10739 * @class Roo.data.Record
10740 * Instances of this class encapsulate both record <em>definition</em> information, and record
10741 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10742 * to access Records cached in an {@link Roo.data.Store} object.<br>
10744 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10745 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10748 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10750 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10751 * {@link #create}. The parameters are the same.
10752 * @param {Array} data An associative Array of data values keyed by the field name.
10753 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10754 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10755 * not specified an integer id is generated.
10757 Roo.data.Record = function(data, id){
10758 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10763 * Generate a constructor for a specific record layout.
10764 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10765 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10766 * Each field definition object may contain the following properties: <ul>
10767 * <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,
10768 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10769 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10770 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10771 * is being used, then this is a string containing the javascript expression to reference the data relative to
10772 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10773 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10774 * this may be omitted.</p></li>
10775 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10776 * <ul><li>auto (Default, implies no conversion)</li>
10781 * <li>date</li></ul></p></li>
10782 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10783 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10784 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10785 * by the Reader into an object that will be stored in the Record. It is passed the
10786 * following parameters:<ul>
10787 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10789 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10791 * <br>usage:<br><pre><code>
10792 var TopicRecord = Roo.data.Record.create(
10793 {name: 'title', mapping: 'topic_title'},
10794 {name: 'author', mapping: 'username'},
10795 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10796 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10797 {name: 'lastPoster', mapping: 'user2'},
10798 {name: 'excerpt', mapping: 'post_text'}
10801 var myNewRecord = new TopicRecord({
10802 title: 'Do my job please',
10805 lastPost: new Date(),
10806 lastPoster: 'Animal',
10807 excerpt: 'No way dude!'
10809 myStore.add(myNewRecord);
10814 Roo.data.Record.create = function(o){
10815 var f = function(){
10816 f.superclass.constructor.apply(this, arguments);
10818 Roo.extend(f, Roo.data.Record);
10819 var p = f.prototype;
10820 p.fields = new Roo.util.MixedCollection(false, function(field){
10823 for(var i = 0, len = o.length; i < len; i++){
10824 p.fields.add(new Roo.data.Field(o[i]));
10826 f.getField = function(name){
10827 return p.fields.get(name);
10832 Roo.data.Record.AUTO_ID = 1000;
10833 Roo.data.Record.EDIT = 'edit';
10834 Roo.data.Record.REJECT = 'reject';
10835 Roo.data.Record.COMMIT = 'commit';
10837 Roo.data.Record.prototype = {
10839 * Readonly flag - true if this record has been modified.
10848 join : function(store){
10849 this.store = store;
10853 * Set the named field to the specified value.
10854 * @param {String} name The name of the field to set.
10855 * @param {Object} value The value to set the field to.
10857 set : function(name, value){
10858 if(this.data[name] == value){
10862 if(!this.modified){
10863 this.modified = {};
10865 if(typeof this.modified[name] == 'undefined'){
10866 this.modified[name] = this.data[name];
10868 this.data[name] = value;
10869 if(!this.editing && this.store){
10870 this.store.afterEdit(this);
10875 * Get the value of the named field.
10876 * @param {String} name The name of the field to get the value of.
10877 * @return {Object} The value of the field.
10879 get : function(name){
10880 return this.data[name];
10884 beginEdit : function(){
10885 this.editing = true;
10886 this.modified = {};
10890 cancelEdit : function(){
10891 this.editing = false;
10892 delete this.modified;
10896 endEdit : function(){
10897 this.editing = false;
10898 if(this.dirty && this.store){
10899 this.store.afterEdit(this);
10904 * Usually called by the {@link Roo.data.Store} which owns the Record.
10905 * Rejects all changes made to the Record since either creation, or the last commit operation.
10906 * Modified fields are reverted to their original values.
10908 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10909 * of reject operations.
10911 reject : function(){
10912 var m = this.modified;
10914 if(typeof m[n] != "function"){
10915 this.data[n] = m[n];
10918 this.dirty = false;
10919 delete this.modified;
10920 this.editing = false;
10922 this.store.afterReject(this);
10927 * Usually called by the {@link Roo.data.Store} which owns the Record.
10928 * Commits all changes made to the Record since either creation, or the last commit operation.
10930 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10931 * of commit operations.
10933 commit : function(){
10934 this.dirty = false;
10935 delete this.modified;
10936 this.editing = false;
10938 this.store.afterCommit(this);
10943 hasError : function(){
10944 return this.error != null;
10948 clearError : function(){
10953 * Creates a copy of this record.
10954 * @param {String} id (optional) A new record id if you don't want to use this record's id
10957 copy : function(newId) {
10958 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10962 * Ext JS Library 1.1.1
10963 * Copyright(c) 2006-2007, Ext JS, LLC.
10965 * Originally Released Under LGPL - original licence link has changed is not relivant.
10968 * <script type="text/javascript">
10974 * @class Roo.data.Store
10975 * @extends Roo.util.Observable
10976 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10977 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10979 * 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
10980 * has no knowledge of the format of the data returned by the Proxy.<br>
10982 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10983 * instances from the data object. These records are cached and made available through accessor functions.
10985 * Creates a new Store.
10986 * @param {Object} config A config object containing the objects needed for the Store to access data,
10987 * and read the data into Records.
10989 Roo.data.Store = function(config){
10990 this.data = new Roo.util.MixedCollection(false);
10991 this.data.getKey = function(o){
10994 this.baseParams = {};
10996 this.paramNames = {
11001 "multisort" : "_multisort"
11004 if(config && config.data){
11005 this.inlineData = config.data;
11006 delete config.data;
11009 Roo.apply(this, config);
11011 if(this.reader){ // reader passed
11012 this.reader = Roo.factory(this.reader, Roo.data);
11013 this.reader.xmodule = this.xmodule || false;
11014 if(!this.recordType){
11015 this.recordType = this.reader.recordType;
11017 if(this.reader.onMetaChange){
11018 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11022 if(this.recordType){
11023 this.fields = this.recordType.prototype.fields;
11025 this.modified = [];
11029 * @event datachanged
11030 * Fires when the data cache has changed, and a widget which is using this Store
11031 * as a Record cache should refresh its view.
11032 * @param {Store} this
11034 datachanged : true,
11036 * @event metachange
11037 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11038 * @param {Store} this
11039 * @param {Object} meta The JSON metadata
11044 * Fires when Records have been added to the Store
11045 * @param {Store} this
11046 * @param {Roo.data.Record[]} records The array of Records added
11047 * @param {Number} index The index at which the record(s) were added
11052 * Fires when a Record has been removed from the Store
11053 * @param {Store} this
11054 * @param {Roo.data.Record} record The Record that was removed
11055 * @param {Number} index The index at which the record was removed
11060 * Fires when a Record has been updated
11061 * @param {Store} this
11062 * @param {Roo.data.Record} record The Record that was updated
11063 * @param {String} operation The update operation being performed. Value may be one of:
11065 Roo.data.Record.EDIT
11066 Roo.data.Record.REJECT
11067 Roo.data.Record.COMMIT
11073 * Fires when the data cache has been cleared.
11074 * @param {Store} this
11078 * @event beforeload
11079 * Fires before a request is made for a new data object. If the beforeload handler returns false
11080 * the load action will be canceled.
11081 * @param {Store} this
11082 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11086 * @event beforeloadadd
11087 * Fires after a new set of Records has been loaded.
11088 * @param {Store} this
11089 * @param {Roo.data.Record[]} records The Records that were loaded
11090 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11092 beforeloadadd : true,
11095 * Fires after a new set of Records has been loaded, before they are added to the store.
11096 * @param {Store} this
11097 * @param {Roo.data.Record[]} records The Records that were loaded
11098 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11099 * @params {Object} return from reader
11103 * @event loadexception
11104 * Fires if an exception occurs in the Proxy during loading.
11105 * Called with the signature of the Proxy's "loadexception" event.
11106 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11109 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11110 * @param {Object} load options
11111 * @param {Object} jsonData from your request (normally this contains the Exception)
11113 loadexception : true
11117 this.proxy = Roo.factory(this.proxy, Roo.data);
11118 this.proxy.xmodule = this.xmodule || false;
11119 this.relayEvents(this.proxy, ["loadexception"]);
11121 this.sortToggle = {};
11122 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11124 Roo.data.Store.superclass.constructor.call(this);
11126 if(this.inlineData){
11127 this.loadData(this.inlineData);
11128 delete this.inlineData;
11132 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11134 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11135 * without a remote query - used by combo/forms at present.
11139 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11142 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11145 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11146 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11149 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11150 * on any HTTP request
11153 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11156 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11160 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11161 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11163 remoteSort : false,
11166 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11167 * loaded or when a record is removed. (defaults to false).
11169 pruneModifiedRecords : false,
11172 lastOptions : null,
11175 * Add Records to the Store and fires the add event.
11176 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11178 add : function(records){
11179 records = [].concat(records);
11180 for(var i = 0, len = records.length; i < len; i++){
11181 records[i].join(this);
11183 var index = this.data.length;
11184 this.data.addAll(records);
11185 this.fireEvent("add", this, records, index);
11189 * Remove a Record from the Store and fires the remove event.
11190 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11192 remove : function(record){
11193 var index = this.data.indexOf(record);
11194 this.data.removeAt(index);
11196 if(this.pruneModifiedRecords){
11197 this.modified.remove(record);
11199 this.fireEvent("remove", this, record, index);
11203 * Remove all Records from the Store and fires the clear event.
11205 removeAll : function(){
11207 if(this.pruneModifiedRecords){
11208 this.modified = [];
11210 this.fireEvent("clear", this);
11214 * Inserts Records to the Store at the given index and fires the add event.
11215 * @param {Number} index The start index at which to insert the passed Records.
11216 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11218 insert : function(index, records){
11219 records = [].concat(records);
11220 for(var i = 0, len = records.length; i < len; i++){
11221 this.data.insert(index, records[i]);
11222 records[i].join(this);
11224 this.fireEvent("add", this, records, index);
11228 * Get the index within the cache of the passed Record.
11229 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11230 * @return {Number} The index of the passed Record. Returns -1 if not found.
11232 indexOf : function(record){
11233 return this.data.indexOf(record);
11237 * Get the index within the cache of the Record with the passed id.
11238 * @param {String} id The id of the Record to find.
11239 * @return {Number} The index of the Record. Returns -1 if not found.
11241 indexOfId : function(id){
11242 return this.data.indexOfKey(id);
11246 * Get the Record with the specified id.
11247 * @param {String} id The id of the Record to find.
11248 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11250 getById : function(id){
11251 return this.data.key(id);
11255 * Get the Record at the specified index.
11256 * @param {Number} index The index of the Record to find.
11257 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11259 getAt : function(index){
11260 return this.data.itemAt(index);
11264 * Returns a range of Records between specified indices.
11265 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11266 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11267 * @return {Roo.data.Record[]} An array of Records
11269 getRange : function(start, end){
11270 return this.data.getRange(start, end);
11274 storeOptions : function(o){
11275 o = Roo.apply({}, o);
11278 this.lastOptions = o;
11282 * Loads the Record cache from the configured Proxy using the configured Reader.
11284 * If using remote paging, then the first load call must specify the <em>start</em>
11285 * and <em>limit</em> properties in the options.params property to establish the initial
11286 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11288 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11289 * and this call will return before the new data has been loaded. Perform any post-processing
11290 * in a callback function, or in a "load" event handler.</strong>
11292 * @param {Object} options An object containing properties which control loading options:<ul>
11293 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11294 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11295 * passed the following arguments:<ul>
11296 * <li>r : Roo.data.Record[]</li>
11297 * <li>options: Options object from the load call</li>
11298 * <li>success: Boolean success indicator</li></ul></li>
11299 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11300 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11303 load : function(options){
11304 options = options || {};
11305 if(this.fireEvent("beforeload", this, options) !== false){
11306 this.storeOptions(options);
11307 var p = Roo.apply(options.params || {}, this.baseParams);
11308 // if meta was not loaded from remote source.. try requesting it.
11309 if (!this.reader.metaFromRemote) {
11310 p._requestMeta = 1;
11312 if(this.sortInfo && this.remoteSort){
11313 var pn = this.paramNames;
11314 p[pn["sort"]] = this.sortInfo.field;
11315 p[pn["dir"]] = this.sortInfo.direction;
11317 if (this.multiSort) {
11318 var pn = this.paramNames;
11319 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11322 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11327 * Reloads the Record cache from the configured Proxy using the configured Reader and
11328 * the options from the last load operation performed.
11329 * @param {Object} options (optional) An object containing properties which may override the options
11330 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11331 * the most recently used options are reused).
11333 reload : function(options){
11334 this.load(Roo.applyIf(options||{}, this.lastOptions));
11338 // Called as a callback by the Reader during a load operation.
11339 loadRecords : function(o, options, success){
11340 if(!o || success === false){
11341 if(success !== false){
11342 this.fireEvent("load", this, [], options, o);
11344 if(options.callback){
11345 options.callback.call(options.scope || this, [], options, false);
11349 // if data returned failure - throw an exception.
11350 if (o.success === false) {
11351 // show a message if no listener is registered.
11352 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11353 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11355 // loadmask wil be hooked into this..
11356 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11359 var r = o.records, t = o.totalRecords || r.length;
11361 this.fireEvent("beforeloadadd", this, r, options, o);
11363 if(!options || options.add !== true){
11364 if(this.pruneModifiedRecords){
11365 this.modified = [];
11367 for(var i = 0, len = r.length; i < len; i++){
11371 this.data = this.snapshot;
11372 delete this.snapshot;
11375 this.data.addAll(r);
11376 this.totalLength = t;
11378 this.fireEvent("datachanged", this);
11380 this.totalLength = Math.max(t, this.data.length+r.length);
11384 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11386 var e = new Roo.data.Record({});
11388 e.set(this.parent.displayField, this.parent.emptyTitle);
11389 e.set(this.parent.valueField, '');
11394 this.fireEvent("load", this, r, options, o);
11395 if(options.callback){
11396 options.callback.call(options.scope || this, r, options, true);
11402 * Loads data from a passed data block. A Reader which understands the format of the data
11403 * must have been configured in the constructor.
11404 * @param {Object} data The data block from which to read the Records. The format of the data expected
11405 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11406 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11408 loadData : function(o, append){
11409 var r = this.reader.readRecords(o);
11410 this.loadRecords(r, {add: append}, true);
11414 * Gets the number of cached records.
11416 * <em>If using paging, this may not be the total size of the dataset. If the data object
11417 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11418 * the data set size</em>
11420 getCount : function(){
11421 return this.data.length || 0;
11425 * Gets the total number of records in the dataset as returned by the server.
11427 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11428 * the dataset size</em>
11430 getTotalCount : function(){
11431 return this.totalLength || 0;
11435 * Returns the sort state of the Store as an object with two properties:
11437 field {String} The name of the field by which the Records are sorted
11438 direction {String} The sort order, "ASC" or "DESC"
11441 getSortState : function(){
11442 return this.sortInfo;
11446 applySort : function(){
11447 if(this.sortInfo && !this.remoteSort){
11448 var s = this.sortInfo, f = s.field;
11449 var st = this.fields.get(f).sortType;
11450 var fn = function(r1, r2){
11451 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11452 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11454 this.data.sort(s.direction, fn);
11455 if(this.snapshot && this.snapshot != this.data){
11456 this.snapshot.sort(s.direction, fn);
11462 * Sets the default sort column and order to be used by the next load operation.
11463 * @param {String} fieldName The name of the field to sort by.
11464 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11466 setDefaultSort : function(field, dir){
11467 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11471 * Sort the Records.
11472 * If remote sorting is used, the sort is performed on the server, and the cache is
11473 * reloaded. If local sorting is used, the cache is sorted internally.
11474 * @param {String} fieldName The name of the field to sort by.
11475 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11477 sort : function(fieldName, dir){
11478 var f = this.fields.get(fieldName);
11480 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11482 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11483 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11488 this.sortToggle[f.name] = dir;
11489 this.sortInfo = {field: f.name, direction: dir};
11490 if(!this.remoteSort){
11492 this.fireEvent("datachanged", this);
11494 this.load(this.lastOptions);
11499 * Calls the specified function for each of the Records in the cache.
11500 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11501 * Returning <em>false</em> aborts and exits the iteration.
11502 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11504 each : function(fn, scope){
11505 this.data.each(fn, scope);
11509 * Gets all records modified since the last commit. Modified records are persisted across load operations
11510 * (e.g., during paging).
11511 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11513 getModifiedRecords : function(){
11514 return this.modified;
11518 createFilterFn : function(property, value, anyMatch){
11519 if(!value.exec){ // not a regex
11520 value = String(value);
11521 if(value.length == 0){
11524 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11526 return function(r){
11527 return value.test(r.data[property]);
11532 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11533 * @param {String} property A field on your records
11534 * @param {Number} start The record index to start at (defaults to 0)
11535 * @param {Number} end The last record index to include (defaults to length - 1)
11536 * @return {Number} The sum
11538 sum : function(property, start, end){
11539 var rs = this.data.items, v = 0;
11540 start = start || 0;
11541 end = (end || end === 0) ? end : rs.length-1;
11543 for(var i = start; i <= end; i++){
11544 v += (rs[i].data[property] || 0);
11550 * Filter the records by a specified property.
11551 * @param {String} field A field on your records
11552 * @param {String/RegExp} value Either a string that the field
11553 * should start with or a RegExp to test against the field
11554 * @param {Boolean} anyMatch True to match any part not just the beginning
11556 filter : function(property, value, anyMatch){
11557 var fn = this.createFilterFn(property, value, anyMatch);
11558 return fn ? this.filterBy(fn) : this.clearFilter();
11562 * Filter by a function. The specified function will be called with each
11563 * record in this data source. If the function returns true the record is included,
11564 * otherwise it is filtered.
11565 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11566 * @param {Object} scope (optional) The scope of the function (defaults to this)
11568 filterBy : function(fn, scope){
11569 this.snapshot = this.snapshot || this.data;
11570 this.data = this.queryBy(fn, scope||this);
11571 this.fireEvent("datachanged", this);
11575 * Query the records by a specified property.
11576 * @param {String} field A field on your records
11577 * @param {String/RegExp} value Either a string that the field
11578 * should start with or a RegExp to test against the field
11579 * @param {Boolean} anyMatch True to match any part not just the beginning
11580 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11582 query : function(property, value, anyMatch){
11583 var fn = this.createFilterFn(property, value, anyMatch);
11584 return fn ? this.queryBy(fn) : this.data.clone();
11588 * Query by a function. The specified function will be called with each
11589 * record in this data source. If the function returns true the record is included
11591 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11592 * @param {Object} scope (optional) The scope of the function (defaults to this)
11593 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11595 queryBy : function(fn, scope){
11596 var data = this.snapshot || this.data;
11597 return data.filterBy(fn, scope||this);
11601 * Collects unique values for a particular dataIndex from this store.
11602 * @param {String} dataIndex The property to collect
11603 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11604 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11605 * @return {Array} An array of the unique values
11607 collect : function(dataIndex, allowNull, bypassFilter){
11608 var d = (bypassFilter === true && this.snapshot) ?
11609 this.snapshot.items : this.data.items;
11610 var v, sv, r = [], l = {};
11611 for(var i = 0, len = d.length; i < len; i++){
11612 v = d[i].data[dataIndex];
11614 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11623 * Revert to a view of the Record cache with no filtering applied.
11624 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11626 clearFilter : function(suppressEvent){
11627 if(this.snapshot && this.snapshot != this.data){
11628 this.data = this.snapshot;
11629 delete this.snapshot;
11630 if(suppressEvent !== true){
11631 this.fireEvent("datachanged", this);
11637 afterEdit : function(record){
11638 if(this.modified.indexOf(record) == -1){
11639 this.modified.push(record);
11641 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11645 afterReject : function(record){
11646 this.modified.remove(record);
11647 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11651 afterCommit : function(record){
11652 this.modified.remove(record);
11653 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11657 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11658 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11660 commitChanges : function(){
11661 var m = this.modified.slice(0);
11662 this.modified = [];
11663 for(var i = 0, len = m.length; i < len; i++){
11669 * Cancel outstanding changes on all changed records.
11671 rejectChanges : function(){
11672 var m = this.modified.slice(0);
11673 this.modified = [];
11674 for(var i = 0, len = m.length; i < len; i++){
11679 onMetaChange : function(meta, rtype, o){
11680 this.recordType = rtype;
11681 this.fields = rtype.prototype.fields;
11682 delete this.snapshot;
11683 this.sortInfo = meta.sortInfo || this.sortInfo;
11684 this.modified = [];
11685 this.fireEvent('metachange', this, this.reader.meta);
11688 moveIndex : function(data, type)
11690 var index = this.indexOf(data);
11692 var newIndex = index + type;
11696 this.insert(newIndex, data);
11701 * Ext JS Library 1.1.1
11702 * Copyright(c) 2006-2007, Ext JS, LLC.
11704 * Originally Released Under LGPL - original licence link has changed is not relivant.
11707 * <script type="text/javascript">
11711 * @class Roo.data.SimpleStore
11712 * @extends Roo.data.Store
11713 * Small helper class to make creating Stores from Array data easier.
11714 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11715 * @cfg {Array} fields An array of field definition objects, or field name strings.
11716 * @cfg {Array} data The multi-dimensional array of data
11718 * @param {Object} config
11720 Roo.data.SimpleStore = function(config){
11721 Roo.data.SimpleStore.superclass.constructor.call(this, {
11723 reader: new Roo.data.ArrayReader({
11726 Roo.data.Record.create(config.fields)
11728 proxy : new Roo.data.MemoryProxy(config.data)
11732 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11734 * Ext JS Library 1.1.1
11735 * Copyright(c) 2006-2007, Ext JS, LLC.
11737 * Originally Released Under LGPL - original licence link has changed is not relivant.
11740 * <script type="text/javascript">
11745 * @extends Roo.data.Store
11746 * @class Roo.data.JsonStore
11747 * Small helper class to make creating Stores for JSON data easier. <br/>
11749 var store = new Roo.data.JsonStore({
11750 url: 'get-images.php',
11752 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11755 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11756 * JsonReader and HttpProxy (unless inline data is provided).</b>
11757 * @cfg {Array} fields An array of field definition objects, or field name strings.
11759 * @param {Object} config
11761 Roo.data.JsonStore = function(c){
11762 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11763 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11764 reader: new Roo.data.JsonReader(c, c.fields)
11767 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11769 * Ext JS Library 1.1.1
11770 * Copyright(c) 2006-2007, Ext JS, LLC.
11772 * Originally Released Under LGPL - original licence link has changed is not relivant.
11775 * <script type="text/javascript">
11779 Roo.data.Field = function(config){
11780 if(typeof config == "string"){
11781 config = {name: config};
11783 Roo.apply(this, config);
11786 this.type = "auto";
11789 var st = Roo.data.SortTypes;
11790 // named sortTypes are supported, here we look them up
11791 if(typeof this.sortType == "string"){
11792 this.sortType = st[this.sortType];
11795 // set default sortType for strings and dates
11796 if(!this.sortType){
11799 this.sortType = st.asUCString;
11802 this.sortType = st.asDate;
11805 this.sortType = st.none;
11810 var stripRe = /[\$,%]/g;
11812 // prebuilt conversion function for this field, instead of
11813 // switching every time we're reading a value
11815 var cv, dateFormat = this.dateFormat;
11820 cv = function(v){ return v; };
11823 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11827 return v !== undefined && v !== null && v !== '' ?
11828 parseInt(String(v).replace(stripRe, ""), 10) : '';
11833 return v !== undefined && v !== null && v !== '' ?
11834 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11839 cv = function(v){ return v === true || v === "true" || v == 1; };
11846 if(v instanceof Date){
11850 if(dateFormat == "timestamp"){
11851 return new Date(v*1000);
11853 return Date.parseDate(v, dateFormat);
11855 var parsed = Date.parse(v);
11856 return parsed ? new Date(parsed) : null;
11865 Roo.data.Field.prototype = {
11873 * Ext JS Library 1.1.1
11874 * Copyright(c) 2006-2007, Ext JS, LLC.
11876 * Originally Released Under LGPL - original licence link has changed is not relivant.
11879 * <script type="text/javascript">
11882 // Base class for reading structured data from a data source. This class is intended to be
11883 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11886 * @class Roo.data.DataReader
11887 * Base class for reading structured data from a data source. This class is intended to be
11888 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11891 Roo.data.DataReader = function(meta, recordType){
11895 this.recordType = recordType instanceof Array ?
11896 Roo.data.Record.create(recordType) : recordType;
11899 Roo.data.DataReader.prototype = {
11901 * Create an empty record
11902 * @param {Object} data (optional) - overlay some values
11903 * @return {Roo.data.Record} record created.
11905 newRow : function(d) {
11907 this.recordType.prototype.fields.each(function(c) {
11909 case 'int' : da[c.name] = 0; break;
11910 case 'date' : da[c.name] = new Date(); break;
11911 case 'float' : da[c.name] = 0.0; break;
11912 case 'boolean' : da[c.name] = false; break;
11913 default : da[c.name] = ""; break;
11917 return new this.recordType(Roo.apply(da, d));
11922 * Ext JS Library 1.1.1
11923 * Copyright(c) 2006-2007, Ext JS, LLC.
11925 * Originally Released Under LGPL - original licence link has changed is not relivant.
11928 * <script type="text/javascript">
11932 * @class Roo.data.DataProxy
11933 * @extends Roo.data.Observable
11934 * This class is an abstract base class for implementations which provide retrieval of
11935 * unformatted data objects.<br>
11937 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11938 * (of the appropriate type which knows how to parse the data object) to provide a block of
11939 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11941 * Custom implementations must implement the load method as described in
11942 * {@link Roo.data.HttpProxy#load}.
11944 Roo.data.DataProxy = function(){
11947 * @event beforeload
11948 * Fires before a network request is made to retrieve a data object.
11949 * @param {Object} This DataProxy object.
11950 * @param {Object} params The params parameter to the load function.
11955 * Fires before the load method's callback is called.
11956 * @param {Object} This DataProxy object.
11957 * @param {Object} o The data object.
11958 * @param {Object} arg The callback argument object passed to the load function.
11962 * @event loadexception
11963 * Fires if an Exception occurs during data retrieval.
11964 * @param {Object} This DataProxy object.
11965 * @param {Object} o The data object.
11966 * @param {Object} arg The callback argument object passed to the load function.
11967 * @param {Object} e The Exception.
11969 loadexception : true
11971 Roo.data.DataProxy.superclass.constructor.call(this);
11974 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11977 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11981 * Ext JS Library 1.1.1
11982 * Copyright(c) 2006-2007, Ext JS, LLC.
11984 * Originally Released Under LGPL - original licence link has changed is not relivant.
11987 * <script type="text/javascript">
11990 * @class Roo.data.MemoryProxy
11991 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11992 * to the Reader when its load method is called.
11994 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11996 Roo.data.MemoryProxy = function(data){
12000 Roo.data.MemoryProxy.superclass.constructor.call(this);
12004 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12007 * Load data from the requested source (in this case an in-memory
12008 * data object passed to the constructor), read the data object into
12009 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12010 * process that block using the passed callback.
12011 * @param {Object} params This parameter is not used by the MemoryProxy class.
12012 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12013 * object into a block of Roo.data.Records.
12014 * @param {Function} callback The function into which to pass the block of Roo.data.records.
12015 * The function must be passed <ul>
12016 * <li>The Record block object</li>
12017 * <li>The "arg" argument from the load function</li>
12018 * <li>A boolean success indicator</li>
12020 * @param {Object} scope The scope in which to call the callback
12021 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12023 load : function(params, reader, callback, scope, arg){
12024 params = params || {};
12027 result = reader.readRecords(this.data);
12029 this.fireEvent("loadexception", this, arg, null, e);
12030 callback.call(scope, null, arg, false);
12033 callback.call(scope, result, arg, true);
12037 update : function(params, records){
12042 * Ext JS Library 1.1.1
12043 * Copyright(c) 2006-2007, Ext JS, LLC.
12045 * Originally Released Under LGPL - original licence link has changed is not relivant.
12048 * <script type="text/javascript">
12051 * @class Roo.data.HttpProxy
12052 * @extends Roo.data.DataProxy
12053 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12054 * configured to reference a certain URL.<br><br>
12056 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12057 * from which the running page was served.<br><br>
12059 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12061 * Be aware that to enable the browser to parse an XML document, the server must set
12062 * the Content-Type header in the HTTP response to "text/xml".
12064 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12065 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12066 * will be used to make the request.
12068 Roo.data.HttpProxy = function(conn){
12069 Roo.data.HttpProxy.superclass.constructor.call(this);
12070 // is conn a conn config or a real conn?
12072 this.useAjax = !conn || !conn.events;
12076 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12077 // thse are take from connection...
12080 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12083 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12084 * extra parameters to each request made by this object. (defaults to undefined)
12087 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12088 * to each request made by this object. (defaults to undefined)
12091 * @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)
12094 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12097 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12103 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12107 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12108 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12109 * a finer-grained basis than the DataProxy events.
12111 getConnection : function(){
12112 return this.useAjax ? Roo.Ajax : this.conn;
12116 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12117 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12118 * process that block using the passed callback.
12119 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12120 * for the request to the remote server.
12121 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12122 * object into a block of Roo.data.Records.
12123 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12124 * The function must be passed <ul>
12125 * <li>The Record block object</li>
12126 * <li>The "arg" argument from the load function</li>
12127 * <li>A boolean success indicator</li>
12129 * @param {Object} scope The scope in which to call the callback
12130 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12132 load : function(params, reader, callback, scope, arg){
12133 if(this.fireEvent("beforeload", this, params) !== false){
12135 params : params || {},
12137 callback : callback,
12142 callback : this.loadResponse,
12146 Roo.applyIf(o, this.conn);
12147 if(this.activeRequest){
12148 Roo.Ajax.abort(this.activeRequest);
12150 this.activeRequest = Roo.Ajax.request(o);
12152 this.conn.request(o);
12155 callback.call(scope||this, null, arg, false);
12160 loadResponse : function(o, success, response){
12161 delete this.activeRequest;
12163 this.fireEvent("loadexception", this, o, response);
12164 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12169 result = o.reader.read(response);
12171 this.fireEvent("loadexception", this, o, response, e);
12172 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12176 this.fireEvent("load", this, o, o.request.arg);
12177 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12181 update : function(dataSet){
12186 updateResponse : function(dataSet){
12191 * Ext JS Library 1.1.1
12192 * Copyright(c) 2006-2007, Ext JS, LLC.
12194 * Originally Released Under LGPL - original licence link has changed is not relivant.
12197 * <script type="text/javascript">
12201 * @class Roo.data.ScriptTagProxy
12202 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12203 * other than the originating domain of the running page.<br><br>
12205 * <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
12206 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12208 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12209 * source code that is used as the source inside a <script> tag.<br><br>
12211 * In order for the browser to process the returned data, the server must wrap the data object
12212 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12213 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12214 * depending on whether the callback name was passed:
12217 boolean scriptTag = false;
12218 String cb = request.getParameter("callback");
12221 response.setContentType("text/javascript");
12223 response.setContentType("application/x-json");
12225 Writer out = response.getWriter();
12227 out.write(cb + "(");
12229 out.print(dataBlock.toJsonString());
12236 * @param {Object} config A configuration object.
12238 Roo.data.ScriptTagProxy = function(config){
12239 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12240 Roo.apply(this, config);
12241 this.head = document.getElementsByTagName("head")[0];
12244 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12246 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12248 * @cfg {String} url The URL from which to request the data object.
12251 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12255 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12256 * the server the name of the callback function set up by the load call to process the returned data object.
12257 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12258 * javascript output which calls this named function passing the data object as its only parameter.
12260 callbackParam : "callback",
12262 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12263 * name to the request.
12268 * Load data from the configured URL, read the data object into
12269 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12270 * process that block using the passed callback.
12271 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12272 * for the request to the remote server.
12273 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12274 * object into a block of Roo.data.Records.
12275 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12276 * The function must be passed <ul>
12277 * <li>The Record block object</li>
12278 * <li>The "arg" argument from the load function</li>
12279 * <li>A boolean success indicator</li>
12281 * @param {Object} scope The scope in which to call the callback
12282 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12284 load : function(params, reader, callback, scope, arg){
12285 if(this.fireEvent("beforeload", this, params) !== false){
12287 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12289 var url = this.url;
12290 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12292 url += "&_dc=" + (new Date().getTime());
12294 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12297 cb : "stcCallback"+transId,
12298 scriptId : "stcScript"+transId,
12302 callback : callback,
12308 window[trans.cb] = function(o){
12309 conn.handleResponse(o, trans);
12312 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12314 if(this.autoAbort !== false){
12318 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12320 var script = document.createElement("script");
12321 script.setAttribute("src", url);
12322 script.setAttribute("type", "text/javascript");
12323 script.setAttribute("id", trans.scriptId);
12324 this.head.appendChild(script);
12326 this.trans = trans;
12328 callback.call(scope||this, null, arg, false);
12333 isLoading : function(){
12334 return this.trans ? true : false;
12338 * Abort the current server request.
12340 abort : function(){
12341 if(this.isLoading()){
12342 this.destroyTrans(this.trans);
12347 destroyTrans : function(trans, isLoaded){
12348 this.head.removeChild(document.getElementById(trans.scriptId));
12349 clearTimeout(trans.timeoutId);
12351 window[trans.cb] = undefined;
12353 delete window[trans.cb];
12356 // if hasn't been loaded, wait for load to remove it to prevent script error
12357 window[trans.cb] = function(){
12358 window[trans.cb] = undefined;
12360 delete window[trans.cb];
12367 handleResponse : function(o, trans){
12368 this.trans = false;
12369 this.destroyTrans(trans, true);
12372 result = trans.reader.readRecords(o);
12374 this.fireEvent("loadexception", this, o, trans.arg, e);
12375 trans.callback.call(trans.scope||window, null, trans.arg, false);
12378 this.fireEvent("load", this, o, trans.arg);
12379 trans.callback.call(trans.scope||window, result, trans.arg, true);
12383 handleFailure : function(trans){
12384 this.trans = false;
12385 this.destroyTrans(trans, false);
12386 this.fireEvent("loadexception", this, null, trans.arg);
12387 trans.callback.call(trans.scope||window, null, trans.arg, false);
12391 * Ext JS Library 1.1.1
12392 * Copyright(c) 2006-2007, Ext JS, LLC.
12394 * Originally Released Under LGPL - original licence link has changed is not relivant.
12397 * <script type="text/javascript">
12401 * @class Roo.data.JsonReader
12402 * @extends Roo.data.DataReader
12403 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12404 * based on mappings in a provided Roo.data.Record constructor.
12406 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12407 * in the reply previously.
12412 var RecordDef = Roo.data.Record.create([
12413 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12414 {name: 'occupation'} // This field will use "occupation" as the mapping.
12416 var myReader = new Roo.data.JsonReader({
12417 totalProperty: "results", // The property which contains the total dataset size (optional)
12418 root: "rows", // The property which contains an Array of row objects
12419 id: "id" // The property within each row object that provides an ID for the record (optional)
12423 * This would consume a JSON file like this:
12425 { 'results': 2, 'rows': [
12426 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12427 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12430 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12431 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12432 * paged from the remote server.
12433 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12434 * @cfg {String} root name of the property which contains the Array of row objects.
12435 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12436 * @cfg {Array} fields Array of field definition objects
12438 * Create a new JsonReader
12439 * @param {Object} meta Metadata configuration options
12440 * @param {Object} recordType Either an Array of field definition objects,
12441 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12443 Roo.data.JsonReader = function(meta, recordType){
12446 // set some defaults:
12447 Roo.applyIf(meta, {
12448 totalProperty: 'total',
12449 successProperty : 'success',
12454 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12456 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12459 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12460 * Used by Store query builder to append _requestMeta to params.
12463 metaFromRemote : false,
12465 * This method is only used by a DataProxy which has retrieved data from a remote server.
12466 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12467 * @return {Object} data A data block which is used by an Roo.data.Store object as
12468 * a cache of Roo.data.Records.
12470 read : function(response){
12471 var json = response.responseText;
12473 var o = /* eval:var:o */ eval("("+json+")");
12475 throw {message: "JsonReader.read: Json object not found"};
12481 this.metaFromRemote = true;
12482 this.meta = o.metaData;
12483 this.recordType = Roo.data.Record.create(o.metaData.fields);
12484 this.onMetaChange(this.meta, this.recordType, o);
12486 return this.readRecords(o);
12489 // private function a store will implement
12490 onMetaChange : function(meta, recordType, o){
12497 simpleAccess: function(obj, subsc) {
12504 getJsonAccessor: function(){
12506 return function(expr) {
12508 return(re.test(expr))
12509 ? new Function("obj", "return obj." + expr)
12514 return Roo.emptyFn;
12519 * Create a data block containing Roo.data.Records from an XML document.
12520 * @param {Object} o An object which contains an Array of row objects in the property specified
12521 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12522 * which contains the total size of the dataset.
12523 * @return {Object} data A data block which is used by an Roo.data.Store object as
12524 * a cache of Roo.data.Records.
12526 readRecords : function(o){
12528 * After any data loads, the raw JSON data is available for further custom processing.
12532 var s = this.meta, Record = this.recordType,
12533 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12535 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12537 if(s.totalProperty) {
12538 this.getTotal = this.getJsonAccessor(s.totalProperty);
12540 if(s.successProperty) {
12541 this.getSuccess = this.getJsonAccessor(s.successProperty);
12543 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12545 var g = this.getJsonAccessor(s.id);
12546 this.getId = function(rec) {
12548 return (r === undefined || r === "") ? null : r;
12551 this.getId = function(){return null;};
12554 for(var jj = 0; jj < fl; jj++){
12556 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12557 this.ef[jj] = this.getJsonAccessor(map);
12561 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12562 if(s.totalProperty){
12563 var vt = parseInt(this.getTotal(o), 10);
12568 if(s.successProperty){
12569 var vs = this.getSuccess(o);
12570 if(vs === false || vs === 'false'){
12575 for(var i = 0; i < c; i++){
12578 var id = this.getId(n);
12579 for(var j = 0; j < fl; j++){
12581 var v = this.ef[j](n);
12583 Roo.log('missing convert for ' + f.name);
12587 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12589 var record = new Record(values, id);
12591 records[i] = record;
12597 totalRecords : totalRecords
12602 * Ext JS Library 1.1.1
12603 * Copyright(c) 2006-2007, Ext JS, LLC.
12605 * Originally Released Under LGPL - original licence link has changed is not relivant.
12608 * <script type="text/javascript">
12612 * @class Roo.data.ArrayReader
12613 * @extends Roo.data.DataReader
12614 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12615 * Each element of that Array represents a row of data fields. The
12616 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12617 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12621 var RecordDef = Roo.data.Record.create([
12622 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12623 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12625 var myReader = new Roo.data.ArrayReader({
12626 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12630 * This would consume an Array like this:
12632 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12634 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12636 * Create a new JsonReader
12637 * @param {Object} meta Metadata configuration options.
12638 * @param {Object} recordType Either an Array of field definition objects
12639 * as specified to {@link Roo.data.Record#create},
12640 * or an {@link Roo.data.Record} object
12641 * created using {@link Roo.data.Record#create}.
12643 Roo.data.ArrayReader = function(meta, recordType){
12644 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12647 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12649 * Create a data block containing Roo.data.Records from an XML document.
12650 * @param {Object} o An Array of row objects which represents the dataset.
12651 * @return {Object} data A data block which is used by an Roo.data.Store object as
12652 * a cache of Roo.data.Records.
12654 readRecords : function(o){
12655 var sid = this.meta ? this.meta.id : null;
12656 var recordType = this.recordType, fields = recordType.prototype.fields;
12659 for(var i = 0; i < root.length; i++){
12662 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12663 for(var j = 0, jlen = fields.length; j < jlen; j++){
12664 var f = fields.items[j];
12665 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12666 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12668 values[f.name] = v;
12670 var record = new recordType(values, id);
12672 records[records.length] = record;
12676 totalRecords : records.length
12685 * @class Roo.bootstrap.ComboBox
12686 * @extends Roo.bootstrap.TriggerField
12687 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12688 * @cfg {Boolean} append (true|false) default false
12689 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12690 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12691 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12692 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12693 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12694 * @cfg {Boolean} animate default true
12695 * @cfg {Boolean} emptyResultText only for touch device
12696 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12697 * @cfg {String} emptyTitle default ''
12699 * Create a new ComboBox.
12700 * @param {Object} config Configuration options
12702 Roo.bootstrap.ComboBox = function(config){
12703 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12707 * Fires when the dropdown list is expanded
12708 * @param {Roo.bootstrap.ComboBox} combo This combo box
12713 * Fires when the dropdown list is collapsed
12714 * @param {Roo.bootstrap.ComboBox} combo This combo box
12718 * @event beforeselect
12719 * Fires before a list item is selected. Return false to cancel the selection.
12720 * @param {Roo.bootstrap.ComboBox} combo This combo box
12721 * @param {Roo.data.Record} record The data record returned from the underlying store
12722 * @param {Number} index The index of the selected item in the dropdown list
12724 'beforeselect' : true,
12727 * Fires when a list item is selected
12728 * @param {Roo.bootstrap.ComboBox} combo This combo box
12729 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12730 * @param {Number} index The index of the selected item in the dropdown list
12734 * @event beforequery
12735 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12736 * The event object passed has these properties:
12737 * @param {Roo.bootstrap.ComboBox} combo This combo box
12738 * @param {String} query The query
12739 * @param {Boolean} forceAll true to force "all" query
12740 * @param {Boolean} cancel true to cancel the query
12741 * @param {Object} e The query event object
12743 'beforequery': true,
12746 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12747 * @param {Roo.bootstrap.ComboBox} combo This combo box
12752 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12753 * @param {Roo.bootstrap.ComboBox} combo This combo box
12754 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12759 * Fires when the remove value from the combobox array
12760 * @param {Roo.bootstrap.ComboBox} combo This combo box
12764 * @event afterremove
12765 * Fires when the remove value from the combobox array
12766 * @param {Roo.bootstrap.ComboBox} combo This combo box
12768 'afterremove' : true,
12770 * @event specialfilter
12771 * Fires when specialfilter
12772 * @param {Roo.bootstrap.ComboBox} combo This combo box
12774 'specialfilter' : true,
12777 * Fires when tick the element
12778 * @param {Roo.bootstrap.ComboBox} combo This combo box
12782 * @event touchviewdisplay
12783 * Fires when touch view require special display (default is using displayField)
12784 * @param {Roo.bootstrap.ComboBox} combo This combo box
12785 * @param {Object} cfg set html .
12787 'touchviewdisplay' : true
12792 this.tickItems = [];
12794 this.selectedIndex = -1;
12795 if(this.mode == 'local'){
12796 if(config.queryDelay === undefined){
12797 this.queryDelay = 10;
12799 if(config.minChars === undefined){
12805 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12808 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12809 * rendering into an Roo.Editor, defaults to false)
12812 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12813 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12816 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12819 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12820 * the dropdown list (defaults to undefined, with no header element)
12824 * @cfg {String/Roo.Template} tpl The template to use to render the output
12828 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12830 listWidth: undefined,
12832 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12833 * mode = 'remote' or 'text' if mode = 'local')
12835 displayField: undefined,
12838 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12839 * mode = 'remote' or 'value' if mode = 'local').
12840 * Note: use of a valueField requires the user make a selection
12841 * in order for a value to be mapped.
12843 valueField: undefined,
12845 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12850 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12851 * field's data value (defaults to the underlying DOM element's name)
12853 hiddenName: undefined,
12855 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12859 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12861 selectedClass: 'active',
12864 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12868 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12869 * anchor positions (defaults to 'tl-bl')
12871 listAlign: 'tl-bl?',
12873 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12877 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12878 * query specified by the allQuery config option (defaults to 'query')
12880 triggerAction: 'query',
12882 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12883 * (defaults to 4, does not apply if editable = false)
12887 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12888 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12892 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12893 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12897 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12898 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12902 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12903 * when editable = true (defaults to false)
12905 selectOnFocus:false,
12907 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12909 queryParam: 'query',
12911 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12912 * when mode = 'remote' (defaults to 'Loading...')
12914 loadingText: 'Loading...',
12916 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12920 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12924 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12925 * traditional select (defaults to true)
12929 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12933 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12937 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12938 * listWidth has a higher value)
12942 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12943 * allow the user to set arbitrary text into the field (defaults to false)
12945 forceSelection:false,
12947 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12948 * if typeAhead = true (defaults to 250)
12950 typeAheadDelay : 250,
12952 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12953 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12955 valueNotFoundText : undefined,
12957 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12959 blockFocus : false,
12962 * @cfg {Boolean} disableClear Disable showing of clear button.
12964 disableClear : false,
12966 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12968 alwaysQuery : false,
12971 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12976 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12978 invalidClass : "has-warning",
12981 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12983 validClass : "has-success",
12986 * @cfg {Boolean} specialFilter (true|false) special filter default false
12988 specialFilter : false,
12991 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12993 mobileTouchView : true,
12996 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12998 useNativeIOS : false,
13000 ios_options : false,
13012 btnPosition : 'right',
13013 triggerList : true,
13014 showToggleBtn : true,
13016 emptyResultText: 'Empty',
13017 triggerText : 'Select',
13020 // element that contains real text value.. (when hidden is used..)
13022 getAutoCreate : function()
13027 * Render classic select for iso
13030 if(Roo.isIOS && this.useNativeIOS){
13031 cfg = this.getAutoCreateNativeIOS();
13039 if(Roo.isTouch && this.mobileTouchView){
13040 cfg = this.getAutoCreateTouchView();
13047 if(!this.tickable){
13048 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13053 * ComboBox with tickable selections
13056 var align = this.labelAlign || this.parentLabelAlign();
13059 cls : 'form-group roo-combobox-tickable' //input-group
13062 var btn_text_select = '';
13063 var btn_text_done = '';
13064 var btn_text_cancel = '';
13066 if (this.btn_text_show) {
13067 btn_text_select = 'Select';
13068 btn_text_done = 'Done';
13069 btn_text_cancel = 'Cancel';
13074 cls : 'tickable-buttons',
13079 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13080 //html : this.triggerText
13081 html: btn_text_select
13087 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13089 html: btn_text_done
13095 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13097 html: btn_text_cancel
13103 buttons.cn.unshift({
13105 cls: 'roo-select2-search-field-input'
13111 Roo.each(buttons.cn, function(c){
13113 c.cls += ' btn-' + _this.size;
13116 if (_this.disabled) {
13127 cls: 'form-hidden-field'
13131 cls: 'roo-select2-choices',
13135 cls: 'roo-select2-search-field',
13146 cls: 'roo-select2-container input-group roo-select2-container-multi',
13151 // cls: 'typeahead typeahead-long dropdown-menu',
13152 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13157 if(this.hasFeedback && !this.allowBlank){
13161 cls: 'glyphicon form-control-feedback'
13164 combobox.cn.push(feedback);
13168 if (align ==='left' && this.fieldLabel.length) {
13170 cfg.cls += ' roo-form-group-label-left';
13175 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13176 tooltip : 'This field is required'
13181 cls : 'control-label',
13182 html : this.fieldLabel
13194 var labelCfg = cfg.cn[1];
13195 var contentCfg = cfg.cn[2];
13198 if(this.indicatorpos == 'right'){
13204 cls : 'control-label',
13208 html : this.fieldLabel
13212 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13213 tooltip : 'This field is required'
13228 labelCfg = cfg.cn[0];
13229 contentCfg = cfg.cn[1];
13233 if(this.labelWidth > 12){
13234 labelCfg.style = "width: " + this.labelWidth + 'px';
13237 if(this.labelWidth < 13 && this.labelmd == 0){
13238 this.labelmd = this.labelWidth;
13241 if(this.labellg > 0){
13242 labelCfg.cls += ' col-lg-' + this.labellg;
13243 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13246 if(this.labelmd > 0){
13247 labelCfg.cls += ' col-md-' + this.labelmd;
13248 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13251 if(this.labelsm > 0){
13252 labelCfg.cls += ' col-sm-' + this.labelsm;
13253 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13256 if(this.labelxs > 0){
13257 labelCfg.cls += ' col-xs-' + this.labelxs;
13258 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13262 } else if ( this.fieldLabel.length) {
13263 // Roo.log(" label");
13267 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13268 tooltip : 'This field is required'
13272 //cls : 'input-group-addon',
13273 html : this.fieldLabel
13278 if(this.indicatorpos == 'right'){
13282 //cls : 'input-group-addon',
13283 html : this.fieldLabel
13287 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13288 tooltip : 'This field is required'
13297 // Roo.log(" no label && no align");
13304 ['xs','sm','md','lg'].map(function(size){
13305 if (settings[size]) {
13306 cfg.cls += ' col-' + size + '-' + settings[size];
13314 _initEventsCalled : false,
13317 initEvents: function()
13319 if (this._initEventsCalled) { // as we call render... prevent looping...
13322 this._initEventsCalled = true;
13325 throw "can not find store for combo";
13328 this.indicator = this.indicatorEl();
13330 this.store = Roo.factory(this.store, Roo.data);
13331 this.store.parent = this;
13333 // if we are building from html. then this element is so complex, that we can not really
13334 // use the rendered HTML.
13335 // so we have to trash and replace the previous code.
13336 if (Roo.XComponent.build_from_html) {
13337 // remove this element....
13338 var e = this.el.dom, k=0;
13339 while (e ) { e = e.previousSibling; ++k;}
13344 this.rendered = false;
13346 this.render(this.parent().getChildContainer(true), k);
13349 if(Roo.isIOS && this.useNativeIOS){
13350 this.initIOSView();
13358 if(Roo.isTouch && this.mobileTouchView){
13359 this.initTouchView();
13364 this.initTickableEvents();
13368 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13370 if(this.hiddenName){
13372 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13374 this.hiddenField.dom.value =
13375 this.hiddenValue !== undefined ? this.hiddenValue :
13376 this.value !== undefined ? this.value : '';
13378 // prevent input submission
13379 this.el.dom.removeAttribute('name');
13380 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13385 // this.el.dom.setAttribute('autocomplete', 'off');
13388 var cls = 'x-combo-list';
13390 //this.list = new Roo.Layer({
13391 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13397 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13398 _this.list.setWidth(lw);
13401 this.list.on('mouseover', this.onViewOver, this);
13402 this.list.on('mousemove', this.onViewMove, this);
13403 this.list.on('scroll', this.onViewScroll, this);
13406 this.list.swallowEvent('mousewheel');
13407 this.assetHeight = 0;
13410 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13411 this.assetHeight += this.header.getHeight();
13414 this.innerList = this.list.createChild({cls:cls+'-inner'});
13415 this.innerList.on('mouseover', this.onViewOver, this);
13416 this.innerList.on('mousemove', this.onViewMove, this);
13417 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13419 if(this.allowBlank && !this.pageSize && !this.disableClear){
13420 this.footer = this.list.createChild({cls:cls+'-ft'});
13421 this.pageTb = new Roo.Toolbar(this.footer);
13425 this.footer = this.list.createChild({cls:cls+'-ft'});
13426 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13427 {pageSize: this.pageSize});
13431 if (this.pageTb && this.allowBlank && !this.disableClear) {
13433 this.pageTb.add(new Roo.Toolbar.Fill(), {
13434 cls: 'x-btn-icon x-btn-clear',
13436 handler: function()
13439 _this.clearValue();
13440 _this.onSelect(false, -1);
13445 this.assetHeight += this.footer.getHeight();
13450 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13453 this.view = new Roo.View(this.list, this.tpl, {
13454 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13456 //this.view.wrapEl.setDisplayed(false);
13457 this.view.on('click', this.onViewClick, this);
13460 this.store.on('beforeload', this.onBeforeLoad, this);
13461 this.store.on('load', this.onLoad, this);
13462 this.store.on('loadexception', this.onLoadException, this);
13464 if(this.resizable){
13465 this.resizer = new Roo.Resizable(this.list, {
13466 pinned:true, handles:'se'
13468 this.resizer.on('resize', function(r, w, h){
13469 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13470 this.listWidth = w;
13471 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13472 this.restrictHeight();
13474 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13477 if(!this.editable){
13478 this.editable = true;
13479 this.setEditable(false);
13484 if (typeof(this.events.add.listeners) != 'undefined') {
13486 this.addicon = this.wrap.createChild(
13487 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13489 this.addicon.on('click', function(e) {
13490 this.fireEvent('add', this);
13493 if (typeof(this.events.edit.listeners) != 'undefined') {
13495 this.editicon = this.wrap.createChild(
13496 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13497 if (this.addicon) {
13498 this.editicon.setStyle('margin-left', '40px');
13500 this.editicon.on('click', function(e) {
13502 // we fire even if inothing is selected..
13503 this.fireEvent('edit', this, this.lastData );
13509 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13510 "up" : function(e){
13511 this.inKeyMode = true;
13515 "down" : function(e){
13516 if(!this.isExpanded()){
13517 this.onTriggerClick();
13519 this.inKeyMode = true;
13524 "enter" : function(e){
13525 // this.onViewClick();
13529 if(this.fireEvent("specialkey", this, e)){
13530 this.onViewClick(false);
13536 "esc" : function(e){
13540 "tab" : function(e){
13543 if(this.fireEvent("specialkey", this, e)){
13544 this.onViewClick(false);
13552 doRelay : function(foo, bar, hname){
13553 if(hname == 'down' || this.scope.isExpanded()){
13554 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13563 this.queryDelay = Math.max(this.queryDelay || 10,
13564 this.mode == 'local' ? 10 : 250);
13567 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13569 if(this.typeAhead){
13570 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13572 if(this.editable !== false){
13573 this.inputEl().on("keyup", this.onKeyUp, this);
13575 if(this.forceSelection){
13576 this.inputEl().on('blur', this.doForce, this);
13580 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13581 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13585 initTickableEvents: function()
13589 if(this.hiddenName){
13591 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13593 this.hiddenField.dom.value =
13594 this.hiddenValue !== undefined ? this.hiddenValue :
13595 this.value !== undefined ? this.value : '';
13597 // prevent input submission
13598 this.el.dom.removeAttribute('name');
13599 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13604 // this.list = this.el.select('ul.dropdown-menu',true).first();
13606 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13607 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13608 if(this.triggerList){
13609 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13612 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13613 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13615 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13616 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13618 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13619 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13621 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13622 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13623 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13626 this.cancelBtn.hide();
13631 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13632 _this.list.setWidth(lw);
13635 this.list.on('mouseover', this.onViewOver, this);
13636 this.list.on('mousemove', this.onViewMove, this);
13638 this.list.on('scroll', this.onViewScroll, this);
13641 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13642 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13645 this.view = new Roo.View(this.list, this.tpl, {
13650 selectedClass: this.selectedClass
13653 //this.view.wrapEl.setDisplayed(false);
13654 this.view.on('click', this.onViewClick, this);
13658 this.store.on('beforeload', this.onBeforeLoad, this);
13659 this.store.on('load', this.onLoad, this);
13660 this.store.on('loadexception', this.onLoadException, this);
13663 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13664 "up" : function(e){
13665 this.inKeyMode = true;
13669 "down" : function(e){
13670 this.inKeyMode = true;
13674 "enter" : function(e){
13675 if(this.fireEvent("specialkey", this, e)){
13676 this.onViewClick(false);
13682 "esc" : function(e){
13683 this.onTickableFooterButtonClick(e, false, false);
13686 "tab" : function(e){
13687 this.fireEvent("specialkey", this, e);
13689 this.onTickableFooterButtonClick(e, false, false);
13696 doRelay : function(e, fn, key){
13697 if(this.scope.isExpanded()){
13698 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13707 this.queryDelay = Math.max(this.queryDelay || 10,
13708 this.mode == 'local' ? 10 : 250);
13711 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13713 if(this.typeAhead){
13714 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13717 if(this.editable !== false){
13718 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13721 this.indicator = this.indicatorEl();
13723 if(this.indicator){
13724 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13725 this.indicator.hide();
13730 onDestroy : function(){
13732 this.view.setStore(null);
13733 this.view.el.removeAllListeners();
13734 this.view.el.remove();
13735 this.view.purgeListeners();
13738 this.list.dom.innerHTML = '';
13742 this.store.un('beforeload', this.onBeforeLoad, this);
13743 this.store.un('load', this.onLoad, this);
13744 this.store.un('loadexception', this.onLoadException, this);
13746 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13750 fireKey : function(e){
13751 if(e.isNavKeyPress() && !this.list.isVisible()){
13752 this.fireEvent("specialkey", this, e);
13757 onResize: function(w, h){
13758 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13760 // if(typeof w != 'number'){
13761 // // we do not handle it!?!?
13764 // var tw = this.trigger.getWidth();
13765 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13766 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13768 // this.inputEl().setWidth( this.adjustWidth('input', x));
13770 // //this.trigger.setStyle('left', x+'px');
13772 // if(this.list && this.listWidth === undefined){
13773 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13774 // this.list.setWidth(lw);
13775 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13783 * Allow or prevent the user from directly editing the field text. If false is passed,
13784 * the user will only be able to select from the items defined in the dropdown list. This method
13785 * is the runtime equivalent of setting the 'editable' config option at config time.
13786 * @param {Boolean} value True to allow the user to directly edit the field text
13788 setEditable : function(value){
13789 if(value == this.editable){
13792 this.editable = value;
13794 this.inputEl().dom.setAttribute('readOnly', true);
13795 this.inputEl().on('mousedown', this.onTriggerClick, this);
13796 this.inputEl().addClass('x-combo-noedit');
13798 this.inputEl().dom.setAttribute('readOnly', false);
13799 this.inputEl().un('mousedown', this.onTriggerClick, this);
13800 this.inputEl().removeClass('x-combo-noedit');
13806 onBeforeLoad : function(combo,opts){
13807 if(!this.hasFocus){
13811 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13813 this.restrictHeight();
13814 this.selectedIndex = -1;
13818 onLoad : function(){
13820 this.hasQuery = false;
13822 if(!this.hasFocus){
13826 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13827 this.loading.hide();
13830 if(this.store.getCount() > 0){
13833 this.restrictHeight();
13834 if(this.lastQuery == this.allQuery){
13835 if(this.editable && !this.tickable){
13836 this.inputEl().dom.select();
13840 !this.selectByValue(this.value, true) &&
13843 !this.store.lastOptions ||
13844 typeof(this.store.lastOptions.add) == 'undefined' ||
13845 this.store.lastOptions.add != true
13848 this.select(0, true);
13851 if(this.autoFocus){
13854 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13855 this.taTask.delay(this.typeAheadDelay);
13859 this.onEmptyResults();
13865 onLoadException : function()
13867 this.hasQuery = false;
13869 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13870 this.loading.hide();
13873 if(this.tickable && this.editable){
13878 // only causes errors at present
13879 //Roo.log(this.store.reader.jsonData);
13880 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13882 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13888 onTypeAhead : function(){
13889 if(this.store.getCount() > 0){
13890 var r = this.store.getAt(0);
13891 var newValue = r.data[this.displayField];
13892 var len = newValue.length;
13893 var selStart = this.getRawValue().length;
13895 if(selStart != len){
13896 this.setRawValue(newValue);
13897 this.selectText(selStart, newValue.length);
13903 onSelect : function(record, index){
13905 if(this.fireEvent('beforeselect', this, record, index) !== false){
13907 this.setFromData(index > -1 ? record.data : false);
13910 this.fireEvent('select', this, record, index);
13915 * Returns the currently selected field value or empty string if no value is set.
13916 * @return {String} value The selected value
13918 getValue : function()
13920 if(Roo.isIOS && this.useNativeIOS){
13921 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13925 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13928 if(this.valueField){
13929 return typeof this.value != 'undefined' ? this.value : '';
13931 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13935 getRawValue : function()
13937 if(Roo.isIOS && this.useNativeIOS){
13938 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13941 var v = this.inputEl().getValue();
13947 * Clears any text/value currently set in the field
13949 clearValue : function(){
13951 if(this.hiddenField){
13952 this.hiddenField.dom.value = '';
13955 this.setRawValue('');
13956 this.lastSelectionText = '';
13957 this.lastData = false;
13959 var close = this.closeTriggerEl();
13970 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13971 * will be displayed in the field. If the value does not match the data value of an existing item,
13972 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13973 * Otherwise the field will be blank (although the value will still be set).
13974 * @param {String} value The value to match
13976 setValue : function(v)
13978 if(Roo.isIOS && this.useNativeIOS){
13979 this.setIOSValue(v);
13989 if(this.valueField){
13990 var r = this.findRecord(this.valueField, v);
13992 text = r.data[this.displayField];
13993 }else if(this.valueNotFoundText !== undefined){
13994 text = this.valueNotFoundText;
13997 this.lastSelectionText = text;
13998 if(this.hiddenField){
13999 this.hiddenField.dom.value = v;
14001 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14004 var close = this.closeTriggerEl();
14007 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14013 * @property {Object} the last set data for the element
14018 * Sets the value of the field based on a object which is related to the record format for the store.
14019 * @param {Object} value the value to set as. or false on reset?
14021 setFromData : function(o){
14028 var dv = ''; // display value
14029 var vv = ''; // value value..
14031 if (this.displayField) {
14032 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14034 // this is an error condition!!!
14035 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14038 if(this.valueField){
14039 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14042 var close = this.closeTriggerEl();
14045 if(dv.length || vv * 1 > 0){
14047 this.blockFocus=true;
14053 if(this.hiddenField){
14054 this.hiddenField.dom.value = vv;
14056 this.lastSelectionText = dv;
14057 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14061 // no hidden field.. - we store the value in 'value', but still display
14062 // display field!!!!
14063 this.lastSelectionText = dv;
14064 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14071 reset : function(){
14072 // overridden so that last data is reset..
14079 this.setValue(this.originalValue);
14080 //this.clearInvalid();
14081 this.lastData = false;
14083 this.view.clearSelections();
14089 findRecord : function(prop, value){
14091 if(this.store.getCount() > 0){
14092 this.store.each(function(r){
14093 if(r.data[prop] == value){
14103 getName: function()
14105 // returns hidden if it's set..
14106 if (!this.rendered) {return ''};
14107 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14111 onViewMove : function(e, t){
14112 this.inKeyMode = false;
14116 onViewOver : function(e, t){
14117 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14120 var item = this.view.findItemFromChild(t);
14123 var index = this.view.indexOf(item);
14124 this.select(index, false);
14129 onViewClick : function(view, doFocus, el, e)
14131 var index = this.view.getSelectedIndexes()[0];
14133 var r = this.store.getAt(index);
14137 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14144 Roo.each(this.tickItems, function(v,k){
14146 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14148 _this.tickItems.splice(k, 1);
14150 if(typeof(e) == 'undefined' && view == false){
14151 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14163 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14164 this.tickItems.push(r.data);
14167 if(typeof(e) == 'undefined' && view == false){
14168 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14175 this.onSelect(r, index);
14177 if(doFocus !== false && !this.blockFocus){
14178 this.inputEl().focus();
14183 restrictHeight : function(){
14184 //this.innerList.dom.style.height = '';
14185 //var inner = this.innerList.dom;
14186 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14187 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14188 //this.list.beginUpdate();
14189 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14190 this.list.alignTo(this.inputEl(), this.listAlign);
14191 this.list.alignTo(this.inputEl(), this.listAlign);
14192 //this.list.endUpdate();
14196 onEmptyResults : function(){
14198 if(this.tickable && this.editable){
14199 this.hasFocus = false;
14200 this.restrictHeight();
14208 * Returns true if the dropdown list is expanded, else false.
14210 isExpanded : function(){
14211 return this.list.isVisible();
14215 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14216 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14217 * @param {String} value The data value of the item to select
14218 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14219 * selected item if it is not currently in view (defaults to true)
14220 * @return {Boolean} True if the value matched an item in the list, else false
14222 selectByValue : function(v, scrollIntoView){
14223 if(v !== undefined && v !== null){
14224 var r = this.findRecord(this.valueField || this.displayField, v);
14226 this.select(this.store.indexOf(r), scrollIntoView);
14234 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14235 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14236 * @param {Number} index The zero-based index of the list item to select
14237 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14238 * selected item if it is not currently in view (defaults to true)
14240 select : function(index, scrollIntoView){
14241 this.selectedIndex = index;
14242 this.view.select(index);
14243 if(scrollIntoView !== false){
14244 var el = this.view.getNode(index);
14246 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14249 this.list.scrollChildIntoView(el, false);
14255 selectNext : function(){
14256 var ct = this.store.getCount();
14258 if(this.selectedIndex == -1){
14260 }else if(this.selectedIndex < ct-1){
14261 this.select(this.selectedIndex+1);
14267 selectPrev : function(){
14268 var ct = this.store.getCount();
14270 if(this.selectedIndex == -1){
14272 }else if(this.selectedIndex != 0){
14273 this.select(this.selectedIndex-1);
14279 onKeyUp : function(e){
14280 if(this.editable !== false && !e.isSpecialKey()){
14281 this.lastKey = e.getKey();
14282 this.dqTask.delay(this.queryDelay);
14287 validateBlur : function(){
14288 return !this.list || !this.list.isVisible();
14292 initQuery : function(){
14294 var v = this.getRawValue();
14296 if(this.tickable && this.editable){
14297 v = this.tickableInputEl().getValue();
14304 doForce : function(){
14305 if(this.inputEl().dom.value.length > 0){
14306 this.inputEl().dom.value =
14307 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14313 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14314 * query allowing the query action to be canceled if needed.
14315 * @param {String} query The SQL query to execute
14316 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14317 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14318 * saved in the current store (defaults to false)
14320 doQuery : function(q, forceAll){
14322 if(q === undefined || q === null){
14327 forceAll: forceAll,
14331 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14336 forceAll = qe.forceAll;
14337 if(forceAll === true || (q.length >= this.minChars)){
14339 this.hasQuery = true;
14341 if(this.lastQuery != q || this.alwaysQuery){
14342 this.lastQuery = q;
14343 if(this.mode == 'local'){
14344 this.selectedIndex = -1;
14346 this.store.clearFilter();
14349 if(this.specialFilter){
14350 this.fireEvent('specialfilter', this);
14355 this.store.filter(this.displayField, q);
14358 this.store.fireEvent("datachanged", this.store);
14365 this.store.baseParams[this.queryParam] = q;
14367 var options = {params : this.getParams(q)};
14370 options.add = true;
14371 options.params.start = this.page * this.pageSize;
14374 this.store.load(options);
14377 * this code will make the page width larger, at the beginning, the list not align correctly,
14378 * we should expand the list on onLoad
14379 * so command out it
14384 this.selectedIndex = -1;
14389 this.loadNext = false;
14393 getParams : function(q){
14395 //p[this.queryParam] = q;
14399 p.limit = this.pageSize;
14405 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14407 collapse : function(){
14408 if(!this.isExpanded()){
14414 this.hasFocus = false;
14418 this.cancelBtn.hide();
14419 this.trigger.show();
14422 this.tickableInputEl().dom.value = '';
14423 this.tickableInputEl().blur();
14428 Roo.get(document).un('mousedown', this.collapseIf, this);
14429 Roo.get(document).un('mousewheel', this.collapseIf, this);
14430 if (!this.editable) {
14431 Roo.get(document).un('keydown', this.listKeyPress, this);
14433 this.fireEvent('collapse', this);
14439 collapseIf : function(e){
14440 var in_combo = e.within(this.el);
14441 var in_list = e.within(this.list);
14442 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14444 if (in_combo || in_list || is_list) {
14445 //e.stopPropagation();
14450 this.onTickableFooterButtonClick(e, false, false);
14458 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14460 expand : function(){
14462 if(this.isExpanded() || !this.hasFocus){
14466 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14467 this.list.setWidth(lw);
14473 this.restrictHeight();
14477 this.tickItems = Roo.apply([], this.item);
14480 this.cancelBtn.show();
14481 this.trigger.hide();
14484 this.tickableInputEl().focus();
14489 Roo.get(document).on('mousedown', this.collapseIf, this);
14490 Roo.get(document).on('mousewheel', this.collapseIf, this);
14491 if (!this.editable) {
14492 Roo.get(document).on('keydown', this.listKeyPress, this);
14495 this.fireEvent('expand', this);
14499 // Implements the default empty TriggerField.onTriggerClick function
14500 onTriggerClick : function(e)
14502 Roo.log('trigger click');
14504 if(this.disabled || !this.triggerList){
14509 this.loadNext = false;
14511 if(this.isExpanded()){
14513 if (!this.blockFocus) {
14514 this.inputEl().focus();
14518 this.hasFocus = true;
14519 if(this.triggerAction == 'all') {
14520 this.doQuery(this.allQuery, true);
14522 this.doQuery(this.getRawValue());
14524 if (!this.blockFocus) {
14525 this.inputEl().focus();
14530 onTickableTriggerClick : function(e)
14537 this.loadNext = false;
14538 this.hasFocus = true;
14540 if(this.triggerAction == 'all') {
14541 this.doQuery(this.allQuery, true);
14543 this.doQuery(this.getRawValue());
14547 onSearchFieldClick : function(e)
14549 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14550 this.onTickableFooterButtonClick(e, false, false);
14554 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14559 this.loadNext = false;
14560 this.hasFocus = true;
14562 if(this.triggerAction == 'all') {
14563 this.doQuery(this.allQuery, true);
14565 this.doQuery(this.getRawValue());
14569 listKeyPress : function(e)
14571 //Roo.log('listkeypress');
14572 // scroll to first matching element based on key pres..
14573 if (e.isSpecialKey()) {
14576 var k = String.fromCharCode(e.getKey()).toUpperCase();
14579 var csel = this.view.getSelectedNodes();
14580 var cselitem = false;
14582 var ix = this.view.indexOf(csel[0]);
14583 cselitem = this.store.getAt(ix);
14584 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14590 this.store.each(function(v) {
14592 // start at existing selection.
14593 if (cselitem.id == v.id) {
14599 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14600 match = this.store.indexOf(v);
14606 if (match === false) {
14607 return true; // no more action?
14610 this.view.select(match);
14611 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14612 sn.scrollIntoView(sn.dom.parentNode, false);
14615 onViewScroll : function(e, t){
14617 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){
14621 this.hasQuery = true;
14623 this.loading = this.list.select('.loading', true).first();
14625 if(this.loading === null){
14626 this.list.createChild({
14628 cls: 'loading roo-select2-more-results roo-select2-active',
14629 html: 'Loading more results...'
14632 this.loading = this.list.select('.loading', true).first();
14634 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14636 this.loading.hide();
14639 this.loading.show();
14644 this.loadNext = true;
14646 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14651 addItem : function(o)
14653 var dv = ''; // display value
14655 if (this.displayField) {
14656 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14658 // this is an error condition!!!
14659 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14666 var choice = this.choices.createChild({
14668 cls: 'roo-select2-search-choice',
14677 cls: 'roo-select2-search-choice-close fa fa-times',
14682 }, this.searchField);
14684 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14686 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14694 this.inputEl().dom.value = '';
14699 onRemoveItem : function(e, _self, o)
14701 e.preventDefault();
14703 this.lastItem = Roo.apply([], this.item);
14705 var index = this.item.indexOf(o.data) * 1;
14708 Roo.log('not this item?!');
14712 this.item.splice(index, 1);
14717 this.fireEvent('remove', this, e);
14723 syncValue : function()
14725 if(!this.item.length){
14732 Roo.each(this.item, function(i){
14733 if(_this.valueField){
14734 value.push(i[_this.valueField]);
14741 this.value = value.join(',');
14743 if(this.hiddenField){
14744 this.hiddenField.dom.value = this.value;
14747 this.store.fireEvent("datachanged", this.store);
14752 clearItem : function()
14754 if(!this.multiple){
14760 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14768 if(this.tickable && !Roo.isTouch){
14769 this.view.refresh();
14773 inputEl: function ()
14775 if(Roo.isIOS && this.useNativeIOS){
14776 return this.el.select('select.roo-ios-select', true).first();
14779 if(Roo.isTouch && this.mobileTouchView){
14780 return this.el.select('input.form-control',true).first();
14784 return this.searchField;
14787 return this.el.select('input.form-control',true).first();
14790 onTickableFooterButtonClick : function(e, btn, el)
14792 e.preventDefault();
14794 this.lastItem = Roo.apply([], this.item);
14796 if(btn && btn.name == 'cancel'){
14797 this.tickItems = Roo.apply([], this.item);
14806 Roo.each(this.tickItems, function(o){
14814 validate : function()
14816 if(this.getVisibilityEl().hasClass('hidden')){
14820 var v = this.getRawValue();
14823 v = this.getValue();
14826 if(this.disabled || this.allowBlank || v.length){
14831 this.markInvalid();
14835 tickableInputEl : function()
14837 if(!this.tickable || !this.editable){
14838 return this.inputEl();
14841 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14845 getAutoCreateTouchView : function()
14850 cls: 'form-group' //input-group
14856 type : this.inputType,
14857 cls : 'form-control x-combo-noedit',
14858 autocomplete: 'new-password',
14859 placeholder : this.placeholder || '',
14864 input.name = this.name;
14868 input.cls += ' input-' + this.size;
14871 if (this.disabled) {
14872 input.disabled = true;
14883 inputblock.cls += ' input-group';
14885 inputblock.cn.unshift({
14887 cls : 'input-group-addon',
14892 if(this.removable && !this.multiple){
14893 inputblock.cls += ' roo-removable';
14895 inputblock.cn.push({
14898 cls : 'roo-combo-removable-btn close'
14902 if(this.hasFeedback && !this.allowBlank){
14904 inputblock.cls += ' has-feedback';
14906 inputblock.cn.push({
14908 cls: 'glyphicon form-control-feedback'
14915 inputblock.cls += (this.before) ? '' : ' input-group';
14917 inputblock.cn.push({
14919 cls : 'input-group-addon',
14930 cls: 'form-hidden-field'
14944 cls: 'form-hidden-field'
14948 cls: 'roo-select2-choices',
14952 cls: 'roo-select2-search-field',
14965 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14971 if(!this.multiple && this.showToggleBtn){
14978 if (this.caret != false) {
14981 cls: 'fa fa-' + this.caret
14988 cls : 'input-group-addon btn dropdown-toggle',
14993 cls: 'combobox-clear',
15007 combobox.cls += ' roo-select2-container-multi';
15010 var align = this.labelAlign || this.parentLabelAlign();
15012 if (align ==='left' && this.fieldLabel.length) {
15017 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15018 tooltip : 'This field is required'
15022 cls : 'control-label',
15023 html : this.fieldLabel
15034 var labelCfg = cfg.cn[1];
15035 var contentCfg = cfg.cn[2];
15038 if(this.indicatorpos == 'right'){
15043 cls : 'control-label',
15047 html : this.fieldLabel
15051 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15052 tooltip : 'This field is required'
15065 labelCfg = cfg.cn[0];
15066 contentCfg = cfg.cn[1];
15071 if(this.labelWidth > 12){
15072 labelCfg.style = "width: " + this.labelWidth + 'px';
15075 if(this.labelWidth < 13 && this.labelmd == 0){
15076 this.labelmd = this.labelWidth;
15079 if(this.labellg > 0){
15080 labelCfg.cls += ' col-lg-' + this.labellg;
15081 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15084 if(this.labelmd > 0){
15085 labelCfg.cls += ' col-md-' + this.labelmd;
15086 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15089 if(this.labelsm > 0){
15090 labelCfg.cls += ' col-sm-' + this.labelsm;
15091 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15094 if(this.labelxs > 0){
15095 labelCfg.cls += ' col-xs-' + this.labelxs;
15096 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15100 } else if ( this.fieldLabel.length) {
15104 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15105 tooltip : 'This field is required'
15109 cls : 'control-label',
15110 html : this.fieldLabel
15121 if(this.indicatorpos == 'right'){
15125 cls : 'control-label',
15126 html : this.fieldLabel,
15130 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15131 tooltip : 'This field is required'
15148 var settings = this;
15150 ['xs','sm','md','lg'].map(function(size){
15151 if (settings[size]) {
15152 cfg.cls += ' col-' + size + '-' + settings[size];
15159 initTouchView : function()
15161 this.renderTouchView();
15163 this.touchViewEl.on('scroll', function(){
15164 this.el.dom.scrollTop = 0;
15167 this.originalValue = this.getValue();
15169 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15171 this.inputEl().on("click", this.showTouchView, this);
15172 if (this.triggerEl) {
15173 this.triggerEl.on("click", this.showTouchView, this);
15177 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15178 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15180 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15182 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15183 this.store.on('load', this.onTouchViewLoad, this);
15184 this.store.on('loadexception', this.onTouchViewLoadException, this);
15186 if(this.hiddenName){
15188 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15190 this.hiddenField.dom.value =
15191 this.hiddenValue !== undefined ? this.hiddenValue :
15192 this.value !== undefined ? this.value : '';
15194 this.el.dom.removeAttribute('name');
15195 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15199 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15200 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15203 if(this.removable && !this.multiple){
15204 var close = this.closeTriggerEl();
15206 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15207 close.on('click', this.removeBtnClick, this, close);
15211 * fix the bug in Safari iOS8
15213 this.inputEl().on("focus", function(e){
15214 document.activeElement.blur();
15222 renderTouchView : function()
15224 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15225 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15227 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15228 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15230 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15231 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15232 this.touchViewBodyEl.setStyle('overflow', 'auto');
15234 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15235 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15237 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15238 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15242 showTouchView : function()
15248 this.touchViewHeaderEl.hide();
15250 if(this.modalTitle.length){
15251 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15252 this.touchViewHeaderEl.show();
15255 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15256 this.touchViewEl.show();
15258 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15260 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15261 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15263 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15265 if(this.modalTitle.length){
15266 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15269 this.touchViewBodyEl.setHeight(bodyHeight);
15273 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15275 this.touchViewEl.addClass('in');
15278 this.doTouchViewQuery();
15282 hideTouchView : function()
15284 this.touchViewEl.removeClass('in');
15288 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15290 this.touchViewEl.setStyle('display', 'none');
15295 setTouchViewValue : function()
15302 Roo.each(this.tickItems, function(o){
15307 this.hideTouchView();
15310 doTouchViewQuery : function()
15319 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15323 if(!this.alwaysQuery || this.mode == 'local'){
15324 this.onTouchViewLoad();
15331 onTouchViewBeforeLoad : function(combo,opts)
15337 onTouchViewLoad : function()
15339 if(this.store.getCount() < 1){
15340 this.onTouchViewEmptyResults();
15344 this.clearTouchView();
15346 var rawValue = this.getRawValue();
15348 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15350 this.tickItems = [];
15352 this.store.data.each(function(d, rowIndex){
15353 var row = this.touchViewListGroup.createChild(template);
15355 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15356 row.addClass(d.data.cls);
15359 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15362 html : d.data[this.displayField]
15365 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15366 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15369 row.removeClass('selected');
15370 if(!this.multiple && this.valueField &&
15371 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15374 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15375 row.addClass('selected');
15378 if(this.multiple && this.valueField &&
15379 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15383 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15384 this.tickItems.push(d.data);
15387 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15391 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15393 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15395 if(this.modalTitle.length){
15396 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15399 var listHeight = this.touchViewListGroup.getHeight();
15403 if(firstChecked && listHeight > bodyHeight){
15404 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15409 onTouchViewLoadException : function()
15411 this.hideTouchView();
15414 onTouchViewEmptyResults : function()
15416 this.clearTouchView();
15418 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15420 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15424 clearTouchView : function()
15426 this.touchViewListGroup.dom.innerHTML = '';
15429 onTouchViewClick : function(e, el, o)
15431 e.preventDefault();
15434 var rowIndex = o.rowIndex;
15436 var r = this.store.getAt(rowIndex);
15438 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15440 if(!this.multiple){
15441 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15442 c.dom.removeAttribute('checked');
15445 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15447 this.setFromData(r.data);
15449 var close = this.closeTriggerEl();
15455 this.hideTouchView();
15457 this.fireEvent('select', this, r, rowIndex);
15462 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15463 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15464 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15468 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15469 this.addItem(r.data);
15470 this.tickItems.push(r.data);
15474 getAutoCreateNativeIOS : function()
15477 cls: 'form-group' //input-group,
15482 cls : 'roo-ios-select'
15486 combobox.name = this.name;
15489 if (this.disabled) {
15490 combobox.disabled = true;
15493 var settings = this;
15495 ['xs','sm','md','lg'].map(function(size){
15496 if (settings[size]) {
15497 cfg.cls += ' col-' + size + '-' + settings[size];
15507 initIOSView : function()
15509 this.store.on('load', this.onIOSViewLoad, this);
15514 onIOSViewLoad : function()
15516 if(this.store.getCount() < 1){
15520 this.clearIOSView();
15522 if(this.allowBlank) {
15524 var default_text = '-- SELECT --';
15526 if(this.placeholder.length){
15527 default_text = this.placeholder;
15530 if(this.emptyTitle.length){
15531 default_text += ' - ' + this.emptyTitle + ' -';
15534 var opt = this.inputEl().createChild({
15537 html : default_text
15541 o[this.valueField] = 0;
15542 o[this.displayField] = default_text;
15544 this.ios_options.push({
15551 this.store.data.each(function(d, rowIndex){
15555 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15556 html = d.data[this.displayField];
15561 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15562 value = d.data[this.valueField];
15571 if(this.value == d.data[this.valueField]){
15572 option['selected'] = true;
15575 var opt = this.inputEl().createChild(option);
15577 this.ios_options.push({
15584 this.inputEl().on('change', function(){
15585 this.fireEvent('select', this);
15590 clearIOSView: function()
15592 this.inputEl().dom.innerHTML = '';
15594 this.ios_options = [];
15597 setIOSValue: function(v)
15601 if(!this.ios_options){
15605 Roo.each(this.ios_options, function(opts){
15607 opts.el.dom.removeAttribute('selected');
15609 if(opts.data[this.valueField] != v){
15613 opts.el.dom.setAttribute('selected', true);
15619 * @cfg {Boolean} grow
15623 * @cfg {Number} growMin
15627 * @cfg {Number} growMax
15636 Roo.apply(Roo.bootstrap.ComboBox, {
15640 cls: 'modal-header',
15662 cls: 'list-group-item',
15666 cls: 'roo-combobox-list-group-item-value'
15670 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15684 listItemCheckbox : {
15686 cls: 'list-group-item',
15690 cls: 'roo-combobox-list-group-item-value'
15694 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15710 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15715 cls: 'modal-footer',
15723 cls: 'col-xs-6 text-left',
15726 cls: 'btn btn-danger roo-touch-view-cancel',
15732 cls: 'col-xs-6 text-right',
15735 cls: 'btn btn-success roo-touch-view-ok',
15746 Roo.apply(Roo.bootstrap.ComboBox, {
15748 touchViewTemplate : {
15750 cls: 'modal fade roo-combobox-touch-view',
15754 cls: 'modal-dialog',
15755 style : 'position:fixed', // we have to fix position....
15759 cls: 'modal-content',
15761 Roo.bootstrap.ComboBox.header,
15762 Roo.bootstrap.ComboBox.body,
15763 Roo.bootstrap.ComboBox.footer
15772 * Ext JS Library 1.1.1
15773 * Copyright(c) 2006-2007, Ext JS, LLC.
15775 * Originally Released Under LGPL - original licence link has changed is not relivant.
15778 * <script type="text/javascript">
15783 * @extends Roo.util.Observable
15784 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15785 * This class also supports single and multi selection modes. <br>
15786 * Create a data model bound view:
15788 var store = new Roo.data.Store(...);
15790 var view = new Roo.View({
15792 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15794 singleSelect: true,
15795 selectedClass: "ydataview-selected",
15799 // listen for node click?
15800 view.on("click", function(vw, index, node, e){
15801 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15805 dataModel.load("foobar.xml");
15807 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15809 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15810 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15812 * Note: old style constructor is still suported (container, template, config)
15815 * Create a new View
15816 * @param {Object} config The config object
15819 Roo.View = function(config, depreciated_tpl, depreciated_config){
15821 this.parent = false;
15823 if (typeof(depreciated_tpl) == 'undefined') {
15824 // new way.. - universal constructor.
15825 Roo.apply(this, config);
15826 this.el = Roo.get(this.el);
15829 this.el = Roo.get(config);
15830 this.tpl = depreciated_tpl;
15831 Roo.apply(this, depreciated_config);
15833 this.wrapEl = this.el.wrap().wrap();
15834 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15837 if(typeof(this.tpl) == "string"){
15838 this.tpl = new Roo.Template(this.tpl);
15840 // support xtype ctors..
15841 this.tpl = new Roo.factory(this.tpl, Roo);
15845 this.tpl.compile();
15850 * @event beforeclick
15851 * Fires before a click is processed. Returns false to cancel the default action.
15852 * @param {Roo.View} this
15853 * @param {Number} index The index of the target node
15854 * @param {HTMLElement} node The target node
15855 * @param {Roo.EventObject} e The raw event object
15857 "beforeclick" : true,
15860 * Fires when a template node is clicked.
15861 * @param {Roo.View} this
15862 * @param {Number} index The index of the target node
15863 * @param {HTMLElement} node The target node
15864 * @param {Roo.EventObject} e The raw event object
15869 * Fires when a template node is double clicked.
15870 * @param {Roo.View} this
15871 * @param {Number} index The index of the target node
15872 * @param {HTMLElement} node The target node
15873 * @param {Roo.EventObject} e The raw event object
15877 * @event contextmenu
15878 * Fires when a template node is right clicked.
15879 * @param {Roo.View} this
15880 * @param {Number} index The index of the target node
15881 * @param {HTMLElement} node The target node
15882 * @param {Roo.EventObject} e The raw event object
15884 "contextmenu" : true,
15886 * @event selectionchange
15887 * Fires when the selected nodes change.
15888 * @param {Roo.View} this
15889 * @param {Array} selections Array of the selected nodes
15891 "selectionchange" : true,
15894 * @event beforeselect
15895 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15896 * @param {Roo.View} this
15897 * @param {HTMLElement} node The node to be selected
15898 * @param {Array} selections Array of currently selected nodes
15900 "beforeselect" : true,
15902 * @event preparedata
15903 * Fires on every row to render, to allow you to change the data.
15904 * @param {Roo.View} this
15905 * @param {Object} data to be rendered (change this)
15907 "preparedata" : true
15915 "click": this.onClick,
15916 "dblclick": this.onDblClick,
15917 "contextmenu": this.onContextMenu,
15921 this.selections = [];
15923 this.cmp = new Roo.CompositeElementLite([]);
15925 this.store = Roo.factory(this.store, Roo.data);
15926 this.setStore(this.store, true);
15929 if ( this.footer && this.footer.xtype) {
15931 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15933 this.footer.dataSource = this.store;
15934 this.footer.container = fctr;
15935 this.footer = Roo.factory(this.footer, Roo);
15936 fctr.insertFirst(this.el);
15938 // this is a bit insane - as the paging toolbar seems to detach the el..
15939 // dom.parentNode.parentNode.parentNode
15940 // they get detached?
15944 Roo.View.superclass.constructor.call(this);
15949 Roo.extend(Roo.View, Roo.util.Observable, {
15952 * @cfg {Roo.data.Store} store Data store to load data from.
15957 * @cfg {String|Roo.Element} el The container element.
15962 * @cfg {String|Roo.Template} tpl The template used by this View
15966 * @cfg {String} dataName the named area of the template to use as the data area
15967 * Works with domtemplates roo-name="name"
15971 * @cfg {String} selectedClass The css class to add to selected nodes
15973 selectedClass : "x-view-selected",
15975 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15980 * @cfg {String} text to display on mask (default Loading)
15984 * @cfg {Boolean} multiSelect Allow multiple selection
15986 multiSelect : false,
15988 * @cfg {Boolean} singleSelect Allow single selection
15990 singleSelect: false,
15993 * @cfg {Boolean} toggleSelect - selecting
15995 toggleSelect : false,
15998 * @cfg {Boolean} tickable - selecting
16003 * Returns the element this view is bound to.
16004 * @return {Roo.Element}
16006 getEl : function(){
16007 return this.wrapEl;
16013 * Refreshes the view. - called by datachanged on the store. - do not call directly.
16015 refresh : function(){
16016 //Roo.log('refresh');
16019 // if we are using something like 'domtemplate', then
16020 // the what gets used is:
16021 // t.applySubtemplate(NAME, data, wrapping data..)
16022 // the outer template then get' applied with
16023 // the store 'extra data'
16024 // and the body get's added to the
16025 // roo-name="data" node?
16026 // <span class='roo-tpl-{name}'></span> ?????
16030 this.clearSelections();
16031 this.el.update("");
16033 var records = this.store.getRange();
16034 if(records.length < 1) {
16036 // is this valid?? = should it render a template??
16038 this.el.update(this.emptyText);
16042 if (this.dataName) {
16043 this.el.update(t.apply(this.store.meta)); //????
16044 el = this.el.child('.roo-tpl-' + this.dataName);
16047 for(var i = 0, len = records.length; i < len; i++){
16048 var data = this.prepareData(records[i].data, i, records[i]);
16049 this.fireEvent("preparedata", this, data, i, records[i]);
16051 var d = Roo.apply({}, data);
16054 Roo.apply(d, {'roo-id' : Roo.id()});
16058 Roo.each(this.parent.item, function(item){
16059 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16062 Roo.apply(d, {'roo-data-checked' : 'checked'});
16066 html[html.length] = Roo.util.Format.trim(
16068 t.applySubtemplate(this.dataName, d, this.store.meta) :
16075 el.update(html.join(""));
16076 this.nodes = el.dom.childNodes;
16077 this.updateIndexes(0);
16082 * Function to override to reformat the data that is sent to
16083 * the template for each node.
16084 * DEPRICATED - use the preparedata event handler.
16085 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16086 * a JSON object for an UpdateManager bound view).
16088 prepareData : function(data, index, record)
16090 this.fireEvent("preparedata", this, data, index, record);
16094 onUpdate : function(ds, record){
16095 // Roo.log('on update');
16096 this.clearSelections();
16097 var index = this.store.indexOf(record);
16098 var n = this.nodes[index];
16099 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16100 n.parentNode.removeChild(n);
16101 this.updateIndexes(index, index);
16107 onAdd : function(ds, records, index)
16109 //Roo.log(['on Add', ds, records, index] );
16110 this.clearSelections();
16111 if(this.nodes.length == 0){
16115 var n = this.nodes[index];
16116 for(var i = 0, len = records.length; i < len; i++){
16117 var d = this.prepareData(records[i].data, i, records[i]);
16119 this.tpl.insertBefore(n, d);
16122 this.tpl.append(this.el, d);
16125 this.updateIndexes(index);
16128 onRemove : function(ds, record, index){
16129 // Roo.log('onRemove');
16130 this.clearSelections();
16131 var el = this.dataName ?
16132 this.el.child('.roo-tpl-' + this.dataName) :
16135 el.dom.removeChild(this.nodes[index]);
16136 this.updateIndexes(index);
16140 * Refresh an individual node.
16141 * @param {Number} index
16143 refreshNode : function(index){
16144 this.onUpdate(this.store, this.store.getAt(index));
16147 updateIndexes : function(startIndex, endIndex){
16148 var ns = this.nodes;
16149 startIndex = startIndex || 0;
16150 endIndex = endIndex || ns.length - 1;
16151 for(var i = startIndex; i <= endIndex; i++){
16152 ns[i].nodeIndex = i;
16157 * Changes the data store this view uses and refresh the view.
16158 * @param {Store} store
16160 setStore : function(store, initial){
16161 if(!initial && this.store){
16162 this.store.un("datachanged", this.refresh);
16163 this.store.un("add", this.onAdd);
16164 this.store.un("remove", this.onRemove);
16165 this.store.un("update", this.onUpdate);
16166 this.store.un("clear", this.refresh);
16167 this.store.un("beforeload", this.onBeforeLoad);
16168 this.store.un("load", this.onLoad);
16169 this.store.un("loadexception", this.onLoad);
16173 store.on("datachanged", this.refresh, this);
16174 store.on("add", this.onAdd, this);
16175 store.on("remove", this.onRemove, this);
16176 store.on("update", this.onUpdate, this);
16177 store.on("clear", this.refresh, this);
16178 store.on("beforeload", this.onBeforeLoad, this);
16179 store.on("load", this.onLoad, this);
16180 store.on("loadexception", this.onLoad, this);
16188 * onbeforeLoad - masks the loading area.
16191 onBeforeLoad : function(store,opts)
16193 //Roo.log('onBeforeLoad');
16195 this.el.update("");
16197 this.el.mask(this.mask ? this.mask : "Loading" );
16199 onLoad : function ()
16206 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16207 * @param {HTMLElement} node
16208 * @return {HTMLElement} The template node
16210 findItemFromChild : function(node){
16211 var el = this.dataName ?
16212 this.el.child('.roo-tpl-' + this.dataName,true) :
16215 if(!node || node.parentNode == el){
16218 var p = node.parentNode;
16219 while(p && p != el){
16220 if(p.parentNode == el){
16229 onClick : function(e){
16230 var item = this.findItemFromChild(e.getTarget());
16232 var index = this.indexOf(item);
16233 if(this.onItemClick(item, index, e) !== false){
16234 this.fireEvent("click", this, index, item, e);
16237 this.clearSelections();
16242 onContextMenu : function(e){
16243 var item = this.findItemFromChild(e.getTarget());
16245 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16250 onDblClick : function(e){
16251 var item = this.findItemFromChild(e.getTarget());
16253 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16257 onItemClick : function(item, index, e)
16259 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16262 if (this.toggleSelect) {
16263 var m = this.isSelected(item) ? 'unselect' : 'select';
16266 _t[m](item, true, false);
16269 if(this.multiSelect || this.singleSelect){
16270 if(this.multiSelect && e.shiftKey && this.lastSelection){
16271 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16273 this.select(item, this.multiSelect && e.ctrlKey);
16274 this.lastSelection = item;
16277 if(!this.tickable){
16278 e.preventDefault();
16286 * Get the number of selected nodes.
16289 getSelectionCount : function(){
16290 return this.selections.length;
16294 * Get the currently selected nodes.
16295 * @return {Array} An array of HTMLElements
16297 getSelectedNodes : function(){
16298 return this.selections;
16302 * Get the indexes of the selected nodes.
16305 getSelectedIndexes : function(){
16306 var indexes = [], s = this.selections;
16307 for(var i = 0, len = s.length; i < len; i++){
16308 indexes.push(s[i].nodeIndex);
16314 * Clear all selections
16315 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16317 clearSelections : function(suppressEvent){
16318 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16319 this.cmp.elements = this.selections;
16320 this.cmp.removeClass(this.selectedClass);
16321 this.selections = [];
16322 if(!suppressEvent){
16323 this.fireEvent("selectionchange", this, this.selections);
16329 * Returns true if the passed node is selected
16330 * @param {HTMLElement/Number} node The node or node index
16331 * @return {Boolean}
16333 isSelected : function(node){
16334 var s = this.selections;
16338 node = this.getNode(node);
16339 return s.indexOf(node) !== -1;
16344 * @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
16345 * @param {Boolean} keepExisting (optional) true to keep existing selections
16346 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16348 select : function(nodeInfo, keepExisting, suppressEvent){
16349 if(nodeInfo instanceof Array){
16351 this.clearSelections(true);
16353 for(var i = 0, len = nodeInfo.length; i < len; i++){
16354 this.select(nodeInfo[i], true, true);
16358 var node = this.getNode(nodeInfo);
16359 if(!node || this.isSelected(node)){
16360 return; // already selected.
16363 this.clearSelections(true);
16366 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16367 Roo.fly(node).addClass(this.selectedClass);
16368 this.selections.push(node);
16369 if(!suppressEvent){
16370 this.fireEvent("selectionchange", this, this.selections);
16378 * @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
16379 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16380 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16382 unselect : function(nodeInfo, keepExisting, suppressEvent)
16384 if(nodeInfo instanceof Array){
16385 Roo.each(this.selections, function(s) {
16386 this.unselect(s, nodeInfo);
16390 var node = this.getNode(nodeInfo);
16391 if(!node || !this.isSelected(node)){
16392 //Roo.log("not selected");
16393 return; // not selected.
16397 Roo.each(this.selections, function(s) {
16399 Roo.fly(node).removeClass(this.selectedClass);
16406 this.selections= ns;
16407 this.fireEvent("selectionchange", this, this.selections);
16411 * Gets a template node.
16412 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16413 * @return {HTMLElement} The node or null if it wasn't found
16415 getNode : function(nodeInfo){
16416 if(typeof nodeInfo == "string"){
16417 return document.getElementById(nodeInfo);
16418 }else if(typeof nodeInfo == "number"){
16419 return this.nodes[nodeInfo];
16425 * Gets a range template nodes.
16426 * @param {Number} startIndex
16427 * @param {Number} endIndex
16428 * @return {Array} An array of nodes
16430 getNodes : function(start, end){
16431 var ns = this.nodes;
16432 start = start || 0;
16433 end = typeof end == "undefined" ? ns.length - 1 : end;
16436 for(var i = start; i <= end; i++){
16440 for(var i = start; i >= end; i--){
16448 * Finds the index of the passed node
16449 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16450 * @return {Number} The index of the node or -1
16452 indexOf : function(node){
16453 node = this.getNode(node);
16454 if(typeof node.nodeIndex == "number"){
16455 return node.nodeIndex;
16457 var ns = this.nodes;
16458 for(var i = 0, len = ns.length; i < len; i++){
16469 * based on jquery fullcalendar
16473 Roo.bootstrap = Roo.bootstrap || {};
16475 * @class Roo.bootstrap.Calendar
16476 * @extends Roo.bootstrap.Component
16477 * Bootstrap Calendar class
16478 * @cfg {Boolean} loadMask (true|false) default false
16479 * @cfg {Object} header generate the user specific header of the calendar, default false
16482 * Create a new Container
16483 * @param {Object} config The config object
16488 Roo.bootstrap.Calendar = function(config){
16489 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16493 * Fires when a date is selected
16494 * @param {DatePicker} this
16495 * @param {Date} date The selected date
16499 * @event monthchange
16500 * Fires when the displayed month changes
16501 * @param {DatePicker} this
16502 * @param {Date} date The selected month
16504 'monthchange': true,
16506 * @event evententer
16507 * Fires when mouse over an event
16508 * @param {Calendar} this
16509 * @param {event} Event
16511 'evententer': true,
16513 * @event eventleave
16514 * Fires when the mouse leaves an
16515 * @param {Calendar} this
16518 'eventleave': true,
16520 * @event eventclick
16521 * Fires when the mouse click an
16522 * @param {Calendar} this
16531 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16534 * @cfg {Number} startDay
16535 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16543 getAutoCreate : function(){
16546 var fc_button = function(name, corner, style, content ) {
16547 return Roo.apply({},{
16549 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16551 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16554 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16565 style : 'width:100%',
16572 cls : 'fc-header-left',
16574 fc_button('prev', 'left', 'arrow', '‹' ),
16575 fc_button('next', 'right', 'arrow', '›' ),
16576 { tag: 'span', cls: 'fc-header-space' },
16577 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16585 cls : 'fc-header-center',
16589 cls: 'fc-header-title',
16592 html : 'month / year'
16600 cls : 'fc-header-right',
16602 /* fc_button('month', 'left', '', 'month' ),
16603 fc_button('week', '', '', 'week' ),
16604 fc_button('day', 'right', '', 'day' )
16616 header = this.header;
16619 var cal_heads = function() {
16621 // fixme - handle this.
16623 for (var i =0; i < Date.dayNames.length; i++) {
16624 var d = Date.dayNames[i];
16627 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16628 html : d.substring(0,3)
16632 ret[0].cls += ' fc-first';
16633 ret[6].cls += ' fc-last';
16636 var cal_cell = function(n) {
16639 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16644 cls: 'fc-day-number',
16648 cls: 'fc-day-content',
16652 style: 'position: relative;' // height: 17px;
16664 var cal_rows = function() {
16667 for (var r = 0; r < 6; r++) {
16674 for (var i =0; i < Date.dayNames.length; i++) {
16675 var d = Date.dayNames[i];
16676 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16679 row.cn[0].cls+=' fc-first';
16680 row.cn[0].cn[0].style = 'min-height:90px';
16681 row.cn[6].cls+=' fc-last';
16685 ret[0].cls += ' fc-first';
16686 ret[4].cls += ' fc-prev-last';
16687 ret[5].cls += ' fc-last';
16694 cls: 'fc-border-separate',
16695 style : 'width:100%',
16703 cls : 'fc-first fc-last',
16721 cls : 'fc-content',
16722 style : "position: relative;",
16725 cls : 'fc-view fc-view-month fc-grid',
16726 style : 'position: relative',
16727 unselectable : 'on',
16730 cls : 'fc-event-container',
16731 style : 'position:absolute;z-index:8;top:0;left:0;'
16749 initEvents : function()
16752 throw "can not find store for calendar";
16758 style: "text-align:center",
16762 style: "background-color:white;width:50%;margin:250 auto",
16766 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16777 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16779 var size = this.el.select('.fc-content', true).first().getSize();
16780 this.maskEl.setSize(size.width, size.height);
16781 this.maskEl.enableDisplayMode("block");
16782 if(!this.loadMask){
16783 this.maskEl.hide();
16786 this.store = Roo.factory(this.store, Roo.data);
16787 this.store.on('load', this.onLoad, this);
16788 this.store.on('beforeload', this.onBeforeLoad, this);
16792 this.cells = this.el.select('.fc-day',true);
16793 //Roo.log(this.cells);
16794 this.textNodes = this.el.query('.fc-day-number');
16795 this.cells.addClassOnOver('fc-state-hover');
16797 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16798 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16799 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16800 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16802 this.on('monthchange', this.onMonthChange, this);
16804 this.update(new Date().clearTime());
16807 resize : function() {
16808 var sz = this.el.getSize();
16810 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16811 this.el.select('.fc-day-content div',true).setHeight(34);
16816 showPrevMonth : function(e){
16817 this.update(this.activeDate.add("mo", -1));
16819 showToday : function(e){
16820 this.update(new Date().clearTime());
16823 showNextMonth : function(e){
16824 this.update(this.activeDate.add("mo", 1));
16828 showPrevYear : function(){
16829 this.update(this.activeDate.add("y", -1));
16833 showNextYear : function(){
16834 this.update(this.activeDate.add("y", 1));
16839 update : function(date)
16841 var vd = this.activeDate;
16842 this.activeDate = date;
16843 // if(vd && this.el){
16844 // var t = date.getTime();
16845 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16846 // Roo.log('using add remove');
16848 // this.fireEvent('monthchange', this, date);
16850 // this.cells.removeClass("fc-state-highlight");
16851 // this.cells.each(function(c){
16852 // if(c.dateValue == t){
16853 // c.addClass("fc-state-highlight");
16854 // setTimeout(function(){
16855 // try{c.dom.firstChild.focus();}catch(e){}
16865 var days = date.getDaysInMonth();
16867 var firstOfMonth = date.getFirstDateOfMonth();
16868 var startingPos = firstOfMonth.getDay()-this.startDay;
16870 if(startingPos < this.startDay){
16874 var pm = date.add(Date.MONTH, -1);
16875 var prevStart = pm.getDaysInMonth()-startingPos;
16877 this.cells = this.el.select('.fc-day',true);
16878 this.textNodes = this.el.query('.fc-day-number');
16879 this.cells.addClassOnOver('fc-state-hover');
16881 var cells = this.cells.elements;
16882 var textEls = this.textNodes;
16884 Roo.each(cells, function(cell){
16885 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16888 days += startingPos;
16890 // convert everything to numbers so it's fast
16891 var day = 86400000;
16892 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16895 //Roo.log(prevStart);
16897 var today = new Date().clearTime().getTime();
16898 var sel = date.clearTime().getTime();
16899 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16900 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16901 var ddMatch = this.disabledDatesRE;
16902 var ddText = this.disabledDatesText;
16903 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16904 var ddaysText = this.disabledDaysText;
16905 var format = this.format;
16907 var setCellClass = function(cal, cell){
16911 //Roo.log('set Cell Class');
16913 var t = d.getTime();
16917 cell.dateValue = t;
16919 cell.className += " fc-today";
16920 cell.className += " fc-state-highlight";
16921 cell.title = cal.todayText;
16924 // disable highlight in other month..
16925 //cell.className += " fc-state-highlight";
16930 cell.className = " fc-state-disabled";
16931 cell.title = cal.minText;
16935 cell.className = " fc-state-disabled";
16936 cell.title = cal.maxText;
16940 if(ddays.indexOf(d.getDay()) != -1){
16941 cell.title = ddaysText;
16942 cell.className = " fc-state-disabled";
16945 if(ddMatch && format){
16946 var fvalue = d.dateFormat(format);
16947 if(ddMatch.test(fvalue)){
16948 cell.title = ddText.replace("%0", fvalue);
16949 cell.className = " fc-state-disabled";
16953 if (!cell.initialClassName) {
16954 cell.initialClassName = cell.dom.className;
16957 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16962 for(; i < startingPos; i++) {
16963 textEls[i].innerHTML = (++prevStart);
16964 d.setDate(d.getDate()+1);
16966 cells[i].className = "fc-past fc-other-month";
16967 setCellClass(this, cells[i]);
16972 for(; i < days; i++){
16973 intDay = i - startingPos + 1;
16974 textEls[i].innerHTML = (intDay);
16975 d.setDate(d.getDate()+1);
16977 cells[i].className = ''; // "x-date-active";
16978 setCellClass(this, cells[i]);
16982 for(; i < 42; i++) {
16983 textEls[i].innerHTML = (++extraDays);
16984 d.setDate(d.getDate()+1);
16986 cells[i].className = "fc-future fc-other-month";
16987 setCellClass(this, cells[i]);
16990 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16992 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16994 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16995 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16997 if(totalRows != 6){
16998 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16999 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17002 this.fireEvent('monthchange', this, date);
17006 if(!this.internalRender){
17007 var main = this.el.dom.firstChild;
17008 var w = main.offsetWidth;
17009 this.el.setWidth(w + this.el.getBorderWidth("lr"));
17010 Roo.fly(main).setWidth(w);
17011 this.internalRender = true;
17012 // opera does not respect the auto grow header center column
17013 // then, after it gets a width opera refuses to recalculate
17014 // without a second pass
17015 if(Roo.isOpera && !this.secondPass){
17016 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17017 this.secondPass = true;
17018 this.update.defer(10, this, [date]);
17025 findCell : function(dt) {
17026 dt = dt.clearTime().getTime();
17028 this.cells.each(function(c){
17029 //Roo.log("check " +c.dateValue + '?=' + dt);
17030 if(c.dateValue == dt){
17040 findCells : function(ev) {
17041 var s = ev.start.clone().clearTime().getTime();
17043 var e= ev.end.clone().clearTime().getTime();
17046 this.cells.each(function(c){
17047 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17049 if(c.dateValue > e){
17052 if(c.dateValue < s){
17061 // findBestRow: function(cells)
17065 // for (var i =0 ; i < cells.length;i++) {
17066 // ret = Math.max(cells[i].rows || 0,ret);
17073 addItem : function(ev)
17075 // look for vertical location slot in
17076 var cells = this.findCells(ev);
17078 // ev.row = this.findBestRow(cells);
17080 // work out the location.
17084 for(var i =0; i < cells.length; i++) {
17086 cells[i].row = cells[0].row;
17089 cells[i].row = cells[i].row + 1;
17099 if (crow.start.getY() == cells[i].getY()) {
17101 crow.end = cells[i];
17118 cells[0].events.push(ev);
17120 this.calevents.push(ev);
17123 clearEvents: function() {
17125 if(!this.calevents){
17129 Roo.each(this.cells.elements, function(c){
17135 Roo.each(this.calevents, function(e) {
17136 Roo.each(e.els, function(el) {
17137 el.un('mouseenter' ,this.onEventEnter, this);
17138 el.un('mouseleave' ,this.onEventLeave, this);
17143 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17149 renderEvents: function()
17153 this.cells.each(function(c) {
17162 if(c.row != c.events.length){
17163 r = 4 - (4 - (c.row - c.events.length));
17166 c.events = ev.slice(0, r);
17167 c.more = ev.slice(r);
17169 if(c.more.length && c.more.length == 1){
17170 c.events.push(c.more.pop());
17173 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17177 this.cells.each(function(c) {
17179 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17182 for (var e = 0; e < c.events.length; e++){
17183 var ev = c.events[e];
17184 var rows = ev.rows;
17186 for(var i = 0; i < rows.length; i++) {
17188 // how many rows should it span..
17191 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17192 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17194 unselectable : "on",
17197 cls: 'fc-event-inner',
17201 // cls: 'fc-event-time',
17202 // html : cells.length > 1 ? '' : ev.time
17206 cls: 'fc-event-title',
17207 html : String.format('{0}', ev.title)
17214 cls: 'ui-resizable-handle ui-resizable-e',
17215 html : '  '
17222 cfg.cls += ' fc-event-start';
17224 if ((i+1) == rows.length) {
17225 cfg.cls += ' fc-event-end';
17228 var ctr = _this.el.select('.fc-event-container',true).first();
17229 var cg = ctr.createChild(cfg);
17231 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17232 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17234 var r = (c.more.length) ? 1 : 0;
17235 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17236 cg.setWidth(ebox.right - sbox.x -2);
17238 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17239 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17240 cg.on('click', _this.onEventClick, _this, ev);
17251 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17252 style : 'position: absolute',
17253 unselectable : "on",
17256 cls: 'fc-event-inner',
17260 cls: 'fc-event-title',
17268 cls: 'ui-resizable-handle ui-resizable-e',
17269 html : '  '
17275 var ctr = _this.el.select('.fc-event-container',true).first();
17276 var cg = ctr.createChild(cfg);
17278 var sbox = c.select('.fc-day-content',true).first().getBox();
17279 var ebox = c.select('.fc-day-content',true).first().getBox();
17281 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17282 cg.setWidth(ebox.right - sbox.x -2);
17284 cg.on('click', _this.onMoreEventClick, _this, c.more);
17294 onEventEnter: function (e, el,event,d) {
17295 this.fireEvent('evententer', this, el, event);
17298 onEventLeave: function (e, el,event,d) {
17299 this.fireEvent('eventleave', this, el, event);
17302 onEventClick: function (e, el,event,d) {
17303 this.fireEvent('eventclick', this, el, event);
17306 onMonthChange: function () {
17310 onMoreEventClick: function(e, el, more)
17314 this.calpopover.placement = 'right';
17315 this.calpopover.setTitle('More');
17317 this.calpopover.setContent('');
17319 var ctr = this.calpopover.el.select('.popover-content', true).first();
17321 Roo.each(more, function(m){
17323 cls : 'fc-event-hori fc-event-draggable',
17326 var cg = ctr.createChild(cfg);
17328 cg.on('click', _this.onEventClick, _this, m);
17331 this.calpopover.show(el);
17336 onLoad: function ()
17338 this.calevents = [];
17341 if(this.store.getCount() > 0){
17342 this.store.data.each(function(d){
17345 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17346 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17347 time : d.data.start_time,
17348 title : d.data.title,
17349 description : d.data.description,
17350 venue : d.data.venue
17355 this.renderEvents();
17357 if(this.calevents.length && this.loadMask){
17358 this.maskEl.hide();
17362 onBeforeLoad: function()
17364 this.clearEvents();
17366 this.maskEl.show();
17380 * @class Roo.bootstrap.Popover
17381 * @extends Roo.bootstrap.Component
17382 * Bootstrap Popover class
17383 * @cfg {String} html contents of the popover (or false to use children..)
17384 * @cfg {String} title of popover (or false to hide)
17385 * @cfg {String} placement how it is placed
17386 * @cfg {String} trigger click || hover (or false to trigger manually)
17387 * @cfg {String} over what (parent or false to trigger manually.)
17388 * @cfg {Number} delay - delay before showing
17391 * Create a new Popover
17392 * @param {Object} config The config object
17395 Roo.bootstrap.Popover = function(config){
17396 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17402 * After the popover show
17404 * @param {Roo.bootstrap.Popover} this
17409 * After the popover hide
17411 * @param {Roo.bootstrap.Popover} this
17417 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17419 title: 'Fill in a title',
17422 placement : 'right',
17423 trigger : 'hover', // hover
17429 can_build_overlaid : false,
17431 getChildContainer : function()
17433 return this.el.select('.popover-content',true).first();
17436 getAutoCreate : function(){
17439 cls : 'popover roo-dynamic',
17440 style: 'display:block',
17446 cls : 'popover-inner',
17450 cls: 'popover-title',
17454 cls : 'popover-content',
17465 setTitle: function(str)
17468 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17470 setContent: function(str)
17473 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17475 // as it get's added to the bottom of the page.
17476 onRender : function(ct, position)
17478 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17480 var cfg = Roo.apply({}, this.getAutoCreate());
17484 cfg.cls += ' ' + this.cls;
17487 cfg.style = this.style;
17489 //Roo.log("adding to ");
17490 this.el = Roo.get(document.body).createChild(cfg, position);
17491 // Roo.log(this.el);
17496 initEvents : function()
17498 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17499 this.el.enableDisplayMode('block');
17501 if (this.over === false) {
17504 if (this.triggers === false) {
17507 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17508 var triggers = this.trigger ? this.trigger.split(' ') : [];
17509 Roo.each(triggers, function(trigger) {
17511 if (trigger == 'click') {
17512 on_el.on('click', this.toggle, this);
17513 } else if (trigger != 'manual') {
17514 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17515 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17517 on_el.on(eventIn ,this.enter, this);
17518 on_el.on(eventOut, this.leave, this);
17529 toggle : function () {
17530 this.hoverState == 'in' ? this.leave() : this.enter();
17533 enter : function () {
17535 clearTimeout(this.timeout);
17537 this.hoverState = 'in';
17539 if (!this.delay || !this.delay.show) {
17544 this.timeout = setTimeout(function () {
17545 if (_t.hoverState == 'in') {
17548 }, this.delay.show)
17551 leave : function() {
17552 clearTimeout(this.timeout);
17554 this.hoverState = 'out';
17556 if (!this.delay || !this.delay.hide) {
17561 this.timeout = setTimeout(function () {
17562 if (_t.hoverState == 'out') {
17565 }, this.delay.hide)
17568 show : function (on_el)
17571 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17575 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17576 if (this.html !== false) {
17577 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17579 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17580 if (!this.title.length) {
17581 this.el.select('.popover-title',true).hide();
17584 var placement = typeof this.placement == 'function' ?
17585 this.placement.call(this, this.el, on_el) :
17588 var autoToken = /\s?auto?\s?/i;
17589 var autoPlace = autoToken.test(placement);
17591 placement = placement.replace(autoToken, '') || 'top';
17595 //this.el.setXY([0,0]);
17597 this.el.dom.style.display='block';
17598 this.el.addClass(placement);
17600 //this.el.appendTo(on_el);
17602 var p = this.getPosition();
17603 var box = this.el.getBox();
17608 var align = Roo.bootstrap.Popover.alignment[placement];
17611 this.el.alignTo(on_el, align[0],align[1]);
17612 //var arrow = this.el.select('.arrow',true).first();
17613 //arrow.set(align[2],
17615 this.el.addClass('in');
17618 if (this.el.hasClass('fade')) {
17622 this.hoverState = 'in';
17624 this.fireEvent('show', this);
17629 this.el.setXY([0,0]);
17630 this.el.removeClass('in');
17632 this.hoverState = null;
17634 this.fireEvent('hide', this);
17639 Roo.bootstrap.Popover.alignment = {
17640 'left' : ['r-l', [-10,0], 'right'],
17641 'right' : ['l-r', [10,0], 'left'],
17642 'bottom' : ['t-b', [0,10], 'top'],
17643 'top' : [ 'b-t', [0,-10], 'bottom']
17654 * @class Roo.bootstrap.Progress
17655 * @extends Roo.bootstrap.Component
17656 * Bootstrap Progress class
17657 * @cfg {Boolean} striped striped of the progress bar
17658 * @cfg {Boolean} active animated of the progress bar
17662 * Create a new Progress
17663 * @param {Object} config The config object
17666 Roo.bootstrap.Progress = function(config){
17667 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17670 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17675 getAutoCreate : function(){
17683 cfg.cls += ' progress-striped';
17687 cfg.cls += ' active';
17706 * @class Roo.bootstrap.ProgressBar
17707 * @extends Roo.bootstrap.Component
17708 * Bootstrap ProgressBar class
17709 * @cfg {Number} aria_valuenow aria-value now
17710 * @cfg {Number} aria_valuemin aria-value min
17711 * @cfg {Number} aria_valuemax aria-value max
17712 * @cfg {String} label label for the progress bar
17713 * @cfg {String} panel (success | info | warning | danger )
17714 * @cfg {String} role role of the progress bar
17715 * @cfg {String} sr_only text
17719 * Create a new ProgressBar
17720 * @param {Object} config The config object
17723 Roo.bootstrap.ProgressBar = function(config){
17724 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17727 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17731 aria_valuemax : 100,
17737 getAutoCreate : function()
17742 cls: 'progress-bar',
17743 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17755 cfg.role = this.role;
17758 if(this.aria_valuenow){
17759 cfg['aria-valuenow'] = this.aria_valuenow;
17762 if(this.aria_valuemin){
17763 cfg['aria-valuemin'] = this.aria_valuemin;
17766 if(this.aria_valuemax){
17767 cfg['aria-valuemax'] = this.aria_valuemax;
17770 if(this.label && !this.sr_only){
17771 cfg.html = this.label;
17775 cfg.cls += ' progress-bar-' + this.panel;
17781 update : function(aria_valuenow)
17783 this.aria_valuenow = aria_valuenow;
17785 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17800 * @class Roo.bootstrap.TabGroup
17801 * @extends Roo.bootstrap.Column
17802 * Bootstrap Column class
17803 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17804 * @cfg {Boolean} carousel true to make the group behave like a carousel
17805 * @cfg {Boolean} bullets show bullets for the panels
17806 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17807 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17808 * @cfg {Boolean} showarrow (true|false) show arrow default true
17811 * Create a new TabGroup
17812 * @param {Object} config The config object
17815 Roo.bootstrap.TabGroup = function(config){
17816 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17818 this.navId = Roo.id();
17821 Roo.bootstrap.TabGroup.register(this);
17825 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17828 transition : false,
17833 slideOnTouch : false,
17836 getAutoCreate : function()
17838 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17840 cfg.cls += ' tab-content';
17842 if (this.carousel) {
17843 cfg.cls += ' carousel slide';
17846 cls : 'carousel-inner',
17850 if(this.bullets && !Roo.isTouch){
17853 cls : 'carousel-bullets',
17857 if(this.bullets_cls){
17858 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17865 cfg.cn[0].cn.push(bullets);
17868 if(this.showarrow){
17869 cfg.cn[0].cn.push({
17871 class : 'carousel-arrow',
17875 class : 'carousel-prev',
17879 class : 'fa fa-chevron-left'
17885 class : 'carousel-next',
17889 class : 'fa fa-chevron-right'
17902 initEvents: function()
17904 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17905 // this.el.on("touchstart", this.onTouchStart, this);
17908 if(this.autoslide){
17911 this.slideFn = window.setInterval(function() {
17912 _this.showPanelNext();
17916 if(this.showarrow){
17917 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17918 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17924 // onTouchStart : function(e, el, o)
17926 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17930 // this.showPanelNext();
17934 getChildContainer : function()
17936 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17940 * register a Navigation item
17941 * @param {Roo.bootstrap.NavItem} the navitem to add
17943 register : function(item)
17945 this.tabs.push( item);
17946 item.navId = this.navId; // not really needed..
17951 getActivePanel : function()
17954 Roo.each(this.tabs, function(t) {
17964 getPanelByName : function(n)
17967 Roo.each(this.tabs, function(t) {
17968 if (t.tabId == n) {
17976 indexOfPanel : function(p)
17979 Roo.each(this.tabs, function(t,i) {
17980 if (t.tabId == p.tabId) {
17989 * show a specific panel
17990 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17991 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17993 showPanel : function (pan)
17995 if(this.transition || typeof(pan) == 'undefined'){
17996 Roo.log("waiting for the transitionend");
18000 if (typeof(pan) == 'number') {
18001 pan = this.tabs[pan];
18004 if (typeof(pan) == 'string') {
18005 pan = this.getPanelByName(pan);
18008 var cur = this.getActivePanel();
18011 Roo.log('pan or acitve pan is undefined');
18015 if (pan.tabId == this.getActivePanel().tabId) {
18019 if (false === cur.fireEvent('beforedeactivate')) {
18023 if(this.bullets > 0 && !Roo.isTouch){
18024 this.setActiveBullet(this.indexOfPanel(pan));
18027 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18029 this.transition = true;
18030 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18031 var lr = dir == 'next' ? 'left' : 'right';
18032 pan.el.addClass(dir); // or prev
18033 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18034 cur.el.addClass(lr); // or right
18035 pan.el.addClass(lr);
18038 cur.el.on('transitionend', function() {
18039 Roo.log("trans end?");
18041 pan.el.removeClass([lr,dir]);
18042 pan.setActive(true);
18044 cur.el.removeClass([lr]);
18045 cur.setActive(false);
18047 _this.transition = false;
18049 }, this, { single: true } );
18054 cur.setActive(false);
18055 pan.setActive(true);
18060 showPanelNext : function()
18062 var i = this.indexOfPanel(this.getActivePanel());
18064 if (i >= this.tabs.length - 1 && !this.autoslide) {
18068 if (i >= this.tabs.length - 1 && this.autoslide) {
18072 this.showPanel(this.tabs[i+1]);
18075 showPanelPrev : function()
18077 var i = this.indexOfPanel(this.getActivePanel());
18079 if (i < 1 && !this.autoslide) {
18083 if (i < 1 && this.autoslide) {
18084 i = this.tabs.length;
18087 this.showPanel(this.tabs[i-1]);
18091 addBullet: function()
18093 if(!this.bullets || Roo.isTouch){
18096 var ctr = this.el.select('.carousel-bullets',true).first();
18097 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18098 var bullet = ctr.createChild({
18099 cls : 'bullet bullet-' + i
18100 },ctr.dom.lastChild);
18105 bullet.on('click', (function(e, el, o, ii, t){
18107 e.preventDefault();
18109 this.showPanel(ii);
18111 if(this.autoslide && this.slideFn){
18112 clearInterval(this.slideFn);
18113 this.slideFn = window.setInterval(function() {
18114 _this.showPanelNext();
18118 }).createDelegate(this, [i, bullet], true));
18123 setActiveBullet : function(i)
18129 Roo.each(this.el.select('.bullet', true).elements, function(el){
18130 el.removeClass('selected');
18133 var bullet = this.el.select('.bullet-' + i, true).first();
18139 bullet.addClass('selected');
18150 Roo.apply(Roo.bootstrap.TabGroup, {
18154 * register a Navigation Group
18155 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18157 register : function(navgrp)
18159 this.groups[navgrp.navId] = navgrp;
18163 * fetch a Navigation Group based on the navigation ID
18164 * if one does not exist , it will get created.
18165 * @param {string} the navgroup to add
18166 * @returns {Roo.bootstrap.NavGroup} the navgroup
18168 get: function(navId) {
18169 if (typeof(this.groups[navId]) == 'undefined') {
18170 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18172 return this.groups[navId] ;
18187 * @class Roo.bootstrap.TabPanel
18188 * @extends Roo.bootstrap.Component
18189 * Bootstrap TabPanel class
18190 * @cfg {Boolean} active panel active
18191 * @cfg {String} html panel content
18192 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18193 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18194 * @cfg {String} href click to link..
18198 * Create a new TabPanel
18199 * @param {Object} config The config object
18202 Roo.bootstrap.TabPanel = function(config){
18203 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18207 * Fires when the active status changes
18208 * @param {Roo.bootstrap.TabPanel} this
18209 * @param {Boolean} state the new state
18214 * @event beforedeactivate
18215 * Fires before a tab is de-activated - can be used to do validation on a form.
18216 * @param {Roo.bootstrap.TabPanel} this
18217 * @return {Boolean} false if there is an error
18220 'beforedeactivate': true
18223 this.tabId = this.tabId || Roo.id();
18227 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18235 getAutoCreate : function(){
18238 // item is needed for carousel - not sure if it has any effect otherwise
18239 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18240 html: this.html || ''
18244 cfg.cls += ' active';
18248 cfg.tabId = this.tabId;
18255 initEvents: function()
18257 var p = this.parent();
18259 this.navId = this.navId || p.navId;
18261 if (typeof(this.navId) != 'undefined') {
18262 // not really needed.. but just in case.. parent should be a NavGroup.
18263 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18267 var i = tg.tabs.length - 1;
18269 if(this.active && tg.bullets > 0 && i < tg.bullets){
18270 tg.setActiveBullet(i);
18274 this.el.on('click', this.onClick, this);
18277 this.el.on("touchstart", this.onTouchStart, this);
18278 this.el.on("touchmove", this.onTouchMove, this);
18279 this.el.on("touchend", this.onTouchEnd, this);
18284 onRender : function(ct, position)
18286 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18289 setActive : function(state)
18291 Roo.log("panel - set active " + this.tabId + "=" + state);
18293 this.active = state;
18295 this.el.removeClass('active');
18297 } else if (!this.el.hasClass('active')) {
18298 this.el.addClass('active');
18301 this.fireEvent('changed', this, state);
18304 onClick : function(e)
18306 e.preventDefault();
18308 if(!this.href.length){
18312 window.location.href = this.href;
18321 onTouchStart : function(e)
18323 this.swiping = false;
18325 this.startX = e.browserEvent.touches[0].clientX;
18326 this.startY = e.browserEvent.touches[0].clientY;
18329 onTouchMove : function(e)
18331 this.swiping = true;
18333 this.endX = e.browserEvent.touches[0].clientX;
18334 this.endY = e.browserEvent.touches[0].clientY;
18337 onTouchEnd : function(e)
18344 var tabGroup = this.parent();
18346 if(this.endX > this.startX){ // swiping right
18347 tabGroup.showPanelPrev();
18351 if(this.startX > this.endX){ // swiping left
18352 tabGroup.showPanelNext();
18371 * @class Roo.bootstrap.DateField
18372 * @extends Roo.bootstrap.Input
18373 * Bootstrap DateField class
18374 * @cfg {Number} weekStart default 0
18375 * @cfg {String} viewMode default empty, (months|years)
18376 * @cfg {String} minViewMode default empty, (months|years)
18377 * @cfg {Number} startDate default -Infinity
18378 * @cfg {Number} endDate default Infinity
18379 * @cfg {Boolean} todayHighlight default false
18380 * @cfg {Boolean} todayBtn default false
18381 * @cfg {Boolean} calendarWeeks default false
18382 * @cfg {Object} daysOfWeekDisabled default empty
18383 * @cfg {Boolean} singleMode default false (true | false)
18385 * @cfg {Boolean} keyboardNavigation default true
18386 * @cfg {String} language default en
18389 * Create a new DateField
18390 * @param {Object} config The config object
18393 Roo.bootstrap.DateField = function(config){
18394 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18398 * Fires when this field show.
18399 * @param {Roo.bootstrap.DateField} this
18400 * @param {Mixed} date The date value
18405 * Fires when this field hide.
18406 * @param {Roo.bootstrap.DateField} this
18407 * @param {Mixed} date The date value
18412 * Fires when select a date.
18413 * @param {Roo.bootstrap.DateField} this
18414 * @param {Mixed} date The date value
18418 * @event beforeselect
18419 * Fires when before select a date.
18420 * @param {Roo.bootstrap.DateField} this
18421 * @param {Mixed} date The date value
18423 beforeselect : true
18427 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18430 * @cfg {String} format
18431 * The default date format string which can be overriden for localization support. The format must be
18432 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18436 * @cfg {String} altFormats
18437 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18438 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18440 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18448 todayHighlight : false,
18454 keyboardNavigation: true,
18456 calendarWeeks: false,
18458 startDate: -Infinity,
18462 daysOfWeekDisabled: [],
18466 singleMode : false,
18468 UTCDate: function()
18470 return new Date(Date.UTC.apply(Date, arguments));
18473 UTCToday: function()
18475 var today = new Date();
18476 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18479 getDate: function() {
18480 var d = this.getUTCDate();
18481 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18484 getUTCDate: function() {
18488 setDate: function(d) {
18489 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18492 setUTCDate: function(d) {
18494 this.setValue(this.formatDate(this.date));
18497 onRender: function(ct, position)
18500 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18502 this.language = this.language || 'en';
18503 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18504 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18506 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18507 this.format = this.format || 'm/d/y';
18508 this.isInline = false;
18509 this.isInput = true;
18510 this.component = this.el.select('.add-on', true).first() || false;
18511 this.component = (this.component && this.component.length === 0) ? false : this.component;
18512 this.hasInput = this.component && this.inputEl().length;
18514 if (typeof(this.minViewMode === 'string')) {
18515 switch (this.minViewMode) {
18517 this.minViewMode = 1;
18520 this.minViewMode = 2;
18523 this.minViewMode = 0;
18528 if (typeof(this.viewMode === 'string')) {
18529 switch (this.viewMode) {
18542 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18544 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18546 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18548 this.picker().on('mousedown', this.onMousedown, this);
18549 this.picker().on('click', this.onClick, this);
18551 this.picker().addClass('datepicker-dropdown');
18553 this.startViewMode = this.viewMode;
18555 if(this.singleMode){
18556 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18557 v.setVisibilityMode(Roo.Element.DISPLAY);
18561 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18562 v.setStyle('width', '189px');
18566 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18567 if(!this.calendarWeeks){
18572 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18573 v.attr('colspan', function(i, val){
18574 return parseInt(val) + 1;
18579 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18581 this.setStartDate(this.startDate);
18582 this.setEndDate(this.endDate);
18584 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18591 if(this.isInline) {
18596 picker : function()
18598 return this.pickerEl;
18599 // return this.el.select('.datepicker', true).first();
18602 fillDow: function()
18604 var dowCnt = this.weekStart;
18613 if(this.calendarWeeks){
18621 while (dowCnt < this.weekStart + 7) {
18625 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18629 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18632 fillMonths: function()
18635 var months = this.picker().select('>.datepicker-months td', true).first();
18637 months.dom.innerHTML = '';
18643 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18646 months.createChild(month);
18653 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;
18655 if (this.date < this.startDate) {
18656 this.viewDate = new Date(this.startDate);
18657 } else if (this.date > this.endDate) {
18658 this.viewDate = new Date(this.endDate);
18660 this.viewDate = new Date(this.date);
18668 var d = new Date(this.viewDate),
18669 year = d.getUTCFullYear(),
18670 month = d.getUTCMonth(),
18671 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18672 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18673 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18674 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18675 currentDate = this.date && this.date.valueOf(),
18676 today = this.UTCToday();
18678 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18680 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18682 // this.picker.select('>tfoot th.today').
18683 // .text(dates[this.language].today)
18684 // .toggle(this.todayBtn !== false);
18686 this.updateNavArrows();
18689 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18691 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18693 prevMonth.setUTCDate(day);
18695 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18697 var nextMonth = new Date(prevMonth);
18699 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18701 nextMonth = nextMonth.valueOf();
18703 var fillMonths = false;
18705 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18707 while(prevMonth.valueOf() <= nextMonth) {
18710 if (prevMonth.getUTCDay() === this.weekStart) {
18712 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18720 if(this.calendarWeeks){
18721 // ISO 8601: First week contains first thursday.
18722 // ISO also states week starts on Monday, but we can be more abstract here.
18724 // Start of current week: based on weekstart/current date
18725 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18726 // Thursday of this week
18727 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18728 // First Thursday of year, year from thursday
18729 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18730 // Calendar week: ms between thursdays, div ms per day, div 7 days
18731 calWeek = (th - yth) / 864e5 / 7 + 1;
18733 fillMonths.cn.push({
18741 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18743 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18746 if (this.todayHighlight &&
18747 prevMonth.getUTCFullYear() == today.getFullYear() &&
18748 prevMonth.getUTCMonth() == today.getMonth() &&
18749 prevMonth.getUTCDate() == today.getDate()) {
18750 clsName += ' today';
18753 if (currentDate && prevMonth.valueOf() === currentDate) {
18754 clsName += ' active';
18757 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18758 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18759 clsName += ' disabled';
18762 fillMonths.cn.push({
18764 cls: 'day ' + clsName,
18765 html: prevMonth.getDate()
18768 prevMonth.setDate(prevMonth.getDate()+1);
18771 var currentYear = this.date && this.date.getUTCFullYear();
18772 var currentMonth = this.date && this.date.getUTCMonth();
18774 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18776 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18777 v.removeClass('active');
18779 if(currentYear === year && k === currentMonth){
18780 v.addClass('active');
18783 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18784 v.addClass('disabled');
18790 year = parseInt(year/10, 10) * 10;
18792 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18794 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18797 for (var i = -1; i < 11; i++) {
18798 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18800 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18808 showMode: function(dir)
18811 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18814 Roo.each(this.picker().select('>div',true).elements, function(v){
18815 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18818 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18823 if(this.isInline) {
18827 this.picker().removeClass(['bottom', 'top']);
18829 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18831 * place to the top of element!
18835 this.picker().addClass('top');
18836 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18841 this.picker().addClass('bottom');
18843 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18846 parseDate : function(value)
18848 if(!value || value instanceof Date){
18851 var v = Date.parseDate(value, this.format);
18852 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18853 v = Date.parseDate(value, 'Y-m-d');
18855 if(!v && this.altFormats){
18856 if(!this.altFormatsArray){
18857 this.altFormatsArray = this.altFormats.split("|");
18859 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18860 v = Date.parseDate(value, this.altFormatsArray[i]);
18866 formatDate : function(date, fmt)
18868 return (!date || !(date instanceof Date)) ?
18869 date : date.dateFormat(fmt || this.format);
18872 onFocus : function()
18874 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18878 onBlur : function()
18880 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18882 var d = this.inputEl().getValue();
18889 showPopup : function()
18891 this.picker().show();
18895 this.fireEvent('showpopup', this, this.date);
18898 hidePopup : function()
18900 if(this.isInline) {
18903 this.picker().hide();
18904 this.viewMode = this.startViewMode;
18907 this.fireEvent('hidepopup', this, this.date);
18911 onMousedown: function(e)
18913 e.stopPropagation();
18914 e.preventDefault();
18919 Roo.bootstrap.DateField.superclass.keyup.call(this);
18923 setValue: function(v)
18925 if(this.fireEvent('beforeselect', this, v) !== false){
18926 var d = new Date(this.parseDate(v) ).clearTime();
18928 if(isNaN(d.getTime())){
18929 this.date = this.viewDate = '';
18930 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18934 v = this.formatDate(d);
18936 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18938 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18942 this.fireEvent('select', this, this.date);
18946 getValue: function()
18948 return this.formatDate(this.date);
18951 fireKey: function(e)
18953 if (!this.picker().isVisible()){
18954 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18960 var dateChanged = false,
18962 newDate, newViewDate;
18967 e.preventDefault();
18971 if (!this.keyboardNavigation) {
18974 dir = e.keyCode == 37 ? -1 : 1;
18977 newDate = this.moveYear(this.date, dir);
18978 newViewDate = this.moveYear(this.viewDate, dir);
18979 } else if (e.shiftKey){
18980 newDate = this.moveMonth(this.date, dir);
18981 newViewDate = this.moveMonth(this.viewDate, dir);
18983 newDate = new Date(this.date);
18984 newDate.setUTCDate(this.date.getUTCDate() + dir);
18985 newViewDate = new Date(this.viewDate);
18986 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18988 if (this.dateWithinRange(newDate)){
18989 this.date = newDate;
18990 this.viewDate = newViewDate;
18991 this.setValue(this.formatDate(this.date));
18993 e.preventDefault();
18994 dateChanged = true;
18999 if (!this.keyboardNavigation) {
19002 dir = e.keyCode == 38 ? -1 : 1;
19004 newDate = this.moveYear(this.date, dir);
19005 newViewDate = this.moveYear(this.viewDate, dir);
19006 } else if (e.shiftKey){
19007 newDate = this.moveMonth(this.date, dir);
19008 newViewDate = this.moveMonth(this.viewDate, dir);
19010 newDate = new Date(this.date);
19011 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19012 newViewDate = new Date(this.viewDate);
19013 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19015 if (this.dateWithinRange(newDate)){
19016 this.date = newDate;
19017 this.viewDate = newViewDate;
19018 this.setValue(this.formatDate(this.date));
19020 e.preventDefault();
19021 dateChanged = true;
19025 this.setValue(this.formatDate(this.date));
19027 e.preventDefault();
19030 this.setValue(this.formatDate(this.date));
19044 onClick: function(e)
19046 e.stopPropagation();
19047 e.preventDefault();
19049 var target = e.getTarget();
19051 if(target.nodeName.toLowerCase() === 'i'){
19052 target = Roo.get(target).dom.parentNode;
19055 var nodeName = target.nodeName;
19056 var className = target.className;
19057 var html = target.innerHTML;
19058 //Roo.log(nodeName);
19060 switch(nodeName.toLowerCase()) {
19062 switch(className) {
19068 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19069 switch(this.viewMode){
19071 this.viewDate = this.moveMonth(this.viewDate, dir);
19075 this.viewDate = this.moveYear(this.viewDate, dir);
19081 var date = new Date();
19082 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19084 this.setValue(this.formatDate(this.date));
19091 if (className.indexOf('disabled') < 0) {
19092 this.viewDate.setUTCDate(1);
19093 if (className.indexOf('month') > -1) {
19094 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19096 var year = parseInt(html, 10) || 0;
19097 this.viewDate.setUTCFullYear(year);
19101 if(this.singleMode){
19102 this.setValue(this.formatDate(this.viewDate));
19113 //Roo.log(className);
19114 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19115 var day = parseInt(html, 10) || 1;
19116 var year = this.viewDate.getUTCFullYear(),
19117 month = this.viewDate.getUTCMonth();
19119 if (className.indexOf('old') > -1) {
19126 } else if (className.indexOf('new') > -1) {
19134 //Roo.log([year,month,day]);
19135 this.date = this.UTCDate(year, month, day,0,0,0,0);
19136 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19138 //Roo.log(this.formatDate(this.date));
19139 this.setValue(this.formatDate(this.date));
19146 setStartDate: function(startDate)
19148 this.startDate = startDate || -Infinity;
19149 if (this.startDate !== -Infinity) {
19150 this.startDate = this.parseDate(this.startDate);
19153 this.updateNavArrows();
19156 setEndDate: function(endDate)
19158 this.endDate = endDate || Infinity;
19159 if (this.endDate !== Infinity) {
19160 this.endDate = this.parseDate(this.endDate);
19163 this.updateNavArrows();
19166 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19168 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19169 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19170 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19172 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19173 return parseInt(d, 10);
19176 this.updateNavArrows();
19179 updateNavArrows: function()
19181 if(this.singleMode){
19185 var d = new Date(this.viewDate),
19186 year = d.getUTCFullYear(),
19187 month = d.getUTCMonth();
19189 Roo.each(this.picker().select('.prev', true).elements, function(v){
19191 switch (this.viewMode) {
19194 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19200 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19207 Roo.each(this.picker().select('.next', true).elements, function(v){
19209 switch (this.viewMode) {
19212 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19218 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19226 moveMonth: function(date, dir)
19231 var new_date = new Date(date.valueOf()),
19232 day = new_date.getUTCDate(),
19233 month = new_date.getUTCMonth(),
19234 mag = Math.abs(dir),
19236 dir = dir > 0 ? 1 : -1;
19239 // If going back one month, make sure month is not current month
19240 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19242 return new_date.getUTCMonth() == month;
19244 // If going forward one month, make sure month is as expected
19245 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19247 return new_date.getUTCMonth() != new_month;
19249 new_month = month + dir;
19250 new_date.setUTCMonth(new_month);
19251 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19252 if (new_month < 0 || new_month > 11) {
19253 new_month = (new_month + 12) % 12;
19256 // For magnitudes >1, move one month at a time...
19257 for (var i=0; i<mag; i++) {
19258 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19259 new_date = this.moveMonth(new_date, dir);
19261 // ...then reset the day, keeping it in the new month
19262 new_month = new_date.getUTCMonth();
19263 new_date.setUTCDate(day);
19265 return new_month != new_date.getUTCMonth();
19268 // Common date-resetting loop -- if date is beyond end of month, make it
19271 new_date.setUTCDate(--day);
19272 new_date.setUTCMonth(new_month);
19277 moveYear: function(date, dir)
19279 return this.moveMonth(date, dir*12);
19282 dateWithinRange: function(date)
19284 return date >= this.startDate && date <= this.endDate;
19290 this.picker().remove();
19293 validateValue : function(value)
19295 if(this.getVisibilityEl().hasClass('hidden')){
19299 if(value.length < 1) {
19300 if(this.allowBlank){
19306 if(value.length < this.minLength){
19309 if(value.length > this.maxLength){
19313 var vt = Roo.form.VTypes;
19314 if(!vt[this.vtype](value, this)){
19318 if(typeof this.validator == "function"){
19319 var msg = this.validator(value);
19325 if(this.regex && !this.regex.test(value)){
19329 if(typeof(this.parseDate(value)) == 'undefined'){
19333 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19337 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19347 this.date = this.viewDate = '';
19349 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19354 Roo.apply(Roo.bootstrap.DateField, {
19365 html: '<i class="fa fa-arrow-left"/>'
19375 html: '<i class="fa fa-arrow-right"/>'
19417 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19418 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19419 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19420 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19421 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19434 navFnc: 'FullYear',
19439 navFnc: 'FullYear',
19444 Roo.apply(Roo.bootstrap.DateField, {
19448 cls: 'datepicker dropdown-menu roo-dynamic',
19452 cls: 'datepicker-days',
19456 cls: 'table-condensed',
19458 Roo.bootstrap.DateField.head,
19462 Roo.bootstrap.DateField.footer
19469 cls: 'datepicker-months',
19473 cls: 'table-condensed',
19475 Roo.bootstrap.DateField.head,
19476 Roo.bootstrap.DateField.content,
19477 Roo.bootstrap.DateField.footer
19484 cls: 'datepicker-years',
19488 cls: 'table-condensed',
19490 Roo.bootstrap.DateField.head,
19491 Roo.bootstrap.DateField.content,
19492 Roo.bootstrap.DateField.footer
19511 * @class Roo.bootstrap.TimeField
19512 * @extends Roo.bootstrap.Input
19513 * Bootstrap DateField class
19517 * Create a new TimeField
19518 * @param {Object} config The config object
19521 Roo.bootstrap.TimeField = function(config){
19522 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19526 * Fires when this field show.
19527 * @param {Roo.bootstrap.DateField} thisthis
19528 * @param {Mixed} date The date value
19533 * Fires when this field hide.
19534 * @param {Roo.bootstrap.DateField} this
19535 * @param {Mixed} date The date value
19540 * Fires when select a date.
19541 * @param {Roo.bootstrap.DateField} this
19542 * @param {Mixed} date The date value
19548 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19551 * @cfg {String} format
19552 * The default time format string which can be overriden for localization support. The format must be
19553 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19557 onRender: function(ct, position)
19560 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19562 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19564 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19566 this.pop = this.picker().select('>.datepicker-time',true).first();
19567 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19569 this.picker().on('mousedown', this.onMousedown, this);
19570 this.picker().on('click', this.onClick, this);
19572 this.picker().addClass('datepicker-dropdown');
19577 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19578 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19579 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19580 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19581 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19582 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19586 fireKey: function(e){
19587 if (!this.picker().isVisible()){
19588 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19594 e.preventDefault();
19602 this.onTogglePeriod();
19605 this.onIncrementMinutes();
19608 this.onDecrementMinutes();
19617 onClick: function(e) {
19618 e.stopPropagation();
19619 e.preventDefault();
19622 picker : function()
19624 return this.el.select('.datepicker', true).first();
19627 fillTime: function()
19629 var time = this.pop.select('tbody', true).first();
19631 time.dom.innerHTML = '';
19646 cls: 'hours-up glyphicon glyphicon-chevron-up'
19666 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19687 cls: 'timepicker-hour',
19702 cls: 'timepicker-minute',
19717 cls: 'btn btn-primary period',
19739 cls: 'hours-down glyphicon glyphicon-chevron-down'
19759 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19777 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19784 var hours = this.time.getHours();
19785 var minutes = this.time.getMinutes();
19798 hours = hours - 12;
19802 hours = '0' + hours;
19806 minutes = '0' + minutes;
19809 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19810 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19811 this.pop.select('button', true).first().dom.innerHTML = period;
19817 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19819 var cls = ['bottom'];
19821 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19828 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19833 this.picker().addClass(cls.join('-'));
19837 Roo.each(cls, function(c){
19839 _this.picker().setTop(_this.inputEl().getHeight());
19843 _this.picker().setTop(0 - _this.picker().getHeight());
19848 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19852 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19859 onFocus : function()
19861 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19865 onBlur : function()
19867 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19873 this.picker().show();
19878 this.fireEvent('show', this, this.date);
19883 this.picker().hide();
19886 this.fireEvent('hide', this, this.date);
19889 setTime : function()
19892 this.setValue(this.time.format(this.format));
19894 this.fireEvent('select', this, this.date);
19899 onMousedown: function(e){
19900 e.stopPropagation();
19901 e.preventDefault();
19904 onIncrementHours: function()
19906 Roo.log('onIncrementHours');
19907 this.time = this.time.add(Date.HOUR, 1);
19912 onDecrementHours: function()
19914 Roo.log('onDecrementHours');
19915 this.time = this.time.add(Date.HOUR, -1);
19919 onIncrementMinutes: function()
19921 Roo.log('onIncrementMinutes');
19922 this.time = this.time.add(Date.MINUTE, 1);
19926 onDecrementMinutes: function()
19928 Roo.log('onDecrementMinutes');
19929 this.time = this.time.add(Date.MINUTE, -1);
19933 onTogglePeriod: function()
19935 Roo.log('onTogglePeriod');
19936 this.time = this.time.add(Date.HOUR, 12);
19943 Roo.apply(Roo.bootstrap.TimeField, {
19973 cls: 'btn btn-info ok',
19985 Roo.apply(Roo.bootstrap.TimeField, {
19989 cls: 'datepicker dropdown-menu',
19993 cls: 'datepicker-time',
19997 cls: 'table-condensed',
19999 Roo.bootstrap.TimeField.content,
20000 Roo.bootstrap.TimeField.footer
20019 * @class Roo.bootstrap.MonthField
20020 * @extends Roo.bootstrap.Input
20021 * Bootstrap MonthField class
20023 * @cfg {String} language default en
20026 * Create a new MonthField
20027 * @param {Object} config The config object
20030 Roo.bootstrap.MonthField = function(config){
20031 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20036 * Fires when this field show.
20037 * @param {Roo.bootstrap.MonthField} this
20038 * @param {Mixed} date The date value
20043 * Fires when this field hide.
20044 * @param {Roo.bootstrap.MonthField} this
20045 * @param {Mixed} date The date value
20050 * Fires when select a date.
20051 * @param {Roo.bootstrap.MonthField} this
20052 * @param {String} oldvalue The old value
20053 * @param {String} newvalue The new value
20059 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20061 onRender: function(ct, position)
20064 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20066 this.language = this.language || 'en';
20067 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20068 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20070 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20071 this.isInline = false;
20072 this.isInput = true;
20073 this.component = this.el.select('.add-on', true).first() || false;
20074 this.component = (this.component && this.component.length === 0) ? false : this.component;
20075 this.hasInput = this.component && this.inputEL().length;
20077 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20079 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20081 this.picker().on('mousedown', this.onMousedown, this);
20082 this.picker().on('click', this.onClick, this);
20084 this.picker().addClass('datepicker-dropdown');
20086 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20087 v.setStyle('width', '189px');
20094 if(this.isInline) {
20100 setValue: function(v, suppressEvent)
20102 var o = this.getValue();
20104 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20108 if(suppressEvent !== true){
20109 this.fireEvent('select', this, o, v);
20114 getValue: function()
20119 onClick: function(e)
20121 e.stopPropagation();
20122 e.preventDefault();
20124 var target = e.getTarget();
20126 if(target.nodeName.toLowerCase() === 'i'){
20127 target = Roo.get(target).dom.parentNode;
20130 var nodeName = target.nodeName;
20131 var className = target.className;
20132 var html = target.innerHTML;
20134 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20138 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20140 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20146 picker : function()
20148 return this.pickerEl;
20151 fillMonths: function()
20154 var months = this.picker().select('>.datepicker-months td', true).first();
20156 months.dom.innerHTML = '';
20162 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20165 months.createChild(month);
20174 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20175 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20178 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20179 e.removeClass('active');
20181 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20182 e.addClass('active');
20189 if(this.isInline) {
20193 this.picker().removeClass(['bottom', 'top']);
20195 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20197 * place to the top of element!
20201 this.picker().addClass('top');
20202 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20207 this.picker().addClass('bottom');
20209 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20212 onFocus : function()
20214 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20218 onBlur : function()
20220 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20222 var d = this.inputEl().getValue();
20231 this.picker().show();
20232 this.picker().select('>.datepicker-months', true).first().show();
20236 this.fireEvent('show', this, this.date);
20241 if(this.isInline) {
20244 this.picker().hide();
20245 this.fireEvent('hide', this, this.date);
20249 onMousedown: function(e)
20251 e.stopPropagation();
20252 e.preventDefault();
20257 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20261 fireKey: function(e)
20263 if (!this.picker().isVisible()){
20264 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20275 e.preventDefault();
20279 dir = e.keyCode == 37 ? -1 : 1;
20281 this.vIndex = this.vIndex + dir;
20283 if(this.vIndex < 0){
20287 if(this.vIndex > 11){
20291 if(isNaN(this.vIndex)){
20295 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20301 dir = e.keyCode == 38 ? -1 : 1;
20303 this.vIndex = this.vIndex + dir * 4;
20305 if(this.vIndex < 0){
20309 if(this.vIndex > 11){
20313 if(isNaN(this.vIndex)){
20317 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20322 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20323 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20327 e.preventDefault();
20330 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20331 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20347 this.picker().remove();
20352 Roo.apply(Roo.bootstrap.MonthField, {
20371 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20372 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20377 Roo.apply(Roo.bootstrap.MonthField, {
20381 cls: 'datepicker dropdown-menu roo-dynamic',
20385 cls: 'datepicker-months',
20389 cls: 'table-condensed',
20391 Roo.bootstrap.DateField.content
20411 * @class Roo.bootstrap.CheckBox
20412 * @extends Roo.bootstrap.Input
20413 * Bootstrap CheckBox class
20415 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20416 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20417 * @cfg {String} boxLabel The text that appears beside the checkbox
20418 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20419 * @cfg {Boolean} checked initnal the element
20420 * @cfg {Boolean} inline inline the element (default false)
20421 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20422 * @cfg {String} tooltip label tooltip
20425 * Create a new CheckBox
20426 * @param {Object} config The config object
20429 Roo.bootstrap.CheckBox = function(config){
20430 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20435 * Fires when the element is checked or unchecked.
20436 * @param {Roo.bootstrap.CheckBox} this This input
20437 * @param {Boolean} checked The new checked value
20442 * Fires when the element is click.
20443 * @param {Roo.bootstrap.CheckBox} this This input
20450 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20452 inputType: 'checkbox',
20461 getAutoCreate : function()
20463 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20469 cfg.cls = 'form-group ' + this.inputType; //input-group
20472 cfg.cls += ' ' + this.inputType + '-inline';
20478 type : this.inputType,
20479 value : this.inputValue,
20480 cls : 'roo-' + this.inputType, //'form-box',
20481 placeholder : this.placeholder || ''
20485 if(this.inputType != 'radio'){
20489 cls : 'roo-hidden-value',
20490 value : this.checked ? this.inputValue : this.valueOff
20495 if (this.weight) { // Validity check?
20496 cfg.cls += " " + this.inputType + "-" + this.weight;
20499 if (this.disabled) {
20500 input.disabled=true;
20504 input.checked = this.checked;
20509 input.name = this.name;
20511 if(this.inputType != 'radio'){
20512 hidden.name = this.name;
20513 input.name = '_hidden_' + this.name;
20518 input.cls += ' input-' + this.size;
20523 ['xs','sm','md','lg'].map(function(size){
20524 if (settings[size]) {
20525 cfg.cls += ' col-' + size + '-' + settings[size];
20529 var inputblock = input;
20531 if (this.before || this.after) {
20534 cls : 'input-group',
20539 inputblock.cn.push({
20541 cls : 'input-group-addon',
20546 inputblock.cn.push(input);
20548 if(this.inputType != 'radio'){
20549 inputblock.cn.push(hidden);
20553 inputblock.cn.push({
20555 cls : 'input-group-addon',
20562 if (align ==='left' && this.fieldLabel.length) {
20563 // Roo.log("left and has label");
20568 cls : 'control-label',
20569 html : this.fieldLabel
20579 if(this.labelWidth > 12){
20580 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20583 if(this.labelWidth < 13 && this.labelmd == 0){
20584 this.labelmd = this.labelWidth;
20587 if(this.labellg > 0){
20588 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20589 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20592 if(this.labelmd > 0){
20593 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20594 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20597 if(this.labelsm > 0){
20598 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20599 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20602 if(this.labelxs > 0){
20603 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20604 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20607 } else if ( this.fieldLabel.length) {
20608 // Roo.log(" label");
20612 tag: this.boxLabel ? 'span' : 'label',
20614 cls: 'control-label box-input-label',
20615 //cls : 'input-group-addon',
20616 html : this.fieldLabel
20625 // Roo.log(" no label && no align");
20626 cfg.cn = [ inputblock ] ;
20632 var boxLabelCfg = {
20634 //'for': id, // box label is handled by onclick - so no for...
20636 html: this.boxLabel
20640 boxLabelCfg.tooltip = this.tooltip;
20643 cfg.cn.push(boxLabelCfg);
20646 if(this.inputType != 'radio'){
20647 cfg.cn.push(hidden);
20655 * return the real input element.
20657 inputEl: function ()
20659 return this.el.select('input.roo-' + this.inputType,true).first();
20661 hiddenEl: function ()
20663 return this.el.select('input.roo-hidden-value',true).first();
20666 labelEl: function()
20668 return this.el.select('label.control-label',true).first();
20670 /* depricated... */
20674 return this.labelEl();
20677 boxLabelEl: function()
20679 return this.el.select('label.box-label',true).first();
20682 initEvents : function()
20684 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20686 this.inputEl().on('click', this.onClick, this);
20688 if (this.boxLabel) {
20689 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20692 this.startValue = this.getValue();
20695 Roo.bootstrap.CheckBox.register(this);
20699 onClick : function(e)
20701 if(this.fireEvent('click', this, e) !== false){
20702 this.setChecked(!this.checked);
20707 setChecked : function(state,suppressEvent)
20709 this.startValue = this.getValue();
20711 if(this.inputType == 'radio'){
20713 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20714 e.dom.checked = false;
20717 this.inputEl().dom.checked = true;
20719 this.inputEl().dom.value = this.inputValue;
20721 if(suppressEvent !== true){
20722 this.fireEvent('check', this, true);
20730 this.checked = state;
20732 this.inputEl().dom.checked = state;
20735 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20737 if(suppressEvent !== true){
20738 this.fireEvent('check', this, state);
20744 getValue : function()
20746 if(this.inputType == 'radio'){
20747 return this.getGroupValue();
20750 return this.hiddenEl().dom.value;
20754 getGroupValue : function()
20756 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20760 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20763 setValue : function(v,suppressEvent)
20765 if(this.inputType == 'radio'){
20766 this.setGroupValue(v, suppressEvent);
20770 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20775 setGroupValue : function(v, suppressEvent)
20777 this.startValue = this.getValue();
20779 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20780 e.dom.checked = false;
20782 if(e.dom.value == v){
20783 e.dom.checked = true;
20787 if(suppressEvent !== true){
20788 this.fireEvent('check', this, true);
20796 validate : function()
20798 if(this.getVisibilityEl().hasClass('hidden')){
20804 (this.inputType == 'radio' && this.validateRadio()) ||
20805 (this.inputType == 'checkbox' && this.validateCheckbox())
20811 this.markInvalid();
20815 validateRadio : function()
20817 if(this.getVisibilityEl().hasClass('hidden')){
20821 if(this.allowBlank){
20827 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20828 if(!e.dom.checked){
20840 validateCheckbox : function()
20843 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20844 //return (this.getValue() == this.inputValue) ? true : false;
20847 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20855 for(var i in group){
20856 if(group[i].el.isVisible(true)){
20864 for(var i in group){
20869 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20876 * Mark this field as valid
20878 markValid : function()
20882 this.fireEvent('valid', this);
20884 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20887 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20894 if(this.inputType == 'radio'){
20895 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20896 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20897 e.findParent('.form-group', false, true).addClass(_this.validClass);
20904 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20905 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20909 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20915 for(var i in group){
20916 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20917 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20922 * Mark this field as invalid
20923 * @param {String} msg The validation message
20925 markInvalid : function(msg)
20927 if(this.allowBlank){
20933 this.fireEvent('invalid', this, msg);
20935 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20938 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20942 label.markInvalid();
20945 if(this.inputType == 'radio'){
20946 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20947 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20948 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20955 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20956 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20960 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20966 for(var i in group){
20967 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20968 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20973 clearInvalid : function()
20975 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20977 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20979 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20981 if (label && label.iconEl) {
20982 label.iconEl.removeClass(label.validClass);
20983 label.iconEl.removeClass(label.invalidClass);
20987 disable : function()
20989 if(this.inputType != 'radio'){
20990 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20997 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20998 _this.getActionEl().addClass(this.disabledClass);
20999 e.dom.disabled = true;
21003 this.disabled = true;
21004 this.fireEvent("disable", this);
21008 enable : function()
21010 if(this.inputType != 'radio'){
21011 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21018 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21019 _this.getActionEl().removeClass(this.disabledClass);
21020 e.dom.disabled = false;
21024 this.disabled = false;
21025 this.fireEvent("enable", this);
21029 setBoxLabel : function(v)
21034 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21040 Roo.apply(Roo.bootstrap.CheckBox, {
21045 * register a CheckBox Group
21046 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21048 register : function(checkbox)
21050 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21051 this.groups[checkbox.groupId] = {};
21054 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21058 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21062 * fetch a CheckBox Group based on the group ID
21063 * @param {string} the group ID
21064 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21066 get: function(groupId) {
21067 if (typeof(this.groups[groupId]) == 'undefined') {
21071 return this.groups[groupId] ;
21084 * @class Roo.bootstrap.Radio
21085 * @extends Roo.bootstrap.Component
21086 * Bootstrap Radio class
21087 * @cfg {String} boxLabel - the label associated
21088 * @cfg {String} value - the value of radio
21091 * Create a new Radio
21092 * @param {Object} config The config object
21094 Roo.bootstrap.Radio = function(config){
21095 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21099 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21105 getAutoCreate : function()
21109 cls : 'form-group radio',
21114 html : this.boxLabel
21122 initEvents : function()
21124 this.parent().register(this);
21126 this.el.on('click', this.onClick, this);
21130 onClick : function(e)
21132 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21133 this.setChecked(true);
21137 setChecked : function(state, suppressEvent)
21139 this.parent().setValue(this.value, suppressEvent);
21143 setBoxLabel : function(v)
21148 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21163 * @class Roo.bootstrap.SecurePass
21164 * @extends Roo.bootstrap.Input
21165 * Bootstrap SecurePass class
21169 * Create a new SecurePass
21170 * @param {Object} config The config object
21173 Roo.bootstrap.SecurePass = function (config) {
21174 // these go here, so the translation tool can replace them..
21176 PwdEmpty: "Please type a password, and then retype it to confirm.",
21177 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21178 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21179 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21180 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21181 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21182 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21183 TooWeak: "Your password is Too Weak."
21185 this.meterLabel = "Password strength:";
21186 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21187 this.meterClass = [
21188 "roo-password-meter-tooweak",
21189 "roo-password-meter-weak",
21190 "roo-password-meter-medium",
21191 "roo-password-meter-strong",
21192 "roo-password-meter-grey"
21197 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21200 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21202 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21204 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21205 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21206 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21207 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21208 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21209 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21210 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21220 * @cfg {String/Object} Label for the strength meter (defaults to
21221 * 'Password strength:')
21226 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21227 * ['Weak', 'Medium', 'Strong'])
21230 pwdStrengths: false,
21243 initEvents: function ()
21245 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21247 if (this.el.is('input[type=password]') && Roo.isSafari) {
21248 this.el.on('keydown', this.SafariOnKeyDown, this);
21251 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21254 onRender: function (ct, position)
21256 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21257 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21258 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21260 this.trigger.createChild({
21265 cls: 'roo-password-meter-grey col-xs-12',
21268 //width: this.meterWidth + 'px'
21272 cls: 'roo-password-meter-text'
21278 if (this.hideTrigger) {
21279 this.trigger.setDisplayed(false);
21281 this.setSize(this.width || '', this.height || '');
21284 onDestroy: function ()
21286 if (this.trigger) {
21287 this.trigger.removeAllListeners();
21288 this.trigger.remove();
21291 this.wrap.remove();
21293 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21296 checkStrength: function ()
21298 var pwd = this.inputEl().getValue();
21299 if (pwd == this._lastPwd) {
21304 if (this.ClientSideStrongPassword(pwd)) {
21306 } else if (this.ClientSideMediumPassword(pwd)) {
21308 } else if (this.ClientSideWeakPassword(pwd)) {
21314 Roo.log('strength1: ' + strength);
21316 //var pm = this.trigger.child('div/div/div').dom;
21317 var pm = this.trigger.child('div/div');
21318 pm.removeClass(this.meterClass);
21319 pm.addClass(this.meterClass[strength]);
21322 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21324 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21326 this._lastPwd = pwd;
21330 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21332 this._lastPwd = '';
21334 var pm = this.trigger.child('div/div');
21335 pm.removeClass(this.meterClass);
21336 pm.addClass('roo-password-meter-grey');
21339 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21342 this.inputEl().dom.type='password';
21345 validateValue: function (value)
21348 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21351 if (value.length == 0) {
21352 if (this.allowBlank) {
21353 this.clearInvalid();
21357 this.markInvalid(this.errors.PwdEmpty);
21358 this.errorMsg = this.errors.PwdEmpty;
21366 if ('[\x21-\x7e]*'.match(value)) {
21367 this.markInvalid(this.errors.PwdBadChar);
21368 this.errorMsg = this.errors.PwdBadChar;
21371 if (value.length < 6) {
21372 this.markInvalid(this.errors.PwdShort);
21373 this.errorMsg = this.errors.PwdShort;
21376 if (value.length > 16) {
21377 this.markInvalid(this.errors.PwdLong);
21378 this.errorMsg = this.errors.PwdLong;
21382 if (this.ClientSideStrongPassword(value)) {
21384 } else if (this.ClientSideMediumPassword(value)) {
21386 } else if (this.ClientSideWeakPassword(value)) {
21393 if (strength < 2) {
21394 //this.markInvalid(this.errors.TooWeak);
21395 this.errorMsg = this.errors.TooWeak;
21400 console.log('strength2: ' + strength);
21402 //var pm = this.trigger.child('div/div/div').dom;
21404 var pm = this.trigger.child('div/div');
21405 pm.removeClass(this.meterClass);
21406 pm.addClass(this.meterClass[strength]);
21408 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21410 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21412 this.errorMsg = '';
21416 CharacterSetChecks: function (type)
21419 this.fResult = false;
21422 isctype: function (character, type)
21425 case this.kCapitalLetter:
21426 if (character >= 'A' && character <= 'Z') {
21431 case this.kSmallLetter:
21432 if (character >= 'a' && character <= 'z') {
21438 if (character >= '0' && character <= '9') {
21443 case this.kPunctuation:
21444 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21455 IsLongEnough: function (pwd, size)
21457 return !(pwd == null || isNaN(size) || pwd.length < size);
21460 SpansEnoughCharacterSets: function (word, nb)
21462 if (!this.IsLongEnough(word, nb))
21467 var characterSetChecks = new Array(
21468 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21469 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21472 for (var index = 0; index < word.length; ++index) {
21473 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21474 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21475 characterSetChecks[nCharSet].fResult = true;
21482 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21483 if (characterSetChecks[nCharSet].fResult) {
21488 if (nCharSets < nb) {
21494 ClientSideStrongPassword: function (pwd)
21496 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21499 ClientSideMediumPassword: function (pwd)
21501 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21504 ClientSideWeakPassword: function (pwd)
21506 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21509 })//<script type="text/javascript">
21512 * Based Ext JS Library 1.1.1
21513 * Copyright(c) 2006-2007, Ext JS, LLC.
21519 * @class Roo.HtmlEditorCore
21520 * @extends Roo.Component
21521 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21523 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21526 Roo.HtmlEditorCore = function(config){
21529 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21534 * @event initialize
21535 * Fires when the editor is fully initialized (including the iframe)
21536 * @param {Roo.HtmlEditorCore} this
21541 * Fires when the editor is first receives the focus. Any insertion must wait
21542 * until after this event.
21543 * @param {Roo.HtmlEditorCore} this
21547 * @event beforesync
21548 * Fires before the textarea is updated with content from the editor iframe. Return false
21549 * to cancel the sync.
21550 * @param {Roo.HtmlEditorCore} this
21551 * @param {String} html
21555 * @event beforepush
21556 * Fires before the iframe editor is updated with content from the textarea. Return false
21557 * to cancel the push.
21558 * @param {Roo.HtmlEditorCore} this
21559 * @param {String} html
21564 * Fires when the textarea is updated with content from the editor iframe.
21565 * @param {Roo.HtmlEditorCore} this
21566 * @param {String} html
21571 * Fires when the iframe editor is updated with content from the textarea.
21572 * @param {Roo.HtmlEditorCore} this
21573 * @param {String} html
21578 * @event editorevent
21579 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21580 * @param {Roo.HtmlEditorCore} this
21586 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21588 // defaults : white / black...
21589 this.applyBlacklists();
21596 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21600 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21606 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21611 * @cfg {Number} height (in pixels)
21615 * @cfg {Number} width (in pixels)
21620 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21623 stylesheets: false,
21628 // private properties
21629 validationEvent : false,
21631 initialized : false,
21633 sourceEditMode : false,
21634 onFocus : Roo.emptyFn,
21636 hideMode:'offsets',
21640 // blacklist + whitelisted elements..
21647 * Protected method that will not generally be called directly. It
21648 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21649 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21651 getDocMarkup : function(){
21655 // inherit styels from page...??
21656 if (this.stylesheets === false) {
21658 Roo.get(document.head).select('style').each(function(node) {
21659 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21662 Roo.get(document.head).select('link').each(function(node) {
21663 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21666 } else if (!this.stylesheets.length) {
21668 st = '<style type="text/css">' +
21669 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21672 st = '<style type="text/css">' +
21677 st += '<style type="text/css">' +
21678 'IMG { cursor: pointer } ' +
21681 var cls = 'roo-htmleditor-body';
21683 if(this.bodyCls.length){
21684 cls += ' ' + this.bodyCls;
21687 return '<html><head>' + st +
21688 //<style type="text/css">' +
21689 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21691 ' </head><body class="' + cls + '"></body></html>';
21695 onRender : function(ct, position)
21698 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21699 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21702 this.el.dom.style.border = '0 none';
21703 this.el.dom.setAttribute('tabIndex', -1);
21704 this.el.addClass('x-hidden hide');
21708 if(Roo.isIE){ // fix IE 1px bogus margin
21709 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21713 this.frameId = Roo.id();
21717 var iframe = this.owner.wrap.createChild({
21719 cls: 'form-control', // bootstrap..
21721 name: this.frameId,
21722 frameBorder : 'no',
21723 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21728 this.iframe = iframe.dom;
21730 this.assignDocWin();
21732 this.doc.designMode = 'on';
21735 this.doc.write(this.getDocMarkup());
21739 var task = { // must defer to wait for browser to be ready
21741 //console.log("run task?" + this.doc.readyState);
21742 this.assignDocWin();
21743 if(this.doc.body || this.doc.readyState == 'complete'){
21745 this.doc.designMode="on";
21749 Roo.TaskMgr.stop(task);
21750 this.initEditor.defer(10, this);
21757 Roo.TaskMgr.start(task);
21762 onResize : function(w, h)
21764 Roo.log('resize: ' +w + ',' + h );
21765 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21769 if(typeof w == 'number'){
21771 this.iframe.style.width = w + 'px';
21773 if(typeof h == 'number'){
21775 this.iframe.style.height = h + 'px';
21777 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21784 * Toggles the editor between standard and source edit mode.
21785 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21787 toggleSourceEdit : function(sourceEditMode){
21789 this.sourceEditMode = sourceEditMode === true;
21791 if(this.sourceEditMode){
21793 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21796 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21797 //this.iframe.className = '';
21800 //this.setSize(this.owner.wrap.getSize());
21801 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21808 * Protected method that will not generally be called directly. If you need/want
21809 * custom HTML cleanup, this is the method you should override.
21810 * @param {String} html The HTML to be cleaned
21811 * return {String} The cleaned HTML
21813 cleanHtml : function(html){
21814 html = String(html);
21815 if(html.length > 5){
21816 if(Roo.isSafari){ // strip safari nonsense
21817 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21820 if(html == ' '){
21827 * HTML Editor -> Textarea
21828 * Protected method that will not generally be called directly. Syncs the contents
21829 * of the editor iframe with the textarea.
21831 syncValue : function(){
21832 if(this.initialized){
21833 var bd = (this.doc.body || this.doc.documentElement);
21834 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21835 var html = bd.innerHTML;
21837 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21838 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21840 html = '<div style="'+m[0]+'">' + html + '</div>';
21843 html = this.cleanHtml(html);
21844 // fix up the special chars.. normaly like back quotes in word...
21845 // however we do not want to do this with chinese..
21846 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21847 var cc = b.charCodeAt();
21849 (cc >= 0x4E00 && cc < 0xA000 ) ||
21850 (cc >= 0x3400 && cc < 0x4E00 ) ||
21851 (cc >= 0xf900 && cc < 0xfb00 )
21857 if(this.owner.fireEvent('beforesync', this, html) !== false){
21858 this.el.dom.value = html;
21859 this.owner.fireEvent('sync', this, html);
21865 * Protected method that will not generally be called directly. Pushes the value of the textarea
21866 * into the iframe editor.
21868 pushValue : function(){
21869 if(this.initialized){
21870 var v = this.el.dom.value.trim();
21872 // if(v.length < 1){
21876 if(this.owner.fireEvent('beforepush', this, v) !== false){
21877 var d = (this.doc.body || this.doc.documentElement);
21879 this.cleanUpPaste();
21880 this.el.dom.value = d.innerHTML;
21881 this.owner.fireEvent('push', this, v);
21887 deferFocus : function(){
21888 this.focus.defer(10, this);
21892 focus : function(){
21893 if(this.win && !this.sourceEditMode){
21900 assignDocWin: function()
21902 var iframe = this.iframe;
21905 this.doc = iframe.contentWindow.document;
21906 this.win = iframe.contentWindow;
21908 // if (!Roo.get(this.frameId)) {
21911 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21912 // this.win = Roo.get(this.frameId).dom.contentWindow;
21914 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21918 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21919 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21924 initEditor : function(){
21925 //console.log("INIT EDITOR");
21926 this.assignDocWin();
21930 this.doc.designMode="on";
21932 this.doc.write(this.getDocMarkup());
21935 var dbody = (this.doc.body || this.doc.documentElement);
21936 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21937 // this copies styles from the containing element into thsi one..
21938 // not sure why we need all of this..
21939 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21941 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21942 //ss['background-attachment'] = 'fixed'; // w3c
21943 dbody.bgProperties = 'fixed'; // ie
21944 //Roo.DomHelper.applyStyles(dbody, ss);
21945 Roo.EventManager.on(this.doc, {
21946 //'mousedown': this.onEditorEvent,
21947 'mouseup': this.onEditorEvent,
21948 'dblclick': this.onEditorEvent,
21949 'click': this.onEditorEvent,
21950 'keyup': this.onEditorEvent,
21955 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21957 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21958 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21960 this.initialized = true;
21962 this.owner.fireEvent('initialize', this);
21967 onDestroy : function(){
21973 //for (var i =0; i < this.toolbars.length;i++) {
21974 // // fixme - ask toolbars for heights?
21975 // this.toolbars[i].onDestroy();
21978 //this.wrap.dom.innerHTML = '';
21979 //this.wrap.remove();
21984 onFirstFocus : function(){
21986 this.assignDocWin();
21989 this.activated = true;
21992 if(Roo.isGecko){ // prevent silly gecko errors
21994 var s = this.win.getSelection();
21995 if(!s.focusNode || s.focusNode.nodeType != 3){
21996 var r = s.getRangeAt(0);
21997 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22002 this.execCmd('useCSS', true);
22003 this.execCmd('styleWithCSS', false);
22006 this.owner.fireEvent('activate', this);
22010 adjustFont: function(btn){
22011 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22012 //if(Roo.isSafari){ // safari
22015 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22016 if(Roo.isSafari){ // safari
22017 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22018 v = (v < 10) ? 10 : v;
22019 v = (v > 48) ? 48 : v;
22020 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22025 v = Math.max(1, v+adjust);
22027 this.execCmd('FontSize', v );
22030 onEditorEvent : function(e)
22032 this.owner.fireEvent('editorevent', this, e);
22033 // this.updateToolbar();
22034 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22037 insertTag : function(tg)
22039 // could be a bit smarter... -> wrap the current selected tRoo..
22040 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22042 range = this.createRange(this.getSelection());
22043 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22044 wrappingNode.appendChild(range.extractContents());
22045 range.insertNode(wrappingNode);
22052 this.execCmd("formatblock", tg);
22056 insertText : function(txt)
22060 var range = this.createRange();
22061 range.deleteContents();
22062 //alert(Sender.getAttribute('label'));
22064 range.insertNode(this.doc.createTextNode(txt));
22070 * Executes a Midas editor command on the editor document and performs necessary focus and
22071 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22072 * @param {String} cmd The Midas command
22073 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22075 relayCmd : function(cmd, value){
22077 this.execCmd(cmd, value);
22078 this.owner.fireEvent('editorevent', this);
22079 //this.updateToolbar();
22080 this.owner.deferFocus();
22084 * Executes a Midas editor command directly on the editor document.
22085 * For visual commands, you should use {@link #relayCmd} instead.
22086 * <b>This should only be called after the editor is initialized.</b>
22087 * @param {String} cmd The Midas command
22088 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22090 execCmd : function(cmd, value){
22091 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22098 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22100 * @param {String} text | dom node..
22102 insertAtCursor : function(text)
22105 if(!this.activated){
22111 var r = this.doc.selection.createRange();
22122 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22126 // from jquery ui (MIT licenced)
22128 var win = this.win;
22130 if (win.getSelection && win.getSelection().getRangeAt) {
22131 range = win.getSelection().getRangeAt(0);
22132 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22133 range.insertNode(node);
22134 } else if (win.document.selection && win.document.selection.createRange) {
22135 // no firefox support
22136 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22137 win.document.selection.createRange().pasteHTML(txt);
22139 // no firefox support
22140 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22141 this.execCmd('InsertHTML', txt);
22150 mozKeyPress : function(e){
22152 var c = e.getCharCode(), cmd;
22155 c = String.fromCharCode(c).toLowerCase();
22169 this.cleanUpPaste.defer(100, this);
22177 e.preventDefault();
22185 fixKeys : function(){ // load time branching for fastest keydown performance
22187 return function(e){
22188 var k = e.getKey(), r;
22191 r = this.doc.selection.createRange();
22194 r.pasteHTML('    ');
22201 r = this.doc.selection.createRange();
22203 var target = r.parentElement();
22204 if(!target || target.tagName.toLowerCase() != 'li'){
22206 r.pasteHTML('<br />');
22212 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22213 this.cleanUpPaste.defer(100, this);
22219 }else if(Roo.isOpera){
22220 return function(e){
22221 var k = e.getKey();
22225 this.execCmd('InsertHTML','    ');
22228 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22229 this.cleanUpPaste.defer(100, this);
22234 }else if(Roo.isSafari){
22235 return function(e){
22236 var k = e.getKey();
22240 this.execCmd('InsertText','\t');
22244 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22245 this.cleanUpPaste.defer(100, this);
22253 getAllAncestors: function()
22255 var p = this.getSelectedNode();
22258 a.push(p); // push blank onto stack..
22259 p = this.getParentElement();
22263 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22267 a.push(this.doc.body);
22271 lastSelNode : false,
22274 getSelection : function()
22276 this.assignDocWin();
22277 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22280 getSelectedNode: function()
22282 // this may only work on Gecko!!!
22284 // should we cache this!!!!
22289 var range = this.createRange(this.getSelection()).cloneRange();
22292 var parent = range.parentElement();
22294 var testRange = range.duplicate();
22295 testRange.moveToElementText(parent);
22296 if (testRange.inRange(range)) {
22299 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22302 parent = parent.parentElement;
22307 // is ancestor a text element.
22308 var ac = range.commonAncestorContainer;
22309 if (ac.nodeType == 3) {
22310 ac = ac.parentNode;
22313 var ar = ac.childNodes;
22316 var other_nodes = [];
22317 var has_other_nodes = false;
22318 for (var i=0;i<ar.length;i++) {
22319 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22322 // fullly contained node.
22324 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22329 // probably selected..
22330 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22331 other_nodes.push(ar[i]);
22335 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22340 has_other_nodes = true;
22342 if (!nodes.length && other_nodes.length) {
22343 nodes= other_nodes;
22345 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22351 createRange: function(sel)
22353 // this has strange effects when using with
22354 // top toolbar - not sure if it's a great idea.
22355 //this.editor.contentWindow.focus();
22356 if (typeof sel != "undefined") {
22358 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22360 return this.doc.createRange();
22363 return this.doc.createRange();
22366 getParentElement: function()
22369 this.assignDocWin();
22370 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22372 var range = this.createRange(sel);
22375 var p = range.commonAncestorContainer;
22376 while (p.nodeType == 3) { // text node
22387 * Range intersection.. the hard stuff...
22391 * [ -- selected range --- ]
22395 * if end is before start or hits it. fail.
22396 * if start is after end or hits it fail.
22398 * if either hits (but other is outside. - then it's not
22404 // @see http://www.thismuchiknow.co.uk/?p=64.
22405 rangeIntersectsNode : function(range, node)
22407 var nodeRange = node.ownerDocument.createRange();
22409 nodeRange.selectNode(node);
22411 nodeRange.selectNodeContents(node);
22414 var rangeStartRange = range.cloneRange();
22415 rangeStartRange.collapse(true);
22417 var rangeEndRange = range.cloneRange();
22418 rangeEndRange.collapse(false);
22420 var nodeStartRange = nodeRange.cloneRange();
22421 nodeStartRange.collapse(true);
22423 var nodeEndRange = nodeRange.cloneRange();
22424 nodeEndRange.collapse(false);
22426 return rangeStartRange.compareBoundaryPoints(
22427 Range.START_TO_START, nodeEndRange) == -1 &&
22428 rangeEndRange.compareBoundaryPoints(
22429 Range.START_TO_START, nodeStartRange) == 1;
22433 rangeCompareNode : function(range, node)
22435 var nodeRange = node.ownerDocument.createRange();
22437 nodeRange.selectNode(node);
22439 nodeRange.selectNodeContents(node);
22443 range.collapse(true);
22445 nodeRange.collapse(true);
22447 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22448 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22450 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22452 var nodeIsBefore = ss == 1;
22453 var nodeIsAfter = ee == -1;
22455 if (nodeIsBefore && nodeIsAfter) {
22458 if (!nodeIsBefore && nodeIsAfter) {
22459 return 1; //right trailed.
22462 if (nodeIsBefore && !nodeIsAfter) {
22463 return 2; // left trailed.
22469 // private? - in a new class?
22470 cleanUpPaste : function()
22472 // cleans up the whole document..
22473 Roo.log('cleanuppaste');
22475 this.cleanUpChildren(this.doc.body);
22476 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22477 if (clean != this.doc.body.innerHTML) {
22478 this.doc.body.innerHTML = clean;
22483 cleanWordChars : function(input) {// change the chars to hex code
22484 var he = Roo.HtmlEditorCore;
22486 var output = input;
22487 Roo.each(he.swapCodes, function(sw) {
22488 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22490 output = output.replace(swapper, sw[1]);
22497 cleanUpChildren : function (n)
22499 if (!n.childNodes.length) {
22502 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22503 this.cleanUpChild(n.childNodes[i]);
22510 cleanUpChild : function (node)
22513 //console.log(node);
22514 if (node.nodeName == "#text") {
22515 // clean up silly Windows -- stuff?
22518 if (node.nodeName == "#comment") {
22519 node.parentNode.removeChild(node);
22520 // clean up silly Windows -- stuff?
22523 var lcname = node.tagName.toLowerCase();
22524 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22525 // whitelist of tags..
22527 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22529 node.parentNode.removeChild(node);
22534 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22536 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22537 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22539 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22540 // remove_keep_children = true;
22543 if (remove_keep_children) {
22544 this.cleanUpChildren(node);
22545 // inserts everything just before this node...
22546 while (node.childNodes.length) {
22547 var cn = node.childNodes[0];
22548 node.removeChild(cn);
22549 node.parentNode.insertBefore(cn, node);
22551 node.parentNode.removeChild(node);
22555 if (!node.attributes || !node.attributes.length) {
22556 this.cleanUpChildren(node);
22560 function cleanAttr(n,v)
22563 if (v.match(/^\./) || v.match(/^\//)) {
22566 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22569 if (v.match(/^#/)) {
22572 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22573 node.removeAttribute(n);
22577 var cwhite = this.cwhite;
22578 var cblack = this.cblack;
22580 function cleanStyle(n,v)
22582 if (v.match(/expression/)) { //XSS?? should we even bother..
22583 node.removeAttribute(n);
22587 var parts = v.split(/;/);
22590 Roo.each(parts, function(p) {
22591 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22595 var l = p.split(':').shift().replace(/\s+/g,'');
22596 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22598 if ( cwhite.length && cblack.indexOf(l) > -1) {
22599 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22600 //node.removeAttribute(n);
22604 // only allow 'c whitelisted system attributes'
22605 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22606 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22607 //node.removeAttribute(n);
22617 if (clean.length) {
22618 node.setAttribute(n, clean.join(';'));
22620 node.removeAttribute(n);
22626 for (var i = node.attributes.length-1; i > -1 ; i--) {
22627 var a = node.attributes[i];
22630 if (a.name.toLowerCase().substr(0,2)=='on') {
22631 node.removeAttribute(a.name);
22634 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22635 node.removeAttribute(a.name);
22638 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22639 cleanAttr(a.name,a.value); // fixme..
22642 if (a.name == 'style') {
22643 cleanStyle(a.name,a.value);
22646 /// clean up MS crap..
22647 // tecnically this should be a list of valid class'es..
22650 if (a.name == 'class') {
22651 if (a.value.match(/^Mso/)) {
22652 node.className = '';
22655 if (a.value.match(/^body$/)) {
22656 node.className = '';
22667 this.cleanUpChildren(node);
22673 * Clean up MS wordisms...
22675 cleanWord : function(node)
22680 this.cleanWord(this.doc.body);
22683 if (node.nodeName == "#text") {
22684 // clean up silly Windows -- stuff?
22687 if (node.nodeName == "#comment") {
22688 node.parentNode.removeChild(node);
22689 // clean up silly Windows -- stuff?
22693 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22694 node.parentNode.removeChild(node);
22698 // remove - but keep children..
22699 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22700 while (node.childNodes.length) {
22701 var cn = node.childNodes[0];
22702 node.removeChild(cn);
22703 node.parentNode.insertBefore(cn, node);
22705 node.parentNode.removeChild(node);
22706 this.iterateChildren(node, this.cleanWord);
22710 if (node.className.length) {
22712 var cn = node.className.split(/\W+/);
22714 Roo.each(cn, function(cls) {
22715 if (cls.match(/Mso[a-zA-Z]+/)) {
22720 node.className = cna.length ? cna.join(' ') : '';
22722 node.removeAttribute("class");
22726 if (node.hasAttribute("lang")) {
22727 node.removeAttribute("lang");
22730 if (node.hasAttribute("style")) {
22732 var styles = node.getAttribute("style").split(";");
22734 Roo.each(styles, function(s) {
22735 if (!s.match(/:/)) {
22738 var kv = s.split(":");
22739 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22742 // what ever is left... we allow.
22745 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22746 if (!nstyle.length) {
22747 node.removeAttribute('style');
22750 this.iterateChildren(node, this.cleanWord);
22756 * iterateChildren of a Node, calling fn each time, using this as the scole..
22757 * @param {DomNode} node node to iterate children of.
22758 * @param {Function} fn method of this class to call on each item.
22760 iterateChildren : function(node, fn)
22762 if (!node.childNodes.length) {
22765 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22766 fn.call(this, node.childNodes[i])
22772 * cleanTableWidths.
22774 * Quite often pasting from word etc.. results in tables with column and widths.
22775 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22778 cleanTableWidths : function(node)
22783 this.cleanTableWidths(this.doc.body);
22788 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22791 Roo.log(node.tagName);
22792 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22793 this.iterateChildren(node, this.cleanTableWidths);
22796 if (node.hasAttribute('width')) {
22797 node.removeAttribute('width');
22801 if (node.hasAttribute("style")) {
22804 var styles = node.getAttribute("style").split(";");
22806 Roo.each(styles, function(s) {
22807 if (!s.match(/:/)) {
22810 var kv = s.split(":");
22811 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22814 // what ever is left... we allow.
22817 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22818 if (!nstyle.length) {
22819 node.removeAttribute('style');
22823 this.iterateChildren(node, this.cleanTableWidths);
22831 domToHTML : function(currentElement, depth, nopadtext) {
22833 depth = depth || 0;
22834 nopadtext = nopadtext || false;
22836 if (!currentElement) {
22837 return this.domToHTML(this.doc.body);
22840 //Roo.log(currentElement);
22842 var allText = false;
22843 var nodeName = currentElement.nodeName;
22844 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22846 if (nodeName == '#text') {
22848 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22853 if (nodeName != 'BODY') {
22856 // Prints the node tagName, such as <A>, <IMG>, etc
22859 for(i = 0; i < currentElement.attributes.length;i++) {
22861 var aname = currentElement.attributes.item(i).name;
22862 if (!currentElement.attributes.item(i).value.length) {
22865 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22868 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22877 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22880 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22885 // Traverse the tree
22887 var currentElementChild = currentElement.childNodes.item(i);
22888 var allText = true;
22889 var innerHTML = '';
22891 while (currentElementChild) {
22892 // Formatting code (indent the tree so it looks nice on the screen)
22893 var nopad = nopadtext;
22894 if (lastnode == 'SPAN') {
22898 if (currentElementChild.nodeName == '#text') {
22899 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22900 toadd = nopadtext ? toadd : toadd.trim();
22901 if (!nopad && toadd.length > 80) {
22902 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22904 innerHTML += toadd;
22907 currentElementChild = currentElement.childNodes.item(i);
22913 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22915 // Recursively traverse the tree structure of the child node
22916 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22917 lastnode = currentElementChild.nodeName;
22919 currentElementChild=currentElement.childNodes.item(i);
22925 // The remaining code is mostly for formatting the tree
22926 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22931 ret+= "</"+tagName+">";
22937 applyBlacklists : function()
22939 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22940 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22944 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22945 if (b.indexOf(tag) > -1) {
22948 this.white.push(tag);
22952 Roo.each(w, function(tag) {
22953 if (b.indexOf(tag) > -1) {
22956 if (this.white.indexOf(tag) > -1) {
22959 this.white.push(tag);
22964 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22965 if (w.indexOf(tag) > -1) {
22968 this.black.push(tag);
22972 Roo.each(b, function(tag) {
22973 if (w.indexOf(tag) > -1) {
22976 if (this.black.indexOf(tag) > -1) {
22979 this.black.push(tag);
22984 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22985 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22989 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22990 if (b.indexOf(tag) > -1) {
22993 this.cwhite.push(tag);
22997 Roo.each(w, function(tag) {
22998 if (b.indexOf(tag) > -1) {
23001 if (this.cwhite.indexOf(tag) > -1) {
23004 this.cwhite.push(tag);
23009 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23010 if (w.indexOf(tag) > -1) {
23013 this.cblack.push(tag);
23017 Roo.each(b, function(tag) {
23018 if (w.indexOf(tag) > -1) {
23021 if (this.cblack.indexOf(tag) > -1) {
23024 this.cblack.push(tag);
23029 setStylesheets : function(stylesheets)
23031 if(typeof(stylesheets) == 'string'){
23032 Roo.get(this.iframe.contentDocument.head).createChild({
23034 rel : 'stylesheet',
23043 Roo.each(stylesheets, function(s) {
23048 Roo.get(_this.iframe.contentDocument.head).createChild({
23050 rel : 'stylesheet',
23059 removeStylesheets : function()
23063 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23068 setStyle : function(style)
23070 Roo.get(this.iframe.contentDocument.head).createChild({
23079 // hide stuff that is not compatible
23093 * @event specialkey
23097 * @cfg {String} fieldClass @hide
23100 * @cfg {String} focusClass @hide
23103 * @cfg {String} autoCreate @hide
23106 * @cfg {String} inputType @hide
23109 * @cfg {String} invalidClass @hide
23112 * @cfg {String} invalidText @hide
23115 * @cfg {String} msgFx @hide
23118 * @cfg {String} validateOnBlur @hide
23122 Roo.HtmlEditorCore.white = [
23123 'area', 'br', 'img', 'input', 'hr', 'wbr',
23125 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23126 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23127 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23128 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23129 'table', 'ul', 'xmp',
23131 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23134 'dir', 'menu', 'ol', 'ul', 'dl',
23140 Roo.HtmlEditorCore.black = [
23141 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23143 'base', 'basefont', 'bgsound', 'blink', 'body',
23144 'frame', 'frameset', 'head', 'html', 'ilayer',
23145 'iframe', 'layer', 'link', 'meta', 'object',
23146 'script', 'style' ,'title', 'xml' // clean later..
23148 Roo.HtmlEditorCore.clean = [
23149 'script', 'style', 'title', 'xml'
23151 Roo.HtmlEditorCore.remove = [
23156 Roo.HtmlEditorCore.ablack = [
23160 Roo.HtmlEditorCore.aclean = [
23161 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23165 Roo.HtmlEditorCore.pwhite= [
23166 'http', 'https', 'mailto'
23169 // white listed style attributes.
23170 Roo.HtmlEditorCore.cwhite= [
23171 // 'text-align', /// default is to allow most things..
23177 // black listed style attributes.
23178 Roo.HtmlEditorCore.cblack= [
23179 // 'font-size' -- this can be set by the project
23183 Roo.HtmlEditorCore.swapCodes =[
23202 * @class Roo.bootstrap.HtmlEditor
23203 * @extends Roo.bootstrap.TextArea
23204 * Bootstrap HtmlEditor class
23207 * Create a new HtmlEditor
23208 * @param {Object} config The config object
23211 Roo.bootstrap.HtmlEditor = function(config){
23212 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23213 if (!this.toolbars) {
23214 this.toolbars = [];
23217 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23220 * @event initialize
23221 * Fires when the editor is fully initialized (including the iframe)
23222 * @param {HtmlEditor} this
23227 * Fires when the editor is first receives the focus. Any insertion must wait
23228 * until after this event.
23229 * @param {HtmlEditor} this
23233 * @event beforesync
23234 * Fires before the textarea is updated with content from the editor iframe. Return false
23235 * to cancel the sync.
23236 * @param {HtmlEditor} this
23237 * @param {String} html
23241 * @event beforepush
23242 * Fires before the iframe editor is updated with content from the textarea. Return false
23243 * to cancel the push.
23244 * @param {HtmlEditor} this
23245 * @param {String} html
23250 * Fires when the textarea is updated with content from the editor iframe.
23251 * @param {HtmlEditor} this
23252 * @param {String} html
23257 * Fires when the iframe editor is updated with content from the textarea.
23258 * @param {HtmlEditor} this
23259 * @param {String} html
23263 * @event editmodechange
23264 * Fires when the editor switches edit modes
23265 * @param {HtmlEditor} this
23266 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23268 editmodechange: true,
23270 * @event editorevent
23271 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23272 * @param {HtmlEditor} this
23276 * @event firstfocus
23277 * Fires when on first focus - needed by toolbars..
23278 * @param {HtmlEditor} this
23283 * Auto save the htmlEditor value as a file into Events
23284 * @param {HtmlEditor} this
23288 * @event savedpreview
23289 * preview the saved version of htmlEditor
23290 * @param {HtmlEditor} this
23297 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23301 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23306 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23311 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23316 * @cfg {Number} height (in pixels)
23320 * @cfg {Number} width (in pixels)
23325 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23328 stylesheets: false,
23333 // private properties
23334 validationEvent : false,
23336 initialized : false,
23339 onFocus : Roo.emptyFn,
23341 hideMode:'offsets',
23343 tbContainer : false,
23347 toolbarContainer :function() {
23348 return this.wrap.select('.x-html-editor-tb',true).first();
23352 * Protected method that will not generally be called directly. It
23353 * is called when the editor creates its toolbar. Override this method if you need to
23354 * add custom toolbar buttons.
23355 * @param {HtmlEditor} editor
23357 createToolbar : function(){
23358 Roo.log('renewing');
23359 Roo.log("create toolbars");
23361 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23362 this.toolbars[0].render(this.toolbarContainer());
23366 // if (!editor.toolbars || !editor.toolbars.length) {
23367 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23370 // for (var i =0 ; i < editor.toolbars.length;i++) {
23371 // editor.toolbars[i] = Roo.factory(
23372 // typeof(editor.toolbars[i]) == 'string' ?
23373 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23374 // Roo.bootstrap.HtmlEditor);
23375 // editor.toolbars[i].init(editor);
23381 onRender : function(ct, position)
23383 // Roo.log("Call onRender: " + this.xtype);
23385 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23387 this.wrap = this.inputEl().wrap({
23388 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23391 this.editorcore.onRender(ct, position);
23393 if (this.resizable) {
23394 this.resizeEl = new Roo.Resizable(this.wrap, {
23398 minHeight : this.height,
23399 height: this.height,
23400 handles : this.resizable,
23403 resize : function(r, w, h) {
23404 _t.onResize(w,h); // -something
23410 this.createToolbar(this);
23413 if(!this.width && this.resizable){
23414 this.setSize(this.wrap.getSize());
23416 if (this.resizeEl) {
23417 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23418 // should trigger onReize..
23424 onResize : function(w, h)
23426 Roo.log('resize: ' +w + ',' + h );
23427 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23431 if(this.inputEl() ){
23432 if(typeof w == 'number'){
23433 var aw = w - this.wrap.getFrameWidth('lr');
23434 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23437 if(typeof h == 'number'){
23438 var tbh = -11; // fixme it needs to tool bar size!
23439 for (var i =0; i < this.toolbars.length;i++) {
23440 // fixme - ask toolbars for heights?
23441 tbh += this.toolbars[i].el.getHeight();
23442 //if (this.toolbars[i].footer) {
23443 // tbh += this.toolbars[i].footer.el.getHeight();
23451 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23452 ah -= 5; // knock a few pixes off for look..
23453 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23457 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23458 this.editorcore.onResize(ew,eh);
23463 * Toggles the editor between standard and source edit mode.
23464 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23466 toggleSourceEdit : function(sourceEditMode)
23468 this.editorcore.toggleSourceEdit(sourceEditMode);
23470 if(this.editorcore.sourceEditMode){
23471 Roo.log('editor - showing textarea');
23474 // Roo.log(this.syncValue());
23476 this.inputEl().removeClass(['hide', 'x-hidden']);
23477 this.inputEl().dom.removeAttribute('tabIndex');
23478 this.inputEl().focus();
23480 Roo.log('editor - hiding textarea');
23482 // Roo.log(this.pushValue());
23485 this.inputEl().addClass(['hide', 'x-hidden']);
23486 this.inputEl().dom.setAttribute('tabIndex', -1);
23487 //this.deferFocus();
23490 if(this.resizable){
23491 this.setSize(this.wrap.getSize());
23494 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23497 // private (for BoxComponent)
23498 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23500 // private (for BoxComponent)
23501 getResizeEl : function(){
23505 // private (for BoxComponent)
23506 getPositionEl : function(){
23511 initEvents : function(){
23512 this.originalValue = this.getValue();
23516 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23519 // markInvalid : Roo.emptyFn,
23521 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23524 // clearInvalid : Roo.emptyFn,
23526 setValue : function(v){
23527 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23528 this.editorcore.pushValue();
23533 deferFocus : function(){
23534 this.focus.defer(10, this);
23538 focus : function(){
23539 this.editorcore.focus();
23545 onDestroy : function(){
23551 for (var i =0; i < this.toolbars.length;i++) {
23552 // fixme - ask toolbars for heights?
23553 this.toolbars[i].onDestroy();
23556 this.wrap.dom.innerHTML = '';
23557 this.wrap.remove();
23562 onFirstFocus : function(){
23563 //Roo.log("onFirstFocus");
23564 this.editorcore.onFirstFocus();
23565 for (var i =0; i < this.toolbars.length;i++) {
23566 this.toolbars[i].onFirstFocus();
23572 syncValue : function()
23574 this.editorcore.syncValue();
23577 pushValue : function()
23579 this.editorcore.pushValue();
23583 // hide stuff that is not compatible
23597 * @event specialkey
23601 * @cfg {String} fieldClass @hide
23604 * @cfg {String} focusClass @hide
23607 * @cfg {String} autoCreate @hide
23610 * @cfg {String} inputType @hide
23613 * @cfg {String} invalidClass @hide
23616 * @cfg {String} invalidText @hide
23619 * @cfg {String} msgFx @hide
23622 * @cfg {String} validateOnBlur @hide
23631 Roo.namespace('Roo.bootstrap.htmleditor');
23633 * @class Roo.bootstrap.HtmlEditorToolbar1
23638 new Roo.bootstrap.HtmlEditor({
23641 new Roo.bootstrap.HtmlEditorToolbar1({
23642 disable : { fonts: 1 , format: 1, ..., ... , ...],
23648 * @cfg {Object} disable List of elements to disable..
23649 * @cfg {Array} btns List of additional buttons.
23653 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23656 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23659 Roo.apply(this, config);
23661 // default disabled, based on 'good practice'..
23662 this.disable = this.disable || {};
23663 Roo.applyIf(this.disable, {
23666 specialElements : true
23668 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23670 this.editor = config.editor;
23671 this.editorcore = config.editor.editorcore;
23673 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23675 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23676 // dont call parent... till later.
23678 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23683 editorcore : false,
23688 "h1","h2","h3","h4","h5","h6",
23690 "abbr", "acronym", "address", "cite", "samp", "var",
23694 onRender : function(ct, position)
23696 // Roo.log("Call onRender: " + this.xtype);
23698 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23700 this.el.dom.style.marginBottom = '0';
23702 var editorcore = this.editorcore;
23703 var editor= this.editor;
23706 var btn = function(id,cmd , toggle, handler, html){
23708 var event = toggle ? 'toggle' : 'click';
23713 xns: Roo.bootstrap,
23716 enableToggle:toggle !== false,
23718 pressed : toggle ? false : null,
23721 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23722 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23728 // var cb_box = function...
23733 xns: Roo.bootstrap,
23734 glyphicon : 'font',
23738 xns: Roo.bootstrap,
23742 Roo.each(this.formats, function(f) {
23743 style.menu.items.push({
23745 xns: Roo.bootstrap,
23746 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23751 editorcore.insertTag(this.tagname);
23758 children.push(style);
23760 btn('bold',false,true);
23761 btn('italic',false,true);
23762 btn('align-left', 'justifyleft',true);
23763 btn('align-center', 'justifycenter',true);
23764 btn('align-right' , 'justifyright',true);
23765 btn('link', false, false, function(btn) {
23766 //Roo.log("create link?");
23767 var url = prompt(this.createLinkText, this.defaultLinkValue);
23768 if(url && url != 'http:/'+'/'){
23769 this.editorcore.relayCmd('createlink', url);
23772 btn('list','insertunorderedlist',true);
23773 btn('pencil', false,true, function(btn){
23775 this.toggleSourceEdit(btn.pressed);
23778 if (this.editor.btns.length > 0) {
23779 for (var i = 0; i<this.editor.btns.length; i++) {
23780 children.push(this.editor.btns[i]);
23788 xns: Roo.bootstrap,
23793 xns: Roo.bootstrap,
23798 cog.menu.items.push({
23800 xns: Roo.bootstrap,
23801 html : Clean styles,
23806 editorcore.insertTag(this.tagname);
23815 this.xtype = 'NavSimplebar';
23817 for(var i=0;i< children.length;i++) {
23819 this.buttons.add(this.addxtypeChild(children[i]));
23823 editor.on('editorevent', this.updateToolbar, this);
23825 onBtnClick : function(id)
23827 this.editorcore.relayCmd(id);
23828 this.editorcore.focus();
23832 * Protected method that will not generally be called directly. It triggers
23833 * a toolbar update by reading the markup state of the current selection in the editor.
23835 updateToolbar: function(){
23837 if(!this.editorcore.activated){
23838 this.editor.onFirstFocus(); // is this neeed?
23842 var btns = this.buttons;
23843 var doc = this.editorcore.doc;
23844 btns.get('bold').setActive(doc.queryCommandState('bold'));
23845 btns.get('italic').setActive(doc.queryCommandState('italic'));
23846 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23848 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23849 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23850 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23852 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23853 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23856 var ans = this.editorcore.getAllAncestors();
23857 if (this.formatCombo) {
23860 var store = this.formatCombo.store;
23861 this.formatCombo.setValue("");
23862 for (var i =0; i < ans.length;i++) {
23863 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23865 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23873 // hides menus... - so this cant be on a menu...
23874 Roo.bootstrap.MenuMgr.hideAll();
23876 Roo.bootstrap.MenuMgr.hideAll();
23877 //this.editorsyncValue();
23879 onFirstFocus: function() {
23880 this.buttons.each(function(item){
23884 toggleSourceEdit : function(sourceEditMode){
23887 if(sourceEditMode){
23888 Roo.log("disabling buttons");
23889 this.buttons.each( function(item){
23890 if(item.cmd != 'pencil'){
23896 Roo.log("enabling buttons");
23897 if(this.editorcore.initialized){
23898 this.buttons.each( function(item){
23904 Roo.log("calling toggole on editor");
23905 // tell the editor that it's been pressed..
23906 this.editor.toggleSourceEdit(sourceEditMode);
23916 * @class Roo.bootstrap.Table.AbstractSelectionModel
23917 * @extends Roo.util.Observable
23918 * Abstract base class for grid SelectionModels. It provides the interface that should be
23919 * implemented by descendant classes. This class should not be directly instantiated.
23922 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23923 this.locked = false;
23924 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23928 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23929 /** @ignore Called by the grid automatically. Do not call directly. */
23930 init : function(grid){
23936 * Locks the selections.
23939 this.locked = true;
23943 * Unlocks the selections.
23945 unlock : function(){
23946 this.locked = false;
23950 * Returns true if the selections are locked.
23951 * @return {Boolean}
23953 isLocked : function(){
23954 return this.locked;
23958 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23959 * @class Roo.bootstrap.Table.RowSelectionModel
23960 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23961 * It supports multiple selections and keyboard selection/navigation.
23963 * @param {Object} config
23966 Roo.bootstrap.Table.RowSelectionModel = function(config){
23967 Roo.apply(this, config);
23968 this.selections = new Roo.util.MixedCollection(false, function(o){
23973 this.lastActive = false;
23977 * @event selectionchange
23978 * Fires when the selection changes
23979 * @param {SelectionModel} this
23981 "selectionchange" : true,
23983 * @event afterselectionchange
23984 * Fires after the selection changes (eg. by key press or clicking)
23985 * @param {SelectionModel} this
23987 "afterselectionchange" : true,
23989 * @event beforerowselect
23990 * Fires when a row is selected being selected, return false to cancel.
23991 * @param {SelectionModel} this
23992 * @param {Number} rowIndex The selected index
23993 * @param {Boolean} keepExisting False if other selections will be cleared
23995 "beforerowselect" : true,
23998 * Fires when a row is selected.
23999 * @param {SelectionModel} this
24000 * @param {Number} rowIndex The selected index
24001 * @param {Roo.data.Record} r The record
24003 "rowselect" : true,
24005 * @event rowdeselect
24006 * Fires when a row is deselected.
24007 * @param {SelectionModel} this
24008 * @param {Number} rowIndex The selected index
24010 "rowdeselect" : true
24012 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24013 this.locked = false;
24016 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24018 * @cfg {Boolean} singleSelect
24019 * True to allow selection of only one row at a time (defaults to false)
24021 singleSelect : false,
24024 initEvents : function()
24027 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24028 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24029 //}else{ // allow click to work like normal
24030 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24032 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24033 this.grid.on("rowclick", this.handleMouseDown, this);
24035 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24036 "up" : function(e){
24038 this.selectPrevious(e.shiftKey);
24039 }else if(this.last !== false && this.lastActive !== false){
24040 var last = this.last;
24041 this.selectRange(this.last, this.lastActive-1);
24042 this.grid.getView().focusRow(this.lastActive);
24043 if(last !== false){
24047 this.selectFirstRow();
24049 this.fireEvent("afterselectionchange", this);
24051 "down" : function(e){
24053 this.selectNext(e.shiftKey);
24054 }else if(this.last !== false && this.lastActive !== false){
24055 var last = this.last;
24056 this.selectRange(this.last, this.lastActive+1);
24057 this.grid.getView().focusRow(this.lastActive);
24058 if(last !== false){
24062 this.selectFirstRow();
24064 this.fireEvent("afterselectionchange", this);
24068 this.grid.store.on('load', function(){
24069 this.selections.clear();
24072 var view = this.grid.view;
24073 view.on("refresh", this.onRefresh, this);
24074 view.on("rowupdated", this.onRowUpdated, this);
24075 view.on("rowremoved", this.onRemove, this);
24080 onRefresh : function()
24082 var ds = this.grid.store, i, v = this.grid.view;
24083 var s = this.selections;
24084 s.each(function(r){
24085 if((i = ds.indexOfId(r.id)) != -1){
24094 onRemove : function(v, index, r){
24095 this.selections.remove(r);
24099 onRowUpdated : function(v, index, r){
24100 if(this.isSelected(r)){
24101 v.onRowSelect(index);
24107 * @param {Array} records The records to select
24108 * @param {Boolean} keepExisting (optional) True to keep existing selections
24110 selectRecords : function(records, keepExisting)
24113 this.clearSelections();
24115 var ds = this.grid.store;
24116 for(var i = 0, len = records.length; i < len; i++){
24117 this.selectRow(ds.indexOf(records[i]), true);
24122 * Gets the number of selected rows.
24125 getCount : function(){
24126 return this.selections.length;
24130 * Selects the first row in the grid.
24132 selectFirstRow : function(){
24137 * Select the last row.
24138 * @param {Boolean} keepExisting (optional) True to keep existing selections
24140 selectLastRow : function(keepExisting){
24141 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24142 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24146 * Selects the row immediately following the last selected row.
24147 * @param {Boolean} keepExisting (optional) True to keep existing selections
24149 selectNext : function(keepExisting)
24151 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24152 this.selectRow(this.last+1, keepExisting);
24153 this.grid.getView().focusRow(this.last);
24158 * Selects the row that precedes the last selected row.
24159 * @param {Boolean} keepExisting (optional) True to keep existing selections
24161 selectPrevious : function(keepExisting){
24163 this.selectRow(this.last-1, keepExisting);
24164 this.grid.getView().focusRow(this.last);
24169 * Returns the selected records
24170 * @return {Array} Array of selected records
24172 getSelections : function(){
24173 return [].concat(this.selections.items);
24177 * Returns the first selected record.
24180 getSelected : function(){
24181 return this.selections.itemAt(0);
24186 * Clears all selections.
24188 clearSelections : function(fast)
24194 var ds = this.grid.store;
24195 var s = this.selections;
24196 s.each(function(r){
24197 this.deselectRow(ds.indexOfId(r.id));
24201 this.selections.clear();
24208 * Selects all rows.
24210 selectAll : function(){
24214 this.selections.clear();
24215 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24216 this.selectRow(i, true);
24221 * Returns True if there is a selection.
24222 * @return {Boolean}
24224 hasSelection : function(){
24225 return this.selections.length > 0;
24229 * Returns True if the specified row is selected.
24230 * @param {Number/Record} record The record or index of the record to check
24231 * @return {Boolean}
24233 isSelected : function(index){
24234 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24235 return (r && this.selections.key(r.id) ? true : false);
24239 * Returns True if the specified record id is selected.
24240 * @param {String} id The id of record to check
24241 * @return {Boolean}
24243 isIdSelected : function(id){
24244 return (this.selections.key(id) ? true : false);
24249 handleMouseDBClick : function(e, t){
24253 handleMouseDown : function(e, t)
24255 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24256 if(this.isLocked() || rowIndex < 0 ){
24259 if(e.shiftKey && this.last !== false){
24260 var last = this.last;
24261 this.selectRange(last, rowIndex, e.ctrlKey);
24262 this.last = last; // reset the last
24266 var isSelected = this.isSelected(rowIndex);
24267 //Roo.log("select row:" + rowIndex);
24269 this.deselectRow(rowIndex);
24271 this.selectRow(rowIndex, true);
24275 if(e.button !== 0 && isSelected){
24276 alert('rowIndex 2: ' + rowIndex);
24277 view.focusRow(rowIndex);
24278 }else if(e.ctrlKey && isSelected){
24279 this.deselectRow(rowIndex);
24280 }else if(!isSelected){
24281 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24282 view.focusRow(rowIndex);
24286 this.fireEvent("afterselectionchange", this);
24289 handleDragableRowClick : function(grid, rowIndex, e)
24291 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24292 this.selectRow(rowIndex, false);
24293 grid.view.focusRow(rowIndex);
24294 this.fireEvent("afterselectionchange", this);
24299 * Selects multiple rows.
24300 * @param {Array} rows Array of the indexes of the row to select
24301 * @param {Boolean} keepExisting (optional) True to keep existing selections
24303 selectRows : function(rows, keepExisting){
24305 this.clearSelections();
24307 for(var i = 0, len = rows.length; i < len; i++){
24308 this.selectRow(rows[i], true);
24313 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24314 * @param {Number} startRow The index of the first row in the range
24315 * @param {Number} endRow The index of the last row in the range
24316 * @param {Boolean} keepExisting (optional) True to retain existing selections
24318 selectRange : function(startRow, endRow, keepExisting){
24323 this.clearSelections();
24325 if(startRow <= endRow){
24326 for(var i = startRow; i <= endRow; i++){
24327 this.selectRow(i, true);
24330 for(var i = startRow; i >= endRow; i--){
24331 this.selectRow(i, true);
24337 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24338 * @param {Number} startRow The index of the first row in the range
24339 * @param {Number} endRow The index of the last row in the range
24341 deselectRange : function(startRow, endRow, preventViewNotify){
24345 for(var i = startRow; i <= endRow; i++){
24346 this.deselectRow(i, preventViewNotify);
24352 * @param {Number} row The index of the row to select
24353 * @param {Boolean} keepExisting (optional) True to keep existing selections
24355 selectRow : function(index, keepExisting, preventViewNotify)
24357 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24360 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24361 if(!keepExisting || this.singleSelect){
24362 this.clearSelections();
24365 var r = this.grid.store.getAt(index);
24366 //console.log('selectRow - record id :' + r.id);
24368 this.selections.add(r);
24369 this.last = this.lastActive = index;
24370 if(!preventViewNotify){
24371 var proxy = new Roo.Element(
24372 this.grid.getRowDom(index)
24374 proxy.addClass('bg-info info');
24376 this.fireEvent("rowselect", this, index, r);
24377 this.fireEvent("selectionchange", this);
24383 * @param {Number} row The index of the row to deselect
24385 deselectRow : function(index, preventViewNotify)
24390 if(this.last == index){
24393 if(this.lastActive == index){
24394 this.lastActive = false;
24397 var r = this.grid.store.getAt(index);
24402 this.selections.remove(r);
24403 //.console.log('deselectRow - record id :' + r.id);
24404 if(!preventViewNotify){
24406 var proxy = new Roo.Element(
24407 this.grid.getRowDom(index)
24409 proxy.removeClass('bg-info info');
24411 this.fireEvent("rowdeselect", this, index);
24412 this.fireEvent("selectionchange", this);
24416 restoreLast : function(){
24418 this.last = this._last;
24423 acceptsNav : function(row, col, cm){
24424 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24428 onEditorKey : function(field, e){
24429 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24434 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24436 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24438 }else if(k == e.ENTER && !e.ctrlKey){
24442 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24444 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24446 }else if(k == e.ESC){
24450 g.startEditing(newCell[0], newCell[1]);
24456 * Ext JS Library 1.1.1
24457 * Copyright(c) 2006-2007, Ext JS, LLC.
24459 * Originally Released Under LGPL - original licence link has changed is not relivant.
24462 * <script type="text/javascript">
24466 * @class Roo.bootstrap.PagingToolbar
24467 * @extends Roo.bootstrap.NavSimplebar
24468 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24470 * Create a new PagingToolbar
24471 * @param {Object} config The config object
24472 * @param {Roo.data.Store} store
24474 Roo.bootstrap.PagingToolbar = function(config)
24476 // old args format still supported... - xtype is prefered..
24477 // created from xtype...
24479 this.ds = config.dataSource;
24481 if (config.store && !this.ds) {
24482 this.store= Roo.factory(config.store, Roo.data);
24483 this.ds = this.store;
24484 this.ds.xmodule = this.xmodule || false;
24487 this.toolbarItems = [];
24488 if (config.items) {
24489 this.toolbarItems = config.items;
24492 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24497 this.bind(this.ds);
24500 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24504 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24506 * @cfg {Roo.data.Store} dataSource
24507 * The underlying data store providing the paged data
24510 * @cfg {String/HTMLElement/Element} container
24511 * container The id or element that will contain the toolbar
24514 * @cfg {Boolean} displayInfo
24515 * True to display the displayMsg (defaults to false)
24518 * @cfg {Number} pageSize
24519 * The number of records to display per page (defaults to 20)
24523 * @cfg {String} displayMsg
24524 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24526 displayMsg : 'Displaying {0} - {1} of {2}',
24528 * @cfg {String} emptyMsg
24529 * The message to display when no records are found (defaults to "No data to display")
24531 emptyMsg : 'No data to display',
24533 * Customizable piece of the default paging text (defaults to "Page")
24536 beforePageText : "Page",
24538 * Customizable piece of the default paging text (defaults to "of %0")
24541 afterPageText : "of {0}",
24543 * Customizable piece of the default paging text (defaults to "First Page")
24546 firstText : "First Page",
24548 * Customizable piece of the default paging text (defaults to "Previous Page")
24551 prevText : "Previous Page",
24553 * Customizable piece of the default paging text (defaults to "Next Page")
24556 nextText : "Next Page",
24558 * Customizable piece of the default paging text (defaults to "Last Page")
24561 lastText : "Last Page",
24563 * Customizable piece of the default paging text (defaults to "Refresh")
24566 refreshText : "Refresh",
24570 onRender : function(ct, position)
24572 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24573 this.navgroup.parentId = this.id;
24574 this.navgroup.onRender(this.el, null);
24575 // add the buttons to the navgroup
24577 if(this.displayInfo){
24578 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24579 this.displayEl = this.el.select('.x-paging-info', true).first();
24580 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24581 // this.displayEl = navel.el.select('span',true).first();
24587 Roo.each(_this.buttons, function(e){ // this might need to use render????
24588 Roo.factory(e).render(_this.el);
24592 Roo.each(_this.toolbarItems, function(e) {
24593 _this.navgroup.addItem(e);
24597 this.first = this.navgroup.addItem({
24598 tooltip: this.firstText,
24600 icon : 'fa fa-backward',
24602 preventDefault: true,
24603 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24606 this.prev = this.navgroup.addItem({
24607 tooltip: this.prevText,
24609 icon : 'fa fa-step-backward',
24611 preventDefault: true,
24612 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24614 //this.addSeparator();
24617 var field = this.navgroup.addItem( {
24619 cls : 'x-paging-position',
24621 html : this.beforePageText +
24622 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24623 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24626 this.field = field.el.select('input', true).first();
24627 this.field.on("keydown", this.onPagingKeydown, this);
24628 this.field.on("focus", function(){this.dom.select();});
24631 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24632 //this.field.setHeight(18);
24633 //this.addSeparator();
24634 this.next = this.navgroup.addItem({
24635 tooltip: this.nextText,
24637 html : ' <i class="fa fa-step-forward">',
24639 preventDefault: true,
24640 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24642 this.last = this.navgroup.addItem({
24643 tooltip: this.lastText,
24644 icon : 'fa fa-forward',
24647 preventDefault: true,
24648 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24650 //this.addSeparator();
24651 this.loading = this.navgroup.addItem({
24652 tooltip: this.refreshText,
24653 icon: 'fa fa-refresh',
24654 preventDefault: true,
24655 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24661 updateInfo : function(){
24662 if(this.displayEl){
24663 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24664 var msg = count == 0 ?
24668 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24670 this.displayEl.update(msg);
24675 onLoad : function(ds, r, o)
24677 this.cursor = o.params.start ? o.params.start : 0;
24679 var d = this.getPageData(),
24684 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24685 this.field.dom.value = ap;
24686 this.first.setDisabled(ap == 1);
24687 this.prev.setDisabled(ap == 1);
24688 this.next.setDisabled(ap == ps);
24689 this.last.setDisabled(ap == ps);
24690 this.loading.enable();
24695 getPageData : function(){
24696 var total = this.ds.getTotalCount();
24699 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24700 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24705 onLoadError : function(){
24706 this.loading.enable();
24710 onPagingKeydown : function(e){
24711 var k = e.getKey();
24712 var d = this.getPageData();
24714 var v = this.field.dom.value, pageNum;
24715 if(!v || isNaN(pageNum = parseInt(v, 10))){
24716 this.field.dom.value = d.activePage;
24719 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24720 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24723 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))
24725 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24726 this.field.dom.value = pageNum;
24727 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24730 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24732 var v = this.field.dom.value, pageNum;
24733 var increment = (e.shiftKey) ? 10 : 1;
24734 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24737 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24738 this.field.dom.value = d.activePage;
24741 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24743 this.field.dom.value = parseInt(v, 10) + increment;
24744 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24745 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24752 beforeLoad : function(){
24754 this.loading.disable();
24759 onClick : function(which){
24768 ds.load({params:{start: 0, limit: this.pageSize}});
24771 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24774 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24777 var total = ds.getTotalCount();
24778 var extra = total % this.pageSize;
24779 var lastStart = extra ? (total - extra) : total-this.pageSize;
24780 ds.load({params:{start: lastStart, limit: this.pageSize}});
24783 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24789 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24790 * @param {Roo.data.Store} store The data store to unbind
24792 unbind : function(ds){
24793 ds.un("beforeload", this.beforeLoad, this);
24794 ds.un("load", this.onLoad, this);
24795 ds.un("loadexception", this.onLoadError, this);
24796 ds.un("remove", this.updateInfo, this);
24797 ds.un("add", this.updateInfo, this);
24798 this.ds = undefined;
24802 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24803 * @param {Roo.data.Store} store The data store to bind
24805 bind : function(ds){
24806 ds.on("beforeload", this.beforeLoad, this);
24807 ds.on("load", this.onLoad, this);
24808 ds.on("loadexception", this.onLoadError, this);
24809 ds.on("remove", this.updateInfo, this);
24810 ds.on("add", this.updateInfo, this);
24821 * @class Roo.bootstrap.MessageBar
24822 * @extends Roo.bootstrap.Component
24823 * Bootstrap MessageBar class
24824 * @cfg {String} html contents of the MessageBar
24825 * @cfg {String} weight (info | success | warning | danger) default info
24826 * @cfg {String} beforeClass insert the bar before the given class
24827 * @cfg {Boolean} closable (true | false) default false
24828 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24831 * Create a new Element
24832 * @param {Object} config The config object
24835 Roo.bootstrap.MessageBar = function(config){
24836 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24839 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24845 beforeClass: 'bootstrap-sticky-wrap',
24847 getAutoCreate : function(){
24851 cls: 'alert alert-dismissable alert-' + this.weight,
24856 html: this.html || ''
24862 cfg.cls += ' alert-messages-fixed';
24876 onRender : function(ct, position)
24878 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24881 var cfg = Roo.apply({}, this.getAutoCreate());
24885 cfg.cls += ' ' + this.cls;
24888 cfg.style = this.style;
24890 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24892 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24895 this.el.select('>button.close').on('click', this.hide, this);
24901 if (!this.rendered) {
24907 this.fireEvent('show', this);
24913 if (!this.rendered) {
24919 this.fireEvent('hide', this);
24922 update : function()
24924 // var e = this.el.dom.firstChild;
24926 // if(this.closable){
24927 // e = e.nextSibling;
24930 // e.data = this.html || '';
24932 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24948 * @class Roo.bootstrap.Graph
24949 * @extends Roo.bootstrap.Component
24950 * Bootstrap Graph class
24954 @cfg {String} graphtype bar | vbar | pie
24955 @cfg {number} g_x coodinator | centre x (pie)
24956 @cfg {number} g_y coodinator | centre y (pie)
24957 @cfg {number} g_r radius (pie)
24958 @cfg {number} g_height height of the chart (respected by all elements in the set)
24959 @cfg {number} g_width width of the chart (respected by all elements in the set)
24960 @cfg {Object} title The title of the chart
24963 -opts (object) options for the chart
24965 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24966 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24968 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.
24969 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24971 o stretch (boolean)
24973 -opts (object) options for the pie
24976 o startAngle (number)
24977 o endAngle (number)
24981 * Create a new Input
24982 * @param {Object} config The config object
24985 Roo.bootstrap.Graph = function(config){
24986 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24992 * The img click event for the img.
24993 * @param {Roo.EventObject} e
24999 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
25010 //g_colors: this.colors,
25017 getAutoCreate : function(){
25028 onRender : function(ct,position){
25031 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25033 if (typeof(Raphael) == 'undefined') {
25034 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25038 this.raphael = Raphael(this.el.dom);
25040 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25041 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25042 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25043 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25045 r.text(160, 10, "Single Series Chart").attr(txtattr);
25046 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25047 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25048 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25050 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25051 r.barchart(330, 10, 300, 220, data1);
25052 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25053 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25056 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25057 // r.barchart(30, 30, 560, 250, xdata, {
25058 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25059 // axis : "0 0 1 1",
25060 // axisxlabels : xdata
25061 // //yvalues : cols,
25064 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25066 // this.load(null,xdata,{
25067 // axis : "0 0 1 1",
25068 // axisxlabels : xdata
25073 load : function(graphtype,xdata,opts)
25075 this.raphael.clear();
25077 graphtype = this.graphtype;
25082 var r = this.raphael,
25083 fin = function () {
25084 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25086 fout = function () {
25087 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25089 pfin = function() {
25090 this.sector.stop();
25091 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25094 this.label[0].stop();
25095 this.label[0].attr({ r: 7.5 });
25096 this.label[1].attr({ "font-weight": 800 });
25099 pfout = function() {
25100 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25103 this.label[0].animate({ r: 5 }, 500, "bounce");
25104 this.label[1].attr({ "font-weight": 400 });
25110 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25113 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25116 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25117 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25119 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25126 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25131 setTitle: function(o)
25136 initEvents: function() {
25139 this.el.on('click', this.onClick, this);
25143 onClick : function(e)
25145 Roo.log('img onclick');
25146 this.fireEvent('click', this, e);
25158 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25161 * @class Roo.bootstrap.dash.NumberBox
25162 * @extends Roo.bootstrap.Component
25163 * Bootstrap NumberBox class
25164 * @cfg {String} headline Box headline
25165 * @cfg {String} content Box content
25166 * @cfg {String} icon Box icon
25167 * @cfg {String} footer Footer text
25168 * @cfg {String} fhref Footer href
25171 * Create a new NumberBox
25172 * @param {Object} config The config object
25176 Roo.bootstrap.dash.NumberBox = function(config){
25177 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25181 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25190 getAutoCreate : function(){
25194 cls : 'small-box ',
25202 cls : 'roo-headline',
25203 html : this.headline
25207 cls : 'roo-content',
25208 html : this.content
25222 cls : 'ion ' + this.icon
25231 cls : 'small-box-footer',
25232 href : this.fhref || '#',
25236 cfg.cn.push(footer);
25243 onRender : function(ct,position){
25244 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25251 setHeadline: function (value)
25253 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25256 setFooter: function (value, href)
25258 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25261 this.el.select('a.small-box-footer',true).first().attr('href', href);
25266 setContent: function (value)
25268 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25271 initEvents: function()
25285 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25288 * @class Roo.bootstrap.dash.TabBox
25289 * @extends Roo.bootstrap.Component
25290 * Bootstrap TabBox class
25291 * @cfg {String} title Title of the TabBox
25292 * @cfg {String} icon Icon of the TabBox
25293 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25294 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25297 * Create a new TabBox
25298 * @param {Object} config The config object
25302 Roo.bootstrap.dash.TabBox = function(config){
25303 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25308 * When a pane is added
25309 * @param {Roo.bootstrap.dash.TabPane} pane
25313 * @event activatepane
25314 * When a pane is activated
25315 * @param {Roo.bootstrap.dash.TabPane} pane
25317 "activatepane" : true
25325 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25330 tabScrollable : false,
25332 getChildContainer : function()
25334 return this.el.select('.tab-content', true).first();
25337 getAutoCreate : function(){
25341 cls: 'pull-left header',
25349 cls: 'fa ' + this.icon
25355 cls: 'nav nav-tabs pull-right',
25361 if(this.tabScrollable){
25368 cls: 'nav nav-tabs pull-right',
25379 cls: 'nav-tabs-custom',
25384 cls: 'tab-content no-padding',
25392 initEvents : function()
25394 //Roo.log('add add pane handler');
25395 this.on('addpane', this.onAddPane, this);
25398 * Updates the box title
25399 * @param {String} html to set the title to.
25401 setTitle : function(value)
25403 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25405 onAddPane : function(pane)
25407 this.panes.push(pane);
25408 //Roo.log('addpane');
25410 // tabs are rendere left to right..
25411 if(!this.showtabs){
25415 var ctr = this.el.select('.nav-tabs', true).first();
25418 var existing = ctr.select('.nav-tab',true);
25419 var qty = existing.getCount();;
25422 var tab = ctr.createChild({
25424 cls : 'nav-tab' + (qty ? '' : ' active'),
25432 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25435 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25437 pane.el.addClass('active');
25442 onTabClick : function(ev,un,ob,pane)
25444 //Roo.log('tab - prev default');
25445 ev.preventDefault();
25448 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25449 pane.tab.addClass('active');
25450 //Roo.log(pane.title);
25451 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25452 // technically we should have a deactivate event.. but maybe add later.
25453 // and it should not de-activate the selected tab...
25454 this.fireEvent('activatepane', pane);
25455 pane.el.addClass('active');
25456 pane.fireEvent('activate');
25461 getActivePane : function()
25464 Roo.each(this.panes, function(p) {
25465 if(p.el.hasClass('active')){
25486 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25488 * @class Roo.bootstrap.TabPane
25489 * @extends Roo.bootstrap.Component
25490 * Bootstrap TabPane class
25491 * @cfg {Boolean} active (false | true) Default false
25492 * @cfg {String} title title of panel
25496 * Create a new TabPane
25497 * @param {Object} config The config object
25500 Roo.bootstrap.dash.TabPane = function(config){
25501 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25507 * When a pane is activated
25508 * @param {Roo.bootstrap.dash.TabPane} pane
25515 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25520 // the tabBox that this is attached to.
25523 getAutoCreate : function()
25531 cfg.cls += ' active';
25536 initEvents : function()
25538 //Roo.log('trigger add pane handler');
25539 this.parent().fireEvent('addpane', this)
25543 * Updates the tab title
25544 * @param {String} html to set the title to.
25546 setTitle: function(str)
25552 this.tab.select('a', true).first().dom.innerHTML = str;
25569 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25572 * @class Roo.bootstrap.menu.Menu
25573 * @extends Roo.bootstrap.Component
25574 * Bootstrap Menu class - container for Menu
25575 * @cfg {String} html Text of the menu
25576 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25577 * @cfg {String} icon Font awesome icon
25578 * @cfg {String} pos Menu align to (top | bottom) default bottom
25582 * Create a new Menu
25583 * @param {Object} config The config object
25587 Roo.bootstrap.menu.Menu = function(config){
25588 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25592 * @event beforeshow
25593 * Fires before this menu is displayed
25594 * @param {Roo.bootstrap.menu.Menu} this
25598 * @event beforehide
25599 * Fires before this menu is hidden
25600 * @param {Roo.bootstrap.menu.Menu} this
25605 * Fires after this menu is displayed
25606 * @param {Roo.bootstrap.menu.Menu} this
25611 * Fires after this menu is hidden
25612 * @param {Roo.bootstrap.menu.Menu} this
25617 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25618 * @param {Roo.bootstrap.menu.Menu} this
25619 * @param {Roo.EventObject} e
25626 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25630 weight : 'default',
25635 getChildContainer : function() {
25636 if(this.isSubMenu){
25640 return this.el.select('ul.dropdown-menu', true).first();
25643 getAutoCreate : function()
25648 cls : 'roo-menu-text',
25656 cls : 'fa ' + this.icon
25667 cls : 'dropdown-button btn btn-' + this.weight,
25672 cls : 'dropdown-toggle btn btn-' + this.weight,
25682 cls : 'dropdown-menu'
25688 if(this.pos == 'top'){
25689 cfg.cls += ' dropup';
25692 if(this.isSubMenu){
25695 cls : 'dropdown-menu'
25702 onRender : function(ct, position)
25704 this.isSubMenu = ct.hasClass('dropdown-submenu');
25706 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25709 initEvents : function()
25711 if(this.isSubMenu){
25715 this.hidden = true;
25717 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25718 this.triggerEl.on('click', this.onTriggerPress, this);
25720 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25721 this.buttonEl.on('click', this.onClick, this);
25727 if(this.isSubMenu){
25731 return this.el.select('ul.dropdown-menu', true).first();
25734 onClick : function(e)
25736 this.fireEvent("click", this, e);
25739 onTriggerPress : function(e)
25741 if (this.isVisible()) {
25748 isVisible : function(){
25749 return !this.hidden;
25754 this.fireEvent("beforeshow", this);
25756 this.hidden = false;
25757 this.el.addClass('open');
25759 Roo.get(document).on("mouseup", this.onMouseUp, this);
25761 this.fireEvent("show", this);
25768 this.fireEvent("beforehide", this);
25770 this.hidden = true;
25771 this.el.removeClass('open');
25773 Roo.get(document).un("mouseup", this.onMouseUp);
25775 this.fireEvent("hide", this);
25778 onMouseUp : function()
25792 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25795 * @class Roo.bootstrap.menu.Item
25796 * @extends Roo.bootstrap.Component
25797 * Bootstrap MenuItem class
25798 * @cfg {Boolean} submenu (true | false) default false
25799 * @cfg {String} html text of the item
25800 * @cfg {String} href the link
25801 * @cfg {Boolean} disable (true | false) default false
25802 * @cfg {Boolean} preventDefault (true | false) default true
25803 * @cfg {String} icon Font awesome icon
25804 * @cfg {String} pos Submenu align to (left | right) default right
25808 * Create a new Item
25809 * @param {Object} config The config object
25813 Roo.bootstrap.menu.Item = function(config){
25814 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25818 * Fires when the mouse is hovering over this menu
25819 * @param {Roo.bootstrap.menu.Item} this
25820 * @param {Roo.EventObject} e
25825 * Fires when the mouse exits this menu
25826 * @param {Roo.bootstrap.menu.Item} this
25827 * @param {Roo.EventObject} e
25833 * The raw click event for the entire grid.
25834 * @param {Roo.EventObject} e
25840 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25845 preventDefault: true,
25850 getAutoCreate : function()
25855 cls : 'roo-menu-item-text',
25863 cls : 'fa ' + this.icon
25872 href : this.href || '#',
25879 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25883 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25885 if(this.pos == 'left'){
25886 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25893 initEvents : function()
25895 this.el.on('mouseover', this.onMouseOver, this);
25896 this.el.on('mouseout', this.onMouseOut, this);
25898 this.el.select('a', true).first().on('click', this.onClick, this);
25902 onClick : function(e)
25904 if(this.preventDefault){
25905 e.preventDefault();
25908 this.fireEvent("click", this, e);
25911 onMouseOver : function(e)
25913 if(this.submenu && this.pos == 'left'){
25914 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25917 this.fireEvent("mouseover", this, e);
25920 onMouseOut : function(e)
25922 this.fireEvent("mouseout", this, e);
25934 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25937 * @class Roo.bootstrap.menu.Separator
25938 * @extends Roo.bootstrap.Component
25939 * Bootstrap Separator class
25942 * Create a new Separator
25943 * @param {Object} config The config object
25947 Roo.bootstrap.menu.Separator = function(config){
25948 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25951 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25953 getAutoCreate : function(){
25974 * @class Roo.bootstrap.Tooltip
25975 * Bootstrap Tooltip class
25976 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25977 * to determine which dom element triggers the tooltip.
25979 * It needs to add support for additional attributes like tooltip-position
25982 * Create a new Toolti
25983 * @param {Object} config The config object
25986 Roo.bootstrap.Tooltip = function(config){
25987 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25989 this.alignment = Roo.bootstrap.Tooltip.alignment;
25991 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25992 this.alignment = config.alignment;
25997 Roo.apply(Roo.bootstrap.Tooltip, {
25999 * @function init initialize tooltip monitoring.
26003 currentTip : false,
26004 currentRegion : false,
26010 Roo.get(document).on('mouseover', this.enter ,this);
26011 Roo.get(document).on('mouseout', this.leave, this);
26014 this.currentTip = new Roo.bootstrap.Tooltip();
26017 enter : function(ev)
26019 var dom = ev.getTarget();
26021 //Roo.log(['enter',dom]);
26022 var el = Roo.fly(dom);
26023 if (this.currentEl) {
26025 //Roo.log(this.currentEl);
26026 //Roo.log(this.currentEl.contains(dom));
26027 if (this.currentEl == el) {
26030 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26036 if (this.currentTip.el) {
26037 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26041 if(!el || el.dom == document){
26047 // you can not look for children, as if el is the body.. then everythign is the child..
26048 if (!el.attr('tooltip')) { //
26049 if (!el.select("[tooltip]").elements.length) {
26052 // is the mouse over this child...?
26053 bindEl = el.select("[tooltip]").first();
26054 var xy = ev.getXY();
26055 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26056 //Roo.log("not in region.");
26059 //Roo.log("child element over..");
26062 this.currentEl = bindEl;
26063 this.currentTip.bind(bindEl);
26064 this.currentRegion = Roo.lib.Region.getRegion(dom);
26065 this.currentTip.enter();
26068 leave : function(ev)
26070 var dom = ev.getTarget();
26071 //Roo.log(['leave',dom]);
26072 if (!this.currentEl) {
26077 if (dom != this.currentEl.dom) {
26080 var xy = ev.getXY();
26081 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26084 // only activate leave if mouse cursor is outside... bounding box..
26089 if (this.currentTip) {
26090 this.currentTip.leave();
26092 //Roo.log('clear currentEl');
26093 this.currentEl = false;
26098 'left' : ['r-l', [-2,0], 'right'],
26099 'right' : ['l-r', [2,0], 'left'],
26100 'bottom' : ['t-b', [0,2], 'top'],
26101 'top' : [ 'b-t', [0,-2], 'bottom']
26107 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26112 delay : null, // can be { show : 300 , hide: 500}
26116 hoverState : null, //???
26118 placement : 'bottom',
26122 getAutoCreate : function(){
26129 cls : 'tooltip-arrow'
26132 cls : 'tooltip-inner'
26139 bind : function(el)
26145 enter : function () {
26147 if (this.timeout != null) {
26148 clearTimeout(this.timeout);
26151 this.hoverState = 'in';
26152 //Roo.log("enter - show");
26153 if (!this.delay || !this.delay.show) {
26158 this.timeout = setTimeout(function () {
26159 if (_t.hoverState == 'in') {
26162 }, this.delay.show);
26166 clearTimeout(this.timeout);
26168 this.hoverState = 'out';
26169 if (!this.delay || !this.delay.hide) {
26175 this.timeout = setTimeout(function () {
26176 //Roo.log("leave - timeout");
26178 if (_t.hoverState == 'out') {
26180 Roo.bootstrap.Tooltip.currentEl = false;
26185 show : function (msg)
26188 this.render(document.body);
26191 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26193 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26195 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26197 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26199 var placement = typeof this.placement == 'function' ?
26200 this.placement.call(this, this.el, on_el) :
26203 var autoToken = /\s?auto?\s?/i;
26204 var autoPlace = autoToken.test(placement);
26206 placement = placement.replace(autoToken, '') || 'top';
26210 //this.el.setXY([0,0]);
26212 //this.el.dom.style.display='block';
26214 //this.el.appendTo(on_el);
26216 var p = this.getPosition();
26217 var box = this.el.getBox();
26223 var align = this.alignment[placement];
26225 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26227 if(placement == 'top' || placement == 'bottom'){
26229 placement = 'right';
26232 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26233 placement = 'left';
26236 var scroll = Roo.select('body', true).first().getScroll();
26238 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26242 align = this.alignment[placement];
26245 this.el.alignTo(this.bindEl, align[0],align[1]);
26246 //var arrow = this.el.select('.arrow',true).first();
26247 //arrow.set(align[2],
26249 this.el.addClass(placement);
26251 this.el.addClass('in fade');
26253 this.hoverState = null;
26255 if (this.el.hasClass('fade')) {
26266 //this.el.setXY([0,0]);
26267 this.el.removeClass('in');
26283 * @class Roo.bootstrap.LocationPicker
26284 * @extends Roo.bootstrap.Component
26285 * Bootstrap LocationPicker class
26286 * @cfg {Number} latitude Position when init default 0
26287 * @cfg {Number} longitude Position when init default 0
26288 * @cfg {Number} zoom default 15
26289 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26290 * @cfg {Boolean} mapTypeControl default false
26291 * @cfg {Boolean} disableDoubleClickZoom default false
26292 * @cfg {Boolean} scrollwheel default true
26293 * @cfg {Boolean} streetViewControl default false
26294 * @cfg {Number} radius default 0
26295 * @cfg {String} locationName
26296 * @cfg {Boolean} draggable default true
26297 * @cfg {Boolean} enableAutocomplete default false
26298 * @cfg {Boolean} enableReverseGeocode default true
26299 * @cfg {String} markerTitle
26302 * Create a new LocationPicker
26303 * @param {Object} config The config object
26307 Roo.bootstrap.LocationPicker = function(config){
26309 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26314 * Fires when the picker initialized.
26315 * @param {Roo.bootstrap.LocationPicker} this
26316 * @param {Google Location} location
26320 * @event positionchanged
26321 * Fires when the picker position changed.
26322 * @param {Roo.bootstrap.LocationPicker} this
26323 * @param {Google Location} location
26325 positionchanged : true,
26328 * Fires when the map resize.
26329 * @param {Roo.bootstrap.LocationPicker} this
26334 * Fires when the map show.
26335 * @param {Roo.bootstrap.LocationPicker} this
26340 * Fires when the map hide.
26341 * @param {Roo.bootstrap.LocationPicker} this
26346 * Fires when click the map.
26347 * @param {Roo.bootstrap.LocationPicker} this
26348 * @param {Map event} e
26352 * @event mapRightClick
26353 * Fires when right click the map.
26354 * @param {Roo.bootstrap.LocationPicker} this
26355 * @param {Map event} e
26357 mapRightClick : true,
26359 * @event markerClick
26360 * Fires when click the marker.
26361 * @param {Roo.bootstrap.LocationPicker} this
26362 * @param {Map event} e
26364 markerClick : true,
26366 * @event markerRightClick
26367 * Fires when right click the marker.
26368 * @param {Roo.bootstrap.LocationPicker} this
26369 * @param {Map event} e
26371 markerRightClick : true,
26373 * @event OverlayViewDraw
26374 * Fires when OverlayView Draw
26375 * @param {Roo.bootstrap.LocationPicker} this
26377 OverlayViewDraw : true,
26379 * @event OverlayViewOnAdd
26380 * Fires when OverlayView Draw
26381 * @param {Roo.bootstrap.LocationPicker} this
26383 OverlayViewOnAdd : true,
26385 * @event OverlayViewOnRemove
26386 * Fires when OverlayView Draw
26387 * @param {Roo.bootstrap.LocationPicker} this
26389 OverlayViewOnRemove : true,
26391 * @event OverlayViewShow
26392 * Fires when OverlayView Draw
26393 * @param {Roo.bootstrap.LocationPicker} this
26394 * @param {Pixel} cpx
26396 OverlayViewShow : true,
26398 * @event OverlayViewHide
26399 * Fires when OverlayView Draw
26400 * @param {Roo.bootstrap.LocationPicker} this
26402 OverlayViewHide : true,
26404 * @event loadexception
26405 * Fires when load google lib failed.
26406 * @param {Roo.bootstrap.LocationPicker} this
26408 loadexception : true
26413 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26415 gMapContext: false,
26421 mapTypeControl: false,
26422 disableDoubleClickZoom: false,
26424 streetViewControl: false,
26428 enableAutocomplete: false,
26429 enableReverseGeocode: true,
26432 getAutoCreate: function()
26437 cls: 'roo-location-picker'
26443 initEvents: function(ct, position)
26445 if(!this.el.getWidth() || this.isApplied()){
26449 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26454 initial: function()
26456 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26457 this.fireEvent('loadexception', this);
26461 if(!this.mapTypeId){
26462 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26465 this.gMapContext = this.GMapContext();
26467 this.initOverlayView();
26469 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26473 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26474 _this.setPosition(_this.gMapContext.marker.position);
26477 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26478 _this.fireEvent('mapClick', this, event);
26482 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26483 _this.fireEvent('mapRightClick', this, event);
26487 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26488 _this.fireEvent('markerClick', this, event);
26492 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26493 _this.fireEvent('markerRightClick', this, event);
26497 this.setPosition(this.gMapContext.location);
26499 this.fireEvent('initial', this, this.gMapContext.location);
26502 initOverlayView: function()
26506 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26510 _this.fireEvent('OverlayViewDraw', _this);
26515 _this.fireEvent('OverlayViewOnAdd', _this);
26518 onRemove: function()
26520 _this.fireEvent('OverlayViewOnRemove', _this);
26523 show: function(cpx)
26525 _this.fireEvent('OverlayViewShow', _this, cpx);
26530 _this.fireEvent('OverlayViewHide', _this);
26536 fromLatLngToContainerPixel: function(event)
26538 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26541 isApplied: function()
26543 return this.getGmapContext() == false ? false : true;
26546 getGmapContext: function()
26548 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26551 GMapContext: function()
26553 var position = new google.maps.LatLng(this.latitude, this.longitude);
26555 var _map = new google.maps.Map(this.el.dom, {
26558 mapTypeId: this.mapTypeId,
26559 mapTypeControl: this.mapTypeControl,
26560 disableDoubleClickZoom: this.disableDoubleClickZoom,
26561 scrollwheel: this.scrollwheel,
26562 streetViewControl: this.streetViewControl,
26563 locationName: this.locationName,
26564 draggable: this.draggable,
26565 enableAutocomplete: this.enableAutocomplete,
26566 enableReverseGeocode: this.enableReverseGeocode
26569 var _marker = new google.maps.Marker({
26570 position: position,
26572 title: this.markerTitle,
26573 draggable: this.draggable
26580 location: position,
26581 radius: this.radius,
26582 locationName: this.locationName,
26583 addressComponents: {
26584 formatted_address: null,
26585 addressLine1: null,
26586 addressLine2: null,
26588 streetNumber: null,
26592 stateOrProvince: null
26595 domContainer: this.el.dom,
26596 geodecoder: new google.maps.Geocoder()
26600 drawCircle: function(center, radius, options)
26602 if (this.gMapContext.circle != null) {
26603 this.gMapContext.circle.setMap(null);
26607 options = Roo.apply({}, options, {
26608 strokeColor: "#0000FF",
26609 strokeOpacity: .35,
26611 fillColor: "#0000FF",
26615 options.map = this.gMapContext.map;
26616 options.radius = radius;
26617 options.center = center;
26618 this.gMapContext.circle = new google.maps.Circle(options);
26619 return this.gMapContext.circle;
26625 setPosition: function(location)
26627 this.gMapContext.location = location;
26628 this.gMapContext.marker.setPosition(location);
26629 this.gMapContext.map.panTo(location);
26630 this.drawCircle(location, this.gMapContext.radius, {});
26634 if (this.gMapContext.settings.enableReverseGeocode) {
26635 this.gMapContext.geodecoder.geocode({
26636 latLng: this.gMapContext.location
26637 }, function(results, status) {
26639 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26640 _this.gMapContext.locationName = results[0].formatted_address;
26641 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26643 _this.fireEvent('positionchanged', this, location);
26650 this.fireEvent('positionchanged', this, location);
26655 google.maps.event.trigger(this.gMapContext.map, "resize");
26657 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26659 this.fireEvent('resize', this);
26662 setPositionByLatLng: function(latitude, longitude)
26664 this.setPosition(new google.maps.LatLng(latitude, longitude));
26667 getCurrentPosition: function()
26670 latitude: this.gMapContext.location.lat(),
26671 longitude: this.gMapContext.location.lng()
26675 getAddressName: function()
26677 return this.gMapContext.locationName;
26680 getAddressComponents: function()
26682 return this.gMapContext.addressComponents;
26685 address_component_from_google_geocode: function(address_components)
26689 for (var i = 0; i < address_components.length; i++) {
26690 var component = address_components[i];
26691 if (component.types.indexOf("postal_code") >= 0) {
26692 result.postalCode = component.short_name;
26693 } else if (component.types.indexOf("street_number") >= 0) {
26694 result.streetNumber = component.short_name;
26695 } else if (component.types.indexOf("route") >= 0) {
26696 result.streetName = component.short_name;
26697 } else if (component.types.indexOf("neighborhood") >= 0) {
26698 result.city = component.short_name;
26699 } else if (component.types.indexOf("locality") >= 0) {
26700 result.city = component.short_name;
26701 } else if (component.types.indexOf("sublocality") >= 0) {
26702 result.district = component.short_name;
26703 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26704 result.stateOrProvince = component.short_name;
26705 } else if (component.types.indexOf("country") >= 0) {
26706 result.country = component.short_name;
26710 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26711 result.addressLine2 = "";
26715 setZoomLevel: function(zoom)
26717 this.gMapContext.map.setZoom(zoom);
26730 this.fireEvent('show', this);
26741 this.fireEvent('hide', this);
26746 Roo.apply(Roo.bootstrap.LocationPicker, {
26748 OverlayView : function(map, options)
26750 options = options || {};
26764 * @class Roo.bootstrap.Alert
26765 * @extends Roo.bootstrap.Component
26766 * Bootstrap Alert class
26767 * @cfg {String} title The title of alert
26768 * @cfg {String} html The content of alert
26769 * @cfg {String} weight ( success | info | warning | danger )
26770 * @cfg {String} faicon font-awesomeicon
26773 * Create a new alert
26774 * @param {Object} config The config object
26778 Roo.bootstrap.Alert = function(config){
26779 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26783 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26790 getAutoCreate : function()
26799 cls : 'roo-alert-icon'
26804 cls : 'roo-alert-title',
26809 cls : 'roo-alert-text',
26816 cfg.cn[0].cls += ' fa ' + this.faicon;
26820 cfg.cls += ' alert-' + this.weight;
26826 initEvents: function()
26828 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26831 setTitle : function(str)
26833 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26836 setText : function(str)
26838 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26841 setWeight : function(weight)
26844 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26847 this.weight = weight;
26849 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26852 setIcon : function(icon)
26855 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26858 this.faicon = icon;
26860 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26881 * @class Roo.bootstrap.UploadCropbox
26882 * @extends Roo.bootstrap.Component
26883 * Bootstrap UploadCropbox class
26884 * @cfg {String} emptyText show when image has been loaded
26885 * @cfg {String} rotateNotify show when image too small to rotate
26886 * @cfg {Number} errorTimeout default 3000
26887 * @cfg {Number} minWidth default 300
26888 * @cfg {Number} minHeight default 300
26889 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26890 * @cfg {Boolean} isDocument (true|false) default false
26891 * @cfg {String} url action url
26892 * @cfg {String} paramName default 'imageUpload'
26893 * @cfg {String} method default POST
26894 * @cfg {Boolean} loadMask (true|false) default true
26895 * @cfg {Boolean} loadingText default 'Loading...'
26898 * Create a new UploadCropbox
26899 * @param {Object} config The config object
26902 Roo.bootstrap.UploadCropbox = function(config){
26903 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26907 * @event beforeselectfile
26908 * Fire before select file
26909 * @param {Roo.bootstrap.UploadCropbox} this
26911 "beforeselectfile" : true,
26914 * Fire after initEvent
26915 * @param {Roo.bootstrap.UploadCropbox} this
26920 * Fire after initEvent
26921 * @param {Roo.bootstrap.UploadCropbox} this
26922 * @param {String} data
26927 * Fire when preparing the file data
26928 * @param {Roo.bootstrap.UploadCropbox} this
26929 * @param {Object} file
26934 * Fire when get exception
26935 * @param {Roo.bootstrap.UploadCropbox} this
26936 * @param {XMLHttpRequest} xhr
26938 "exception" : true,
26940 * @event beforeloadcanvas
26941 * Fire before load the canvas
26942 * @param {Roo.bootstrap.UploadCropbox} this
26943 * @param {String} src
26945 "beforeloadcanvas" : true,
26948 * Fire when trash image
26949 * @param {Roo.bootstrap.UploadCropbox} this
26954 * Fire when download the image
26955 * @param {Roo.bootstrap.UploadCropbox} this
26959 * @event footerbuttonclick
26960 * Fire when footerbuttonclick
26961 * @param {Roo.bootstrap.UploadCropbox} this
26962 * @param {String} type
26964 "footerbuttonclick" : true,
26968 * @param {Roo.bootstrap.UploadCropbox} this
26973 * Fire when rotate the image
26974 * @param {Roo.bootstrap.UploadCropbox} this
26975 * @param {String} pos
26980 * Fire when inspect the file
26981 * @param {Roo.bootstrap.UploadCropbox} this
26982 * @param {Object} file
26987 * Fire when xhr upload the file
26988 * @param {Roo.bootstrap.UploadCropbox} this
26989 * @param {Object} data
26994 * Fire when arrange the file data
26995 * @param {Roo.bootstrap.UploadCropbox} this
26996 * @param {Object} formData
27001 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27004 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
27006 emptyText : 'Click to upload image',
27007 rotateNotify : 'Image is too small to rotate',
27008 errorTimeout : 3000,
27022 cropType : 'image/jpeg',
27024 canvasLoaded : false,
27025 isDocument : false,
27027 paramName : 'imageUpload',
27029 loadingText : 'Loading...',
27032 getAutoCreate : function()
27036 cls : 'roo-upload-cropbox',
27040 cls : 'roo-upload-cropbox-selector',
27045 cls : 'roo-upload-cropbox-body',
27046 style : 'cursor:pointer',
27050 cls : 'roo-upload-cropbox-preview'
27054 cls : 'roo-upload-cropbox-thumb'
27058 cls : 'roo-upload-cropbox-empty-notify',
27059 html : this.emptyText
27063 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27064 html : this.rotateNotify
27070 cls : 'roo-upload-cropbox-footer',
27073 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27083 onRender : function(ct, position)
27085 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27087 if (this.buttons.length) {
27089 Roo.each(this.buttons, function(bb) {
27091 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27093 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27099 this.maskEl = this.el;
27103 initEvents : function()
27105 this.urlAPI = (window.createObjectURL && window) ||
27106 (window.URL && URL.revokeObjectURL && URL) ||
27107 (window.webkitURL && webkitURL);
27109 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27110 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27112 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27113 this.selectorEl.hide();
27115 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27116 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27118 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27119 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27120 this.thumbEl.hide();
27122 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27123 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27125 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27126 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27127 this.errorEl.hide();
27129 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27130 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27131 this.footerEl.hide();
27133 this.setThumbBoxSize();
27139 this.fireEvent('initial', this);
27146 window.addEventListener("resize", function() { _this.resize(); } );
27148 this.bodyEl.on('click', this.beforeSelectFile, this);
27151 this.bodyEl.on('touchstart', this.onTouchStart, this);
27152 this.bodyEl.on('touchmove', this.onTouchMove, this);
27153 this.bodyEl.on('touchend', this.onTouchEnd, this);
27157 this.bodyEl.on('mousedown', this.onMouseDown, this);
27158 this.bodyEl.on('mousemove', this.onMouseMove, this);
27159 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27160 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27161 Roo.get(document).on('mouseup', this.onMouseUp, this);
27164 this.selectorEl.on('change', this.onFileSelected, this);
27170 this.baseScale = 1;
27172 this.baseRotate = 1;
27173 this.dragable = false;
27174 this.pinching = false;
27177 this.cropData = false;
27178 this.notifyEl.dom.innerHTML = this.emptyText;
27180 this.selectorEl.dom.value = '';
27184 resize : function()
27186 if(this.fireEvent('resize', this) != false){
27187 this.setThumbBoxPosition();
27188 this.setCanvasPosition();
27192 onFooterButtonClick : function(e, el, o, type)
27195 case 'rotate-left' :
27196 this.onRotateLeft(e);
27198 case 'rotate-right' :
27199 this.onRotateRight(e);
27202 this.beforeSelectFile(e);
27217 this.fireEvent('footerbuttonclick', this, type);
27220 beforeSelectFile : function(e)
27222 e.preventDefault();
27224 if(this.fireEvent('beforeselectfile', this) != false){
27225 this.selectorEl.dom.click();
27229 onFileSelected : function(e)
27231 e.preventDefault();
27233 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27237 var file = this.selectorEl.dom.files[0];
27239 if(this.fireEvent('inspect', this, file) != false){
27240 this.prepare(file);
27245 trash : function(e)
27247 this.fireEvent('trash', this);
27250 download : function(e)
27252 this.fireEvent('download', this);
27255 loadCanvas : function(src)
27257 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27261 this.imageEl = document.createElement('img');
27265 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27267 this.imageEl.src = src;
27271 onLoadCanvas : function()
27273 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27274 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27276 this.bodyEl.un('click', this.beforeSelectFile, this);
27278 this.notifyEl.hide();
27279 this.thumbEl.show();
27280 this.footerEl.show();
27282 this.baseRotateLevel();
27284 if(this.isDocument){
27285 this.setThumbBoxSize();
27288 this.setThumbBoxPosition();
27290 this.baseScaleLevel();
27296 this.canvasLoaded = true;
27299 this.maskEl.unmask();
27304 setCanvasPosition : function()
27306 if(!this.canvasEl){
27310 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27311 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27313 this.previewEl.setLeft(pw);
27314 this.previewEl.setTop(ph);
27318 onMouseDown : function(e)
27322 this.dragable = true;
27323 this.pinching = false;
27325 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27326 this.dragable = false;
27330 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27331 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27335 onMouseMove : function(e)
27339 if(!this.canvasLoaded){
27343 if (!this.dragable){
27347 var minX = Math.ceil(this.thumbEl.getLeft(true));
27348 var minY = Math.ceil(this.thumbEl.getTop(true));
27350 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27351 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27353 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27354 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27356 x = x - this.mouseX;
27357 y = y - this.mouseY;
27359 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27360 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27362 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27363 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27365 this.previewEl.setLeft(bgX);
27366 this.previewEl.setTop(bgY);
27368 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27369 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27372 onMouseUp : function(e)
27376 this.dragable = false;
27379 onMouseWheel : function(e)
27383 this.startScale = this.scale;
27385 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27387 if(!this.zoomable()){
27388 this.scale = this.startScale;
27397 zoomable : function()
27399 var minScale = this.thumbEl.getWidth() / this.minWidth;
27401 if(this.minWidth < this.minHeight){
27402 minScale = this.thumbEl.getHeight() / this.minHeight;
27405 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27406 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27410 (this.rotate == 0 || this.rotate == 180) &&
27412 width > this.imageEl.OriginWidth ||
27413 height > this.imageEl.OriginHeight ||
27414 (width < this.minWidth && height < this.minHeight)
27422 (this.rotate == 90 || this.rotate == 270) &&
27424 width > this.imageEl.OriginWidth ||
27425 height > this.imageEl.OriginHeight ||
27426 (width < this.minHeight && height < this.minWidth)
27433 !this.isDocument &&
27434 (this.rotate == 0 || this.rotate == 180) &&
27436 width < this.minWidth ||
27437 width > this.imageEl.OriginWidth ||
27438 height < this.minHeight ||
27439 height > this.imageEl.OriginHeight
27446 !this.isDocument &&
27447 (this.rotate == 90 || this.rotate == 270) &&
27449 width < this.minHeight ||
27450 width > this.imageEl.OriginWidth ||
27451 height < this.minWidth ||
27452 height > this.imageEl.OriginHeight
27462 onRotateLeft : function(e)
27464 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27466 var minScale = this.thumbEl.getWidth() / this.minWidth;
27468 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27469 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27471 this.startScale = this.scale;
27473 while (this.getScaleLevel() < minScale){
27475 this.scale = this.scale + 1;
27477 if(!this.zoomable()){
27482 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27483 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27488 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27495 this.scale = this.startScale;
27497 this.onRotateFail();
27502 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27504 if(this.isDocument){
27505 this.setThumbBoxSize();
27506 this.setThumbBoxPosition();
27507 this.setCanvasPosition();
27512 this.fireEvent('rotate', this, 'left');
27516 onRotateRight : function(e)
27518 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27520 var minScale = this.thumbEl.getWidth() / this.minWidth;
27522 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27523 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27525 this.startScale = this.scale;
27527 while (this.getScaleLevel() < minScale){
27529 this.scale = this.scale + 1;
27531 if(!this.zoomable()){
27536 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27537 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27542 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27549 this.scale = this.startScale;
27551 this.onRotateFail();
27556 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27558 if(this.isDocument){
27559 this.setThumbBoxSize();
27560 this.setThumbBoxPosition();
27561 this.setCanvasPosition();
27566 this.fireEvent('rotate', this, 'right');
27569 onRotateFail : function()
27571 this.errorEl.show(true);
27575 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27580 this.previewEl.dom.innerHTML = '';
27582 var canvasEl = document.createElement("canvas");
27584 var contextEl = canvasEl.getContext("2d");
27586 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27587 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27588 var center = this.imageEl.OriginWidth / 2;
27590 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27591 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27592 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27593 center = this.imageEl.OriginHeight / 2;
27596 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27598 contextEl.translate(center, center);
27599 contextEl.rotate(this.rotate * Math.PI / 180);
27601 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27603 this.canvasEl = document.createElement("canvas");
27605 this.contextEl = this.canvasEl.getContext("2d");
27607 switch (this.rotate) {
27610 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27611 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27613 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27618 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27619 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27621 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27622 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);
27626 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27631 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27632 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27634 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27635 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);
27639 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);
27644 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27645 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27647 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27648 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27652 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);
27659 this.previewEl.appendChild(this.canvasEl);
27661 this.setCanvasPosition();
27666 if(!this.canvasLoaded){
27670 var imageCanvas = document.createElement("canvas");
27672 var imageContext = imageCanvas.getContext("2d");
27674 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27675 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27677 var center = imageCanvas.width / 2;
27679 imageContext.translate(center, center);
27681 imageContext.rotate(this.rotate * Math.PI / 180);
27683 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27685 var canvas = document.createElement("canvas");
27687 var context = canvas.getContext("2d");
27689 canvas.width = this.minWidth;
27690 canvas.height = this.minHeight;
27692 switch (this.rotate) {
27695 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27696 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27698 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27699 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27701 var targetWidth = this.minWidth - 2 * x;
27702 var targetHeight = this.minHeight - 2 * y;
27706 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27707 scale = targetWidth / width;
27710 if(x > 0 && y == 0){
27711 scale = targetHeight / height;
27714 if(x > 0 && y > 0){
27715 scale = targetWidth / width;
27717 if(width < height){
27718 scale = targetHeight / height;
27722 context.scale(scale, scale);
27724 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27725 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27727 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27728 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27730 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27735 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27736 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27738 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27739 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27741 var targetWidth = this.minWidth - 2 * x;
27742 var targetHeight = this.minHeight - 2 * y;
27746 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27747 scale = targetWidth / width;
27750 if(x > 0 && y == 0){
27751 scale = targetHeight / height;
27754 if(x > 0 && y > 0){
27755 scale = targetWidth / width;
27757 if(width < height){
27758 scale = targetHeight / height;
27762 context.scale(scale, scale);
27764 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27765 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27767 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27768 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27770 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27772 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27777 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27778 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27780 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27781 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27783 var targetWidth = this.minWidth - 2 * x;
27784 var targetHeight = this.minHeight - 2 * y;
27788 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27789 scale = targetWidth / width;
27792 if(x > 0 && y == 0){
27793 scale = targetHeight / height;
27796 if(x > 0 && y > 0){
27797 scale = targetWidth / width;
27799 if(width < height){
27800 scale = targetHeight / height;
27804 context.scale(scale, scale);
27806 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27807 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27809 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27810 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27812 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27813 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27815 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27820 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27821 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27823 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27824 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27826 var targetWidth = this.minWidth - 2 * x;
27827 var targetHeight = this.minHeight - 2 * y;
27831 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27832 scale = targetWidth / width;
27835 if(x > 0 && y == 0){
27836 scale = targetHeight / height;
27839 if(x > 0 && y > 0){
27840 scale = targetWidth / width;
27842 if(width < height){
27843 scale = targetHeight / height;
27847 context.scale(scale, scale);
27849 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27850 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27852 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27853 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27855 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27857 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27864 this.cropData = canvas.toDataURL(this.cropType);
27866 if(this.fireEvent('crop', this, this.cropData) !== false){
27867 this.process(this.file, this.cropData);
27874 setThumbBoxSize : function()
27878 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27879 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27880 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27882 this.minWidth = width;
27883 this.minHeight = height;
27885 if(this.rotate == 90 || this.rotate == 270){
27886 this.minWidth = height;
27887 this.minHeight = width;
27892 width = Math.ceil(this.minWidth * height / this.minHeight);
27894 if(this.minWidth > this.minHeight){
27896 height = Math.ceil(this.minHeight * width / this.minWidth);
27899 this.thumbEl.setStyle({
27900 width : width + 'px',
27901 height : height + 'px'
27908 setThumbBoxPosition : function()
27910 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27911 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27913 this.thumbEl.setLeft(x);
27914 this.thumbEl.setTop(y);
27918 baseRotateLevel : function()
27920 this.baseRotate = 1;
27923 typeof(this.exif) != 'undefined' &&
27924 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27925 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27927 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27930 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27934 baseScaleLevel : function()
27938 if(this.isDocument){
27940 if(this.baseRotate == 6 || this.baseRotate == 8){
27942 height = this.thumbEl.getHeight();
27943 this.baseScale = height / this.imageEl.OriginWidth;
27945 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27946 width = this.thumbEl.getWidth();
27947 this.baseScale = width / this.imageEl.OriginHeight;
27953 height = this.thumbEl.getHeight();
27954 this.baseScale = height / this.imageEl.OriginHeight;
27956 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27957 width = this.thumbEl.getWidth();
27958 this.baseScale = width / this.imageEl.OriginWidth;
27964 if(this.baseRotate == 6 || this.baseRotate == 8){
27966 width = this.thumbEl.getHeight();
27967 this.baseScale = width / this.imageEl.OriginHeight;
27969 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27970 height = this.thumbEl.getWidth();
27971 this.baseScale = height / this.imageEl.OriginHeight;
27974 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27975 height = this.thumbEl.getWidth();
27976 this.baseScale = height / this.imageEl.OriginHeight;
27978 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27979 width = this.thumbEl.getHeight();
27980 this.baseScale = width / this.imageEl.OriginWidth;
27987 width = this.thumbEl.getWidth();
27988 this.baseScale = width / this.imageEl.OriginWidth;
27990 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27991 height = this.thumbEl.getHeight();
27992 this.baseScale = height / this.imageEl.OriginHeight;
27995 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27997 height = this.thumbEl.getHeight();
27998 this.baseScale = height / this.imageEl.OriginHeight;
28000 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28001 width = this.thumbEl.getWidth();
28002 this.baseScale = width / this.imageEl.OriginWidth;
28010 getScaleLevel : function()
28012 return this.baseScale * Math.pow(1.1, this.scale);
28015 onTouchStart : function(e)
28017 if(!this.canvasLoaded){
28018 this.beforeSelectFile(e);
28022 var touches = e.browserEvent.touches;
28028 if(touches.length == 1){
28029 this.onMouseDown(e);
28033 if(touches.length != 2){
28039 for(var i = 0, finger; finger = touches[i]; i++){
28040 coords.push(finger.pageX, finger.pageY);
28043 var x = Math.pow(coords[0] - coords[2], 2);
28044 var y = Math.pow(coords[1] - coords[3], 2);
28046 this.startDistance = Math.sqrt(x + y);
28048 this.startScale = this.scale;
28050 this.pinching = true;
28051 this.dragable = false;
28055 onTouchMove : function(e)
28057 if(!this.pinching && !this.dragable){
28061 var touches = e.browserEvent.touches;
28068 this.onMouseMove(e);
28074 for(var i = 0, finger; finger = touches[i]; i++){
28075 coords.push(finger.pageX, finger.pageY);
28078 var x = Math.pow(coords[0] - coords[2], 2);
28079 var y = Math.pow(coords[1] - coords[3], 2);
28081 this.endDistance = Math.sqrt(x + y);
28083 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28085 if(!this.zoomable()){
28086 this.scale = this.startScale;
28094 onTouchEnd : function(e)
28096 this.pinching = false;
28097 this.dragable = false;
28101 process : function(file, crop)
28104 this.maskEl.mask(this.loadingText);
28107 this.xhr = new XMLHttpRequest();
28109 file.xhr = this.xhr;
28111 this.xhr.open(this.method, this.url, true);
28114 "Accept": "application/json",
28115 "Cache-Control": "no-cache",
28116 "X-Requested-With": "XMLHttpRequest"
28119 for (var headerName in headers) {
28120 var headerValue = headers[headerName];
28122 this.xhr.setRequestHeader(headerName, headerValue);
28128 this.xhr.onload = function()
28130 _this.xhrOnLoad(_this.xhr);
28133 this.xhr.onerror = function()
28135 _this.xhrOnError(_this.xhr);
28138 var formData = new FormData();
28140 formData.append('returnHTML', 'NO');
28143 formData.append('crop', crop);
28146 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28147 formData.append(this.paramName, file, file.name);
28150 if(typeof(file.filename) != 'undefined'){
28151 formData.append('filename', file.filename);
28154 if(typeof(file.mimetype) != 'undefined'){
28155 formData.append('mimetype', file.mimetype);
28158 if(this.fireEvent('arrange', this, formData) != false){
28159 this.xhr.send(formData);
28163 xhrOnLoad : function(xhr)
28166 this.maskEl.unmask();
28169 if (xhr.readyState !== 4) {
28170 this.fireEvent('exception', this, xhr);
28174 var response = Roo.decode(xhr.responseText);
28176 if(!response.success){
28177 this.fireEvent('exception', this, xhr);
28181 var response = Roo.decode(xhr.responseText);
28183 this.fireEvent('upload', this, response);
28187 xhrOnError : function()
28190 this.maskEl.unmask();
28193 Roo.log('xhr on error');
28195 var response = Roo.decode(xhr.responseText);
28201 prepare : function(file)
28204 this.maskEl.mask(this.loadingText);
28210 if(typeof(file) === 'string'){
28211 this.loadCanvas(file);
28215 if(!file || !this.urlAPI){
28220 this.cropType = file.type;
28224 if(this.fireEvent('prepare', this, this.file) != false){
28226 var reader = new FileReader();
28228 reader.onload = function (e) {
28229 if (e.target.error) {
28230 Roo.log(e.target.error);
28234 var buffer = e.target.result,
28235 dataView = new DataView(buffer),
28237 maxOffset = dataView.byteLength - 4,
28241 if (dataView.getUint16(0) === 0xffd8) {
28242 while (offset < maxOffset) {
28243 markerBytes = dataView.getUint16(offset);
28245 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28246 markerLength = dataView.getUint16(offset + 2) + 2;
28247 if (offset + markerLength > dataView.byteLength) {
28248 Roo.log('Invalid meta data: Invalid segment size.');
28252 if(markerBytes == 0xffe1){
28253 _this.parseExifData(
28260 offset += markerLength;
28270 var url = _this.urlAPI.createObjectURL(_this.file);
28272 _this.loadCanvas(url);
28277 reader.readAsArrayBuffer(this.file);
28283 parseExifData : function(dataView, offset, length)
28285 var tiffOffset = offset + 10,
28289 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28290 // No Exif data, might be XMP data instead
28294 // Check for the ASCII code for "Exif" (0x45786966):
28295 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28296 // No Exif data, might be XMP data instead
28299 if (tiffOffset + 8 > dataView.byteLength) {
28300 Roo.log('Invalid Exif data: Invalid segment size.');
28303 // Check for the two null bytes:
28304 if (dataView.getUint16(offset + 8) !== 0x0000) {
28305 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28308 // Check the byte alignment:
28309 switch (dataView.getUint16(tiffOffset)) {
28311 littleEndian = true;
28314 littleEndian = false;
28317 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28320 // Check for the TIFF tag marker (0x002A):
28321 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28322 Roo.log('Invalid Exif data: Missing TIFF marker.');
28325 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28326 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28328 this.parseExifTags(
28331 tiffOffset + dirOffset,
28336 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28341 if (dirOffset + 6 > dataView.byteLength) {
28342 Roo.log('Invalid Exif data: Invalid directory offset.');
28345 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28346 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28347 if (dirEndOffset + 4 > dataView.byteLength) {
28348 Roo.log('Invalid Exif data: Invalid directory size.');
28351 for (i = 0; i < tagsNumber; i += 1) {
28355 dirOffset + 2 + 12 * i, // tag offset
28359 // Return the offset to the next directory:
28360 return dataView.getUint32(dirEndOffset, littleEndian);
28363 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28365 var tag = dataView.getUint16(offset, littleEndian);
28367 this.exif[tag] = this.getExifValue(
28371 dataView.getUint16(offset + 2, littleEndian), // tag type
28372 dataView.getUint32(offset + 4, littleEndian), // tag length
28377 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28379 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28388 Roo.log('Invalid Exif data: Invalid tag type.');
28392 tagSize = tagType.size * length;
28393 // Determine if the value is contained in the dataOffset bytes,
28394 // or if the value at the dataOffset is a pointer to the actual data:
28395 dataOffset = tagSize > 4 ?
28396 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28397 if (dataOffset + tagSize > dataView.byteLength) {
28398 Roo.log('Invalid Exif data: Invalid data offset.');
28401 if (length === 1) {
28402 return tagType.getValue(dataView, dataOffset, littleEndian);
28405 for (i = 0; i < length; i += 1) {
28406 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28409 if (tagType.ascii) {
28411 // Concatenate the chars:
28412 for (i = 0; i < values.length; i += 1) {
28414 // Ignore the terminating NULL byte(s):
28415 if (c === '\u0000') {
28427 Roo.apply(Roo.bootstrap.UploadCropbox, {
28429 'Orientation': 0x0112
28433 1: 0, //'top-left',
28435 3: 180, //'bottom-right',
28436 // 4: 'bottom-left',
28438 6: 90, //'right-top',
28439 // 7: 'right-bottom',
28440 8: 270 //'left-bottom'
28444 // byte, 8-bit unsigned int:
28446 getValue: function (dataView, dataOffset) {
28447 return dataView.getUint8(dataOffset);
28451 // ascii, 8-bit byte:
28453 getValue: function (dataView, dataOffset) {
28454 return String.fromCharCode(dataView.getUint8(dataOffset));
28459 // short, 16 bit int:
28461 getValue: function (dataView, dataOffset, littleEndian) {
28462 return dataView.getUint16(dataOffset, littleEndian);
28466 // long, 32 bit int:
28468 getValue: function (dataView, dataOffset, littleEndian) {
28469 return dataView.getUint32(dataOffset, littleEndian);
28473 // rational = two long values, first is numerator, second is denominator:
28475 getValue: function (dataView, dataOffset, littleEndian) {
28476 return dataView.getUint32(dataOffset, littleEndian) /
28477 dataView.getUint32(dataOffset + 4, littleEndian);
28481 // slong, 32 bit signed int:
28483 getValue: function (dataView, dataOffset, littleEndian) {
28484 return dataView.getInt32(dataOffset, littleEndian);
28488 // srational, two slongs, first is numerator, second is denominator:
28490 getValue: function (dataView, dataOffset, littleEndian) {
28491 return dataView.getInt32(dataOffset, littleEndian) /
28492 dataView.getInt32(dataOffset + 4, littleEndian);
28502 cls : 'btn-group roo-upload-cropbox-rotate-left',
28503 action : 'rotate-left',
28507 cls : 'btn btn-default',
28508 html : '<i class="fa fa-undo"></i>'
28514 cls : 'btn-group roo-upload-cropbox-picture',
28515 action : 'picture',
28519 cls : 'btn btn-default',
28520 html : '<i class="fa fa-picture-o"></i>'
28526 cls : 'btn-group roo-upload-cropbox-rotate-right',
28527 action : 'rotate-right',
28531 cls : 'btn btn-default',
28532 html : '<i class="fa fa-repeat"></i>'
28540 cls : 'btn-group roo-upload-cropbox-rotate-left',
28541 action : 'rotate-left',
28545 cls : 'btn btn-default',
28546 html : '<i class="fa fa-undo"></i>'
28552 cls : 'btn-group roo-upload-cropbox-download',
28553 action : 'download',
28557 cls : 'btn btn-default',
28558 html : '<i class="fa fa-download"></i>'
28564 cls : 'btn-group roo-upload-cropbox-crop',
28569 cls : 'btn btn-default',
28570 html : '<i class="fa fa-crop"></i>'
28576 cls : 'btn-group roo-upload-cropbox-trash',
28581 cls : 'btn btn-default',
28582 html : '<i class="fa fa-trash"></i>'
28588 cls : 'btn-group roo-upload-cropbox-rotate-right',
28589 action : 'rotate-right',
28593 cls : 'btn btn-default',
28594 html : '<i class="fa fa-repeat"></i>'
28602 cls : 'btn-group roo-upload-cropbox-rotate-left',
28603 action : 'rotate-left',
28607 cls : 'btn btn-default',
28608 html : '<i class="fa fa-undo"></i>'
28614 cls : 'btn-group roo-upload-cropbox-rotate-right',
28615 action : 'rotate-right',
28619 cls : 'btn btn-default',
28620 html : '<i class="fa fa-repeat"></i>'
28633 * @class Roo.bootstrap.DocumentManager
28634 * @extends Roo.bootstrap.Component
28635 * Bootstrap DocumentManager class
28636 * @cfg {String} paramName default 'imageUpload'
28637 * @cfg {String} toolTipName default 'filename'
28638 * @cfg {String} method default POST
28639 * @cfg {String} url action url
28640 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28641 * @cfg {Boolean} multiple multiple upload default true
28642 * @cfg {Number} thumbSize default 300
28643 * @cfg {String} fieldLabel
28644 * @cfg {Number} labelWidth default 4
28645 * @cfg {String} labelAlign (left|top) default left
28646 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28647 * @cfg {Number} labellg set the width of label (1-12)
28648 * @cfg {Number} labelmd set the width of label (1-12)
28649 * @cfg {Number} labelsm set the width of label (1-12)
28650 * @cfg {Number} labelxs set the width of label (1-12)
28653 * Create a new DocumentManager
28654 * @param {Object} config The config object
28657 Roo.bootstrap.DocumentManager = function(config){
28658 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28661 this.delegates = [];
28666 * Fire when initial the DocumentManager
28667 * @param {Roo.bootstrap.DocumentManager} this
28672 * inspect selected file
28673 * @param {Roo.bootstrap.DocumentManager} this
28674 * @param {File} file
28679 * Fire when xhr load exception
28680 * @param {Roo.bootstrap.DocumentManager} this
28681 * @param {XMLHttpRequest} xhr
28683 "exception" : true,
28685 * @event afterupload
28686 * Fire when xhr load exception
28687 * @param {Roo.bootstrap.DocumentManager} this
28688 * @param {XMLHttpRequest} xhr
28690 "afterupload" : true,
28693 * prepare the form data
28694 * @param {Roo.bootstrap.DocumentManager} this
28695 * @param {Object} formData
28700 * Fire when remove the file
28701 * @param {Roo.bootstrap.DocumentManager} this
28702 * @param {Object} file
28707 * Fire after refresh the file
28708 * @param {Roo.bootstrap.DocumentManager} this
28713 * Fire after click the image
28714 * @param {Roo.bootstrap.DocumentManager} this
28715 * @param {Object} file
28720 * Fire when upload a image and editable set to true
28721 * @param {Roo.bootstrap.DocumentManager} this
28722 * @param {Object} file
28726 * @event beforeselectfile
28727 * Fire before select file
28728 * @param {Roo.bootstrap.DocumentManager} this
28730 "beforeselectfile" : true,
28733 * Fire before process file
28734 * @param {Roo.bootstrap.DocumentManager} this
28735 * @param {Object} file
28739 * @event previewrendered
28740 * Fire when preview rendered
28741 * @param {Roo.bootstrap.DocumentManager} this
28742 * @param {Object} file
28744 "previewrendered" : true,
28747 "previewResize" : true
28752 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28761 paramName : 'imageUpload',
28762 toolTipName : 'filename',
28765 labelAlign : 'left',
28775 getAutoCreate : function()
28777 var managerWidget = {
28779 cls : 'roo-document-manager',
28783 cls : 'roo-document-manager-selector',
28788 cls : 'roo-document-manager-uploader',
28792 cls : 'roo-document-manager-upload-btn',
28793 html : '<i class="fa fa-plus"></i>'
28804 cls : 'column col-md-12',
28809 if(this.fieldLabel.length){
28814 cls : 'column col-md-12',
28815 html : this.fieldLabel
28819 cls : 'column col-md-12',
28824 if(this.labelAlign == 'left'){
28829 html : this.fieldLabel
28838 if(this.labelWidth > 12){
28839 content[0].style = "width: " + this.labelWidth + 'px';
28842 if(this.labelWidth < 13 && this.labelmd == 0){
28843 this.labelmd = this.labelWidth;
28846 if(this.labellg > 0){
28847 content[0].cls += ' col-lg-' + this.labellg;
28848 content[1].cls += ' col-lg-' + (12 - this.labellg);
28851 if(this.labelmd > 0){
28852 content[0].cls += ' col-md-' + this.labelmd;
28853 content[1].cls += ' col-md-' + (12 - this.labelmd);
28856 if(this.labelsm > 0){
28857 content[0].cls += ' col-sm-' + this.labelsm;
28858 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28861 if(this.labelxs > 0){
28862 content[0].cls += ' col-xs-' + this.labelxs;
28863 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28871 cls : 'row clearfix',
28879 initEvents : function()
28881 this.managerEl = this.el.select('.roo-document-manager', true).first();
28882 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28884 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28885 this.selectorEl.hide();
28888 this.selectorEl.attr('multiple', 'multiple');
28891 this.selectorEl.on('change', this.onFileSelected, this);
28893 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28894 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28896 this.uploader.on('click', this.onUploaderClick, this);
28898 this.renderProgressDialog();
28902 window.addEventListener("resize", function() { _this.refresh(); } );
28904 this.fireEvent('initial', this);
28907 renderProgressDialog : function()
28911 this.progressDialog = new Roo.bootstrap.Modal({
28912 cls : 'roo-document-manager-progress-dialog',
28913 allow_close : false,
28923 btnclick : function() {
28924 _this.uploadCancel();
28930 this.progressDialog.render(Roo.get(document.body));
28932 this.progress = new Roo.bootstrap.Progress({
28933 cls : 'roo-document-manager-progress',
28938 this.progress.render(this.progressDialog.getChildContainer());
28940 this.progressBar = new Roo.bootstrap.ProgressBar({
28941 cls : 'roo-document-manager-progress-bar',
28944 aria_valuemax : 12,
28948 this.progressBar.render(this.progress.getChildContainer());
28951 onUploaderClick : function(e)
28953 e.preventDefault();
28955 if(this.fireEvent('beforeselectfile', this) != false){
28956 this.selectorEl.dom.click();
28961 onFileSelected : function(e)
28963 e.preventDefault();
28965 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28969 Roo.each(this.selectorEl.dom.files, function(file){
28970 if(this.fireEvent('inspect', this, file) != false){
28971 this.files.push(file);
28981 this.selectorEl.dom.value = '';
28983 if(!this.files || !this.files.length){
28987 if(this.boxes > 0 && this.files.length > this.boxes){
28988 this.files = this.files.slice(0, this.boxes);
28991 this.uploader.show();
28993 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28994 this.uploader.hide();
29003 Roo.each(this.files, function(file){
29005 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29006 var f = this.renderPreview(file);
29011 if(file.type.indexOf('image') != -1){
29012 this.delegates.push(
29014 _this.process(file);
29015 }).createDelegate(this)
29023 _this.process(file);
29024 }).createDelegate(this)
29029 this.files = files;
29031 this.delegates = this.delegates.concat(docs);
29033 if(!this.delegates.length){
29038 this.progressBar.aria_valuemax = this.delegates.length;
29045 arrange : function()
29047 if(!this.delegates.length){
29048 this.progressDialog.hide();
29053 var delegate = this.delegates.shift();
29055 this.progressDialog.show();
29057 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29059 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29064 refresh : function()
29066 this.uploader.show();
29068 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29069 this.uploader.hide();
29072 Roo.isTouch ? this.closable(false) : this.closable(true);
29074 this.fireEvent('refresh', this);
29077 onRemove : function(e, el, o)
29079 e.preventDefault();
29081 this.fireEvent('remove', this, o);
29085 remove : function(o)
29089 Roo.each(this.files, function(file){
29090 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29099 this.files = files;
29106 Roo.each(this.files, function(file){
29111 file.target.remove();
29120 onClick : function(e, el, o)
29122 e.preventDefault();
29124 this.fireEvent('click', this, o);
29128 closable : function(closable)
29130 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29132 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29144 xhrOnLoad : function(xhr)
29146 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29150 if (xhr.readyState !== 4) {
29152 this.fireEvent('exception', this, xhr);
29156 var response = Roo.decode(xhr.responseText);
29158 if(!response.success){
29160 this.fireEvent('exception', this, xhr);
29164 var file = this.renderPreview(response.data);
29166 this.files.push(file);
29170 this.fireEvent('afterupload', this, xhr);
29174 xhrOnError : function(xhr)
29176 Roo.log('xhr on error');
29178 var response = Roo.decode(xhr.responseText);
29185 process : function(file)
29187 if(this.fireEvent('process', this, file) !== false){
29188 if(this.editable && file.type.indexOf('image') != -1){
29189 this.fireEvent('edit', this, file);
29193 this.uploadStart(file, false);
29200 uploadStart : function(file, crop)
29202 this.xhr = new XMLHttpRequest();
29204 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29209 file.xhr = this.xhr;
29211 this.managerEl.createChild({
29213 cls : 'roo-document-manager-loading',
29217 tooltip : file.name,
29218 cls : 'roo-document-manager-thumb',
29219 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29225 this.xhr.open(this.method, this.url, true);
29228 "Accept": "application/json",
29229 "Cache-Control": "no-cache",
29230 "X-Requested-With": "XMLHttpRequest"
29233 for (var headerName in headers) {
29234 var headerValue = headers[headerName];
29236 this.xhr.setRequestHeader(headerName, headerValue);
29242 this.xhr.onload = function()
29244 _this.xhrOnLoad(_this.xhr);
29247 this.xhr.onerror = function()
29249 _this.xhrOnError(_this.xhr);
29252 var formData = new FormData();
29254 formData.append('returnHTML', 'NO');
29257 formData.append('crop', crop);
29260 formData.append(this.paramName, file, file.name);
29267 if(this.fireEvent('prepare', this, formData, options) != false){
29269 if(options.manually){
29273 this.xhr.send(formData);
29277 this.uploadCancel();
29280 uploadCancel : function()
29286 this.delegates = [];
29288 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29295 renderPreview : function(file)
29297 if(typeof(file.target) != 'undefined' && file.target){
29301 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29303 var previewEl = this.managerEl.createChild({
29305 cls : 'roo-document-manager-preview',
29309 tooltip : file[this.toolTipName],
29310 cls : 'roo-document-manager-thumb',
29311 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29316 html : '<i class="fa fa-times-circle"></i>'
29321 var close = previewEl.select('button.close', true).first();
29323 close.on('click', this.onRemove, this, file);
29325 file.target = previewEl;
29327 var image = previewEl.select('img', true).first();
29331 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29333 image.on('click', this.onClick, this, file);
29335 this.fireEvent('previewrendered', this, file);
29341 onPreviewLoad : function(file, image)
29343 if(typeof(file.target) == 'undefined' || !file.target){
29347 var width = image.dom.naturalWidth || image.dom.width;
29348 var height = image.dom.naturalHeight || image.dom.height;
29350 if(!this.previewResize) {
29354 if(width > height){
29355 file.target.addClass('wide');
29359 file.target.addClass('tall');
29364 uploadFromSource : function(file, crop)
29366 this.xhr = new XMLHttpRequest();
29368 this.managerEl.createChild({
29370 cls : 'roo-document-manager-loading',
29374 tooltip : file.name,
29375 cls : 'roo-document-manager-thumb',
29376 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29382 this.xhr.open(this.method, this.url, true);
29385 "Accept": "application/json",
29386 "Cache-Control": "no-cache",
29387 "X-Requested-With": "XMLHttpRequest"
29390 for (var headerName in headers) {
29391 var headerValue = headers[headerName];
29393 this.xhr.setRequestHeader(headerName, headerValue);
29399 this.xhr.onload = function()
29401 _this.xhrOnLoad(_this.xhr);
29404 this.xhr.onerror = function()
29406 _this.xhrOnError(_this.xhr);
29409 var formData = new FormData();
29411 formData.append('returnHTML', 'NO');
29413 formData.append('crop', crop);
29415 if(typeof(file.filename) != 'undefined'){
29416 formData.append('filename', file.filename);
29419 if(typeof(file.mimetype) != 'undefined'){
29420 formData.append('mimetype', file.mimetype);
29425 if(this.fireEvent('prepare', this, formData) != false){
29426 this.xhr.send(formData);
29436 * @class Roo.bootstrap.DocumentViewer
29437 * @extends Roo.bootstrap.Component
29438 * Bootstrap DocumentViewer class
29439 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29440 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29443 * Create a new DocumentViewer
29444 * @param {Object} config The config object
29447 Roo.bootstrap.DocumentViewer = function(config){
29448 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29453 * Fire after initEvent
29454 * @param {Roo.bootstrap.DocumentViewer} this
29460 * @param {Roo.bootstrap.DocumentViewer} this
29465 * Fire after download button
29466 * @param {Roo.bootstrap.DocumentViewer} this
29471 * Fire after trash button
29472 * @param {Roo.bootstrap.DocumentViewer} this
29479 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29481 showDownload : true,
29485 getAutoCreate : function()
29489 cls : 'roo-document-viewer',
29493 cls : 'roo-document-viewer-body',
29497 cls : 'roo-document-viewer-thumb',
29501 cls : 'roo-document-viewer-image'
29509 cls : 'roo-document-viewer-footer',
29512 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29516 cls : 'btn-group roo-document-viewer-download',
29520 cls : 'btn btn-default',
29521 html : '<i class="fa fa-download"></i>'
29527 cls : 'btn-group roo-document-viewer-trash',
29531 cls : 'btn btn-default',
29532 html : '<i class="fa fa-trash"></i>'
29545 initEvents : function()
29547 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29548 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29550 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29551 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29553 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29554 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29556 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29557 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29559 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29560 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29562 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29563 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29565 this.bodyEl.on('click', this.onClick, this);
29566 this.downloadBtn.on('click', this.onDownload, this);
29567 this.trashBtn.on('click', this.onTrash, this);
29569 this.downloadBtn.hide();
29570 this.trashBtn.hide();
29572 if(this.showDownload){
29573 this.downloadBtn.show();
29576 if(this.showTrash){
29577 this.trashBtn.show();
29580 if(!this.showDownload && !this.showTrash) {
29581 this.footerEl.hide();
29586 initial : function()
29588 this.fireEvent('initial', this);
29592 onClick : function(e)
29594 e.preventDefault();
29596 this.fireEvent('click', this);
29599 onDownload : function(e)
29601 e.preventDefault();
29603 this.fireEvent('download', this);
29606 onTrash : function(e)
29608 e.preventDefault();
29610 this.fireEvent('trash', this);
29622 * @class Roo.bootstrap.NavProgressBar
29623 * @extends Roo.bootstrap.Component
29624 * Bootstrap NavProgressBar class
29627 * Create a new nav progress bar
29628 * @param {Object} config The config object
29631 Roo.bootstrap.NavProgressBar = function(config){
29632 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29634 this.bullets = this.bullets || [];
29636 // Roo.bootstrap.NavProgressBar.register(this);
29640 * Fires when the active item changes
29641 * @param {Roo.bootstrap.NavProgressBar} this
29642 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29643 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29650 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29655 getAutoCreate : function()
29657 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29661 cls : 'roo-navigation-bar-group',
29665 cls : 'roo-navigation-top-bar'
29669 cls : 'roo-navigation-bullets-bar',
29673 cls : 'roo-navigation-bar'
29680 cls : 'roo-navigation-bottom-bar'
29690 initEvents: function()
29695 onRender : function(ct, position)
29697 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29699 if(this.bullets.length){
29700 Roo.each(this.bullets, function(b){
29709 addItem : function(cfg)
29711 var item = new Roo.bootstrap.NavProgressItem(cfg);
29713 item.parentId = this.id;
29714 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29717 var top = new Roo.bootstrap.Element({
29719 cls : 'roo-navigation-bar-text'
29722 var bottom = new Roo.bootstrap.Element({
29724 cls : 'roo-navigation-bar-text'
29727 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29728 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29730 var topText = new Roo.bootstrap.Element({
29732 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29735 var bottomText = new Roo.bootstrap.Element({
29737 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29740 topText.onRender(top.el, null);
29741 bottomText.onRender(bottom.el, null);
29744 item.bottomEl = bottom;
29747 this.barItems.push(item);
29752 getActive : function()
29754 var active = false;
29756 Roo.each(this.barItems, function(v){
29758 if (!v.isActive()) {
29770 setActiveItem : function(item)
29774 Roo.each(this.barItems, function(v){
29775 if (v.rid == item.rid) {
29779 if (v.isActive()) {
29780 v.setActive(false);
29785 item.setActive(true);
29787 this.fireEvent('changed', this, item, prev);
29790 getBarItem: function(rid)
29794 Roo.each(this.barItems, function(e) {
29795 if (e.rid != rid) {
29806 indexOfItem : function(item)
29810 Roo.each(this.barItems, function(v, i){
29812 if (v.rid != item.rid) {
29823 setActiveNext : function()
29825 var i = this.indexOfItem(this.getActive());
29827 if (i > this.barItems.length) {
29831 this.setActiveItem(this.barItems[i+1]);
29834 setActivePrev : function()
29836 var i = this.indexOfItem(this.getActive());
29842 this.setActiveItem(this.barItems[i-1]);
29845 format : function()
29847 if(!this.barItems.length){
29851 var width = 100 / this.barItems.length;
29853 Roo.each(this.barItems, function(i){
29854 i.el.setStyle('width', width + '%');
29855 i.topEl.el.setStyle('width', width + '%');
29856 i.bottomEl.el.setStyle('width', width + '%');
29865 * Nav Progress Item
29870 * @class Roo.bootstrap.NavProgressItem
29871 * @extends Roo.bootstrap.Component
29872 * Bootstrap NavProgressItem class
29873 * @cfg {String} rid the reference id
29874 * @cfg {Boolean} active (true|false) Is item active default false
29875 * @cfg {Boolean} disabled (true|false) Is item active default false
29876 * @cfg {String} html
29877 * @cfg {String} position (top|bottom) text position default bottom
29878 * @cfg {String} icon show icon instead of number
29881 * Create a new NavProgressItem
29882 * @param {Object} config The config object
29884 Roo.bootstrap.NavProgressItem = function(config){
29885 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29890 * The raw click event for the entire grid.
29891 * @param {Roo.bootstrap.NavProgressItem} this
29892 * @param {Roo.EventObject} e
29899 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29905 position : 'bottom',
29908 getAutoCreate : function()
29910 var iconCls = 'roo-navigation-bar-item-icon';
29912 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29916 cls: 'roo-navigation-bar-item',
29926 cfg.cls += ' active';
29929 cfg.cls += ' disabled';
29935 disable : function()
29937 this.setDisabled(true);
29940 enable : function()
29942 this.setDisabled(false);
29945 initEvents: function()
29947 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29949 this.iconEl.on('click', this.onClick, this);
29952 onClick : function(e)
29954 e.preventDefault();
29960 if(this.fireEvent('click', this, e) === false){
29964 this.parent().setActiveItem(this);
29967 isActive: function ()
29969 return this.active;
29972 setActive : function(state)
29974 if(this.active == state){
29978 this.active = state;
29981 this.el.addClass('active');
29985 this.el.removeClass('active');
29990 setDisabled : function(state)
29992 if(this.disabled == state){
29996 this.disabled = state;
29999 this.el.addClass('disabled');
30003 this.el.removeClass('disabled');
30006 tooltipEl : function()
30008 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30021 * @class Roo.bootstrap.FieldLabel
30022 * @extends Roo.bootstrap.Component
30023 * Bootstrap FieldLabel class
30024 * @cfg {String} html contents of the element
30025 * @cfg {String} tag tag of the element default label
30026 * @cfg {String} cls class of the element
30027 * @cfg {String} target label target
30028 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30029 * @cfg {String} invalidClass default "text-warning"
30030 * @cfg {String} validClass default "text-success"
30031 * @cfg {String} iconTooltip default "This field is required"
30032 * @cfg {String} indicatorpos (left|right) default left
30035 * Create a new FieldLabel
30036 * @param {Object} config The config object
30039 Roo.bootstrap.FieldLabel = function(config){
30040 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30045 * Fires after the field has been marked as invalid.
30046 * @param {Roo.form.FieldLabel} this
30047 * @param {String} msg The validation message
30052 * Fires after the field has been validated with no errors.
30053 * @param {Roo.form.FieldLabel} this
30059 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30066 invalidClass : 'has-warning',
30067 validClass : 'has-success',
30068 iconTooltip : 'This field is required',
30069 indicatorpos : 'left',
30071 getAutoCreate : function(){
30074 if (!this.allowBlank) {
30080 cls : 'roo-bootstrap-field-label ' + this.cls,
30085 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30086 tooltip : this.iconTooltip
30095 if(this.indicatorpos == 'right'){
30098 cls : 'roo-bootstrap-field-label ' + this.cls,
30107 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30108 tooltip : this.iconTooltip
30117 initEvents: function()
30119 Roo.bootstrap.Element.superclass.initEvents.call(this);
30121 this.indicator = this.indicatorEl();
30123 if(this.indicator){
30124 this.indicator.removeClass('visible');
30125 this.indicator.addClass('invisible');
30128 Roo.bootstrap.FieldLabel.register(this);
30131 indicatorEl : function()
30133 var indicator = this.el.select('i.roo-required-indicator',true).first();
30144 * Mark this field as valid
30146 markValid : function()
30148 if(this.indicator){
30149 this.indicator.removeClass('visible');
30150 this.indicator.addClass('invisible');
30153 this.el.removeClass(this.invalidClass);
30155 this.el.addClass(this.validClass);
30157 this.fireEvent('valid', this);
30161 * Mark this field as invalid
30162 * @param {String} msg The validation message
30164 markInvalid : function(msg)
30166 if(this.indicator){
30167 this.indicator.removeClass('invisible');
30168 this.indicator.addClass('visible');
30171 this.el.removeClass(this.validClass);
30173 this.el.addClass(this.invalidClass);
30175 this.fireEvent('invalid', this, msg);
30181 Roo.apply(Roo.bootstrap.FieldLabel, {
30186 * register a FieldLabel Group
30187 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30189 register : function(label)
30191 if(this.groups.hasOwnProperty(label.target)){
30195 this.groups[label.target] = label;
30199 * fetch a FieldLabel Group based on the target
30200 * @param {string} target
30201 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30203 get: function(target) {
30204 if (typeof(this.groups[target]) == 'undefined') {
30208 return this.groups[target] ;
30217 * page DateSplitField.
30223 * @class Roo.bootstrap.DateSplitField
30224 * @extends Roo.bootstrap.Component
30225 * Bootstrap DateSplitField class
30226 * @cfg {string} fieldLabel - the label associated
30227 * @cfg {Number} labelWidth set the width of label (0-12)
30228 * @cfg {String} labelAlign (top|left)
30229 * @cfg {Boolean} dayAllowBlank (true|false) default false
30230 * @cfg {Boolean} monthAllowBlank (true|false) default false
30231 * @cfg {Boolean} yearAllowBlank (true|false) default false
30232 * @cfg {string} dayPlaceholder
30233 * @cfg {string} monthPlaceholder
30234 * @cfg {string} yearPlaceholder
30235 * @cfg {string} dayFormat default 'd'
30236 * @cfg {string} monthFormat default 'm'
30237 * @cfg {string} yearFormat default 'Y'
30238 * @cfg {Number} labellg set the width of label (1-12)
30239 * @cfg {Number} labelmd set the width of label (1-12)
30240 * @cfg {Number} labelsm set the width of label (1-12)
30241 * @cfg {Number} labelxs set the width of label (1-12)
30245 * Create a new DateSplitField
30246 * @param {Object} config The config object
30249 Roo.bootstrap.DateSplitField = function(config){
30250 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30256 * getting the data of years
30257 * @param {Roo.bootstrap.DateSplitField} this
30258 * @param {Object} years
30263 * getting the data of days
30264 * @param {Roo.bootstrap.DateSplitField} this
30265 * @param {Object} days
30270 * Fires after the field has been marked as invalid.
30271 * @param {Roo.form.Field} this
30272 * @param {String} msg The validation message
30277 * Fires after the field has been validated with no errors.
30278 * @param {Roo.form.Field} this
30284 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30287 labelAlign : 'top',
30289 dayAllowBlank : false,
30290 monthAllowBlank : false,
30291 yearAllowBlank : false,
30292 dayPlaceholder : '',
30293 monthPlaceholder : '',
30294 yearPlaceholder : '',
30298 isFormField : true,
30304 getAutoCreate : function()
30308 cls : 'row roo-date-split-field-group',
30313 cls : 'form-hidden-field roo-date-split-field-group-value',
30319 var labelCls = 'col-md-12';
30320 var contentCls = 'col-md-4';
30322 if(this.fieldLabel){
30326 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30330 html : this.fieldLabel
30335 if(this.labelAlign == 'left'){
30337 if(this.labelWidth > 12){
30338 label.style = "width: " + this.labelWidth + 'px';
30341 if(this.labelWidth < 13 && this.labelmd == 0){
30342 this.labelmd = this.labelWidth;
30345 if(this.labellg > 0){
30346 labelCls = ' col-lg-' + this.labellg;
30347 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30350 if(this.labelmd > 0){
30351 labelCls = ' col-md-' + this.labelmd;
30352 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30355 if(this.labelsm > 0){
30356 labelCls = ' col-sm-' + this.labelsm;
30357 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30360 if(this.labelxs > 0){
30361 labelCls = ' col-xs-' + this.labelxs;
30362 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30366 label.cls += ' ' + labelCls;
30368 cfg.cn.push(label);
30371 Roo.each(['day', 'month', 'year'], function(t){
30374 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30381 inputEl: function ()
30383 return this.el.select('.roo-date-split-field-group-value', true).first();
30386 onRender : function(ct, position)
30390 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30392 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30394 this.dayField = new Roo.bootstrap.ComboBox({
30395 allowBlank : this.dayAllowBlank,
30396 alwaysQuery : true,
30397 displayField : 'value',
30400 forceSelection : true,
30402 placeholder : this.dayPlaceholder,
30403 selectOnFocus : true,
30404 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30405 triggerAction : 'all',
30407 valueField : 'value',
30408 store : new Roo.data.SimpleStore({
30409 data : (function() {
30411 _this.fireEvent('days', _this, days);
30414 fields : [ 'value' ]
30417 select : function (_self, record, index)
30419 _this.setValue(_this.getValue());
30424 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30426 this.monthField = new Roo.bootstrap.MonthField({
30427 after : '<i class=\"fa fa-calendar\"></i>',
30428 allowBlank : this.monthAllowBlank,
30429 placeholder : this.monthPlaceholder,
30432 render : function (_self)
30434 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30435 e.preventDefault();
30439 select : function (_self, oldvalue, newvalue)
30441 _this.setValue(_this.getValue());
30446 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30448 this.yearField = new Roo.bootstrap.ComboBox({
30449 allowBlank : this.yearAllowBlank,
30450 alwaysQuery : true,
30451 displayField : 'value',
30454 forceSelection : true,
30456 placeholder : this.yearPlaceholder,
30457 selectOnFocus : true,
30458 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30459 triggerAction : 'all',
30461 valueField : 'value',
30462 store : new Roo.data.SimpleStore({
30463 data : (function() {
30465 _this.fireEvent('years', _this, years);
30468 fields : [ 'value' ]
30471 select : function (_self, record, index)
30473 _this.setValue(_this.getValue());
30478 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30481 setValue : function(v, format)
30483 this.inputEl.dom.value = v;
30485 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30487 var d = Date.parseDate(v, f);
30494 this.setDay(d.format(this.dayFormat));
30495 this.setMonth(d.format(this.monthFormat));
30496 this.setYear(d.format(this.yearFormat));
30503 setDay : function(v)
30505 this.dayField.setValue(v);
30506 this.inputEl.dom.value = this.getValue();
30511 setMonth : function(v)
30513 this.monthField.setValue(v, true);
30514 this.inputEl.dom.value = this.getValue();
30519 setYear : function(v)
30521 this.yearField.setValue(v);
30522 this.inputEl.dom.value = this.getValue();
30527 getDay : function()
30529 return this.dayField.getValue();
30532 getMonth : function()
30534 return this.monthField.getValue();
30537 getYear : function()
30539 return this.yearField.getValue();
30542 getValue : function()
30544 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30546 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30556 this.inputEl.dom.value = '';
30561 validate : function()
30563 var d = this.dayField.validate();
30564 var m = this.monthField.validate();
30565 var y = this.yearField.validate();
30570 (!this.dayAllowBlank && !d) ||
30571 (!this.monthAllowBlank && !m) ||
30572 (!this.yearAllowBlank && !y)
30577 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30586 this.markInvalid();
30591 markValid : function()
30594 var label = this.el.select('label', true).first();
30595 var icon = this.el.select('i.fa-star', true).first();
30601 this.fireEvent('valid', this);
30605 * Mark this field as invalid
30606 * @param {String} msg The validation message
30608 markInvalid : function(msg)
30611 var label = this.el.select('label', true).first();
30612 var icon = this.el.select('i.fa-star', true).first();
30614 if(label && !icon){
30615 this.el.select('.roo-date-split-field-label', true).createChild({
30617 cls : 'text-danger fa fa-lg fa-star',
30618 tooltip : 'This field is required',
30619 style : 'margin-right:5px;'
30623 this.fireEvent('invalid', this, msg);
30626 clearInvalid : function()
30628 var label = this.el.select('label', true).first();
30629 var icon = this.el.select('i.fa-star', true).first();
30635 this.fireEvent('valid', this);
30638 getName: function()
30648 * http://masonry.desandro.com
30650 * The idea is to render all the bricks based on vertical width...
30652 * The original code extends 'outlayer' - we might need to use that....
30658 * @class Roo.bootstrap.LayoutMasonry
30659 * @extends Roo.bootstrap.Component
30660 * Bootstrap Layout Masonry class
30663 * Create a new Element
30664 * @param {Object} config The config object
30667 Roo.bootstrap.LayoutMasonry = function(config){
30669 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30673 Roo.bootstrap.LayoutMasonry.register(this);
30679 * Fire after layout the items
30680 * @param {Roo.bootstrap.LayoutMasonry} this
30681 * @param {Roo.EventObject} e
30688 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30691 * @cfg {Boolean} isLayoutInstant = no animation?
30693 isLayoutInstant : false, // needed?
30696 * @cfg {Number} boxWidth width of the columns
30701 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30706 * @cfg {Number} padWidth padding below box..
30711 * @cfg {Number} gutter gutter width..
30716 * @cfg {Number} maxCols maximum number of columns
30722 * @cfg {Boolean} isAutoInitial defalut true
30724 isAutoInitial : true,
30729 * @cfg {Boolean} isHorizontal defalut false
30731 isHorizontal : false,
30733 currentSize : null,
30739 bricks: null, //CompositeElement
30743 _isLayoutInited : false,
30745 // isAlternative : false, // only use for vertical layout...
30748 * @cfg {Number} alternativePadWidth padding below box..
30750 alternativePadWidth : 50,
30752 selectedBrick : [],
30754 getAutoCreate : function(){
30756 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30760 cls: 'blog-masonary-wrapper ' + this.cls,
30762 cls : 'mas-boxes masonary'
30769 getChildContainer: function( )
30771 if (this.boxesEl) {
30772 return this.boxesEl;
30775 this.boxesEl = this.el.select('.mas-boxes').first();
30777 return this.boxesEl;
30781 initEvents : function()
30785 if(this.isAutoInitial){
30786 Roo.log('hook children rendered');
30787 this.on('childrenrendered', function() {
30788 Roo.log('children rendered');
30794 initial : function()
30796 this.selectedBrick = [];
30798 this.currentSize = this.el.getBox(true);
30800 Roo.EventManager.onWindowResize(this.resize, this);
30802 if(!this.isAutoInitial){
30810 //this.layout.defer(500,this);
30814 resize : function()
30816 var cs = this.el.getBox(true);
30819 this.currentSize.width == cs.width &&
30820 this.currentSize.x == cs.x &&
30821 this.currentSize.height == cs.height &&
30822 this.currentSize.y == cs.y
30824 Roo.log("no change in with or X or Y");
30828 this.currentSize = cs;
30834 layout : function()
30836 this._resetLayout();
30838 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30840 this.layoutItems( isInstant );
30842 this._isLayoutInited = true;
30844 this.fireEvent('layout', this);
30848 _resetLayout : function()
30850 if(this.isHorizontal){
30851 this.horizontalMeasureColumns();
30855 this.verticalMeasureColumns();
30859 verticalMeasureColumns : function()
30861 this.getContainerWidth();
30863 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30864 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30868 var boxWidth = this.boxWidth + this.padWidth;
30870 if(this.containerWidth < this.boxWidth){
30871 boxWidth = this.containerWidth
30874 var containerWidth = this.containerWidth;
30876 var cols = Math.floor(containerWidth / boxWidth);
30878 this.cols = Math.max( cols, 1 );
30880 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30882 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30884 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30886 this.colWidth = boxWidth + avail - this.padWidth;
30888 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30889 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30892 horizontalMeasureColumns : function()
30894 this.getContainerWidth();
30896 var boxWidth = this.boxWidth;
30898 if(this.containerWidth < boxWidth){
30899 boxWidth = this.containerWidth;
30902 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30904 this.el.setHeight(boxWidth);
30908 getContainerWidth : function()
30910 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30913 layoutItems : function( isInstant )
30915 Roo.log(this.bricks);
30917 var items = Roo.apply([], this.bricks);
30919 if(this.isHorizontal){
30920 this._horizontalLayoutItems( items , isInstant );
30924 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30925 // this._verticalAlternativeLayoutItems( items , isInstant );
30929 this._verticalLayoutItems( items , isInstant );
30933 _verticalLayoutItems : function ( items , isInstant)
30935 if ( !items || !items.length ) {
30940 ['xs', 'xs', 'xs', 'tall'],
30941 ['xs', 'xs', 'tall'],
30942 ['xs', 'xs', 'sm'],
30943 ['xs', 'xs', 'xs'],
30949 ['sm', 'xs', 'xs'],
30953 ['tall', 'xs', 'xs', 'xs'],
30954 ['tall', 'xs', 'xs'],
30966 Roo.each(items, function(item, k){
30968 switch (item.size) {
30969 // these layouts take up a full box,
30980 boxes.push([item]);
31003 var filterPattern = function(box, length)
31011 var pattern = box.slice(0, length);
31015 Roo.each(pattern, function(i){
31016 format.push(i.size);
31019 Roo.each(standard, function(s){
31021 if(String(s) != String(format)){
31030 if(!match && length == 1){
31035 filterPattern(box, length - 1);
31039 queue.push(pattern);
31041 box = box.slice(length, box.length);
31043 filterPattern(box, 4);
31049 Roo.each(boxes, function(box, k){
31055 if(box.length == 1){
31060 filterPattern(box, 4);
31064 this._processVerticalLayoutQueue( queue, isInstant );
31068 // _verticalAlternativeLayoutItems : function( items , isInstant )
31070 // if ( !items || !items.length ) {
31074 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31078 _horizontalLayoutItems : function ( items , isInstant)
31080 if ( !items || !items.length || items.length < 3) {
31086 var eItems = items.slice(0, 3);
31088 items = items.slice(3, items.length);
31091 ['xs', 'xs', 'xs', 'wide'],
31092 ['xs', 'xs', 'wide'],
31093 ['xs', 'xs', 'sm'],
31094 ['xs', 'xs', 'xs'],
31100 ['sm', 'xs', 'xs'],
31104 ['wide', 'xs', 'xs', 'xs'],
31105 ['wide', 'xs', 'xs'],
31118 Roo.each(items, function(item, k){
31120 switch (item.size) {
31131 boxes.push([item]);
31155 var filterPattern = function(box, length)
31163 var pattern = box.slice(0, length);
31167 Roo.each(pattern, function(i){
31168 format.push(i.size);
31171 Roo.each(standard, function(s){
31173 if(String(s) != String(format)){
31182 if(!match && length == 1){
31187 filterPattern(box, length - 1);
31191 queue.push(pattern);
31193 box = box.slice(length, box.length);
31195 filterPattern(box, 4);
31201 Roo.each(boxes, function(box, k){
31207 if(box.length == 1){
31212 filterPattern(box, 4);
31219 var pos = this.el.getBox(true);
31223 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31225 var hit_end = false;
31227 Roo.each(queue, function(box){
31231 Roo.each(box, function(b){
31233 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31243 Roo.each(box, function(b){
31245 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31248 mx = Math.max(mx, b.x);
31252 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31256 Roo.each(box, function(b){
31258 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31272 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31275 /** Sets position of item in DOM
31276 * @param {Element} item
31277 * @param {Number} x - horizontal position
31278 * @param {Number} y - vertical position
31279 * @param {Boolean} isInstant - disables transitions
31281 _processVerticalLayoutQueue : function( queue, isInstant )
31283 var pos = this.el.getBox(true);
31288 for (var i = 0; i < this.cols; i++){
31292 Roo.each(queue, function(box, k){
31294 var col = k % this.cols;
31296 Roo.each(box, function(b,kk){
31298 b.el.position('absolute');
31300 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31301 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31303 if(b.size == 'md-left' || b.size == 'md-right'){
31304 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31305 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31308 b.el.setWidth(width);
31309 b.el.setHeight(height);
31311 b.el.select('iframe',true).setSize(width,height);
31315 for (var i = 0; i < this.cols; i++){
31317 if(maxY[i] < maxY[col]){
31322 col = Math.min(col, i);
31326 x = pos.x + col * (this.colWidth + this.padWidth);
31330 var positions = [];
31332 switch (box.length){
31334 positions = this.getVerticalOneBoxColPositions(x, y, box);
31337 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31340 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31343 positions = this.getVerticalFourBoxColPositions(x, y, box);
31349 Roo.each(box, function(b,kk){
31351 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31353 var sz = b.el.getSize();
31355 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31363 for (var i = 0; i < this.cols; i++){
31364 mY = Math.max(mY, maxY[i]);
31367 this.el.setHeight(mY - pos.y);
31371 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31373 // var pos = this.el.getBox(true);
31376 // var maxX = pos.right;
31378 // var maxHeight = 0;
31380 // Roo.each(items, function(item, k){
31384 // item.el.position('absolute');
31386 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31388 // item.el.setWidth(width);
31390 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31392 // item.el.setHeight(height);
31395 // item.el.setXY([x, y], isInstant ? false : true);
31397 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31400 // y = y + height + this.alternativePadWidth;
31402 // maxHeight = maxHeight + height + this.alternativePadWidth;
31406 // this.el.setHeight(maxHeight);
31410 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31412 var pos = this.el.getBox(true);
31417 var maxX = pos.right;
31419 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31421 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31423 Roo.each(queue, function(box, k){
31425 Roo.each(box, function(b, kk){
31427 b.el.position('absolute');
31429 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31430 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31432 if(b.size == 'md-left' || b.size == 'md-right'){
31433 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31434 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31437 b.el.setWidth(width);
31438 b.el.setHeight(height);
31446 var positions = [];
31448 switch (box.length){
31450 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31453 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31456 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31459 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31465 Roo.each(box, function(b,kk){
31467 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31469 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31477 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31479 Roo.each(eItems, function(b,k){
31481 b.size = (k == 0) ? 'sm' : 'xs';
31482 b.x = (k == 0) ? 2 : 1;
31483 b.y = (k == 0) ? 2 : 1;
31485 b.el.position('absolute');
31487 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31489 b.el.setWidth(width);
31491 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31493 b.el.setHeight(height);
31497 var positions = [];
31500 x : maxX - this.unitWidth * 2 - this.gutter,
31505 x : maxX - this.unitWidth,
31506 y : minY + (this.unitWidth + this.gutter) * 2
31510 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31514 Roo.each(eItems, function(b,k){
31516 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31522 getVerticalOneBoxColPositions : function(x, y, box)
31526 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31528 if(box[0].size == 'md-left'){
31532 if(box[0].size == 'md-right'){
31537 x : x + (this.unitWidth + this.gutter) * rand,
31544 getVerticalTwoBoxColPositions : function(x, y, box)
31548 if(box[0].size == 'xs'){
31552 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31556 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31570 x : x + (this.unitWidth + this.gutter) * 2,
31571 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31578 getVerticalThreeBoxColPositions : function(x, y, box)
31582 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31590 x : x + (this.unitWidth + this.gutter) * 1,
31595 x : x + (this.unitWidth + this.gutter) * 2,
31603 if(box[0].size == 'xs' && box[1].size == 'xs'){
31612 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31616 x : x + (this.unitWidth + this.gutter) * 1,
31630 x : x + (this.unitWidth + this.gutter) * 2,
31635 x : x + (this.unitWidth + this.gutter) * 2,
31636 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31643 getVerticalFourBoxColPositions : function(x, y, box)
31647 if(box[0].size == 'xs'){
31656 y : y + (this.unitHeight + this.gutter) * 1
31661 y : y + (this.unitHeight + this.gutter) * 2
31665 x : x + (this.unitWidth + this.gutter) * 1,
31679 x : x + (this.unitWidth + this.gutter) * 2,
31684 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31685 y : y + (this.unitHeight + this.gutter) * 1
31689 x : x + (this.unitWidth + this.gutter) * 2,
31690 y : y + (this.unitWidth + this.gutter) * 2
31697 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31701 if(box[0].size == 'md-left'){
31703 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31710 if(box[0].size == 'md-right'){
31712 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31713 y : minY + (this.unitWidth + this.gutter) * 1
31719 var rand = Math.floor(Math.random() * (4 - box[0].y));
31722 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31723 y : minY + (this.unitWidth + this.gutter) * rand
31730 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31734 if(box[0].size == 'xs'){
31737 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31742 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31743 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31751 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31756 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31757 y : minY + (this.unitWidth + this.gutter) * 2
31764 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31768 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31771 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31776 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31777 y : minY + (this.unitWidth + this.gutter) * 1
31781 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31782 y : minY + (this.unitWidth + this.gutter) * 2
31789 if(box[0].size == 'xs' && box[1].size == 'xs'){
31792 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31797 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31802 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31803 y : minY + (this.unitWidth + this.gutter) * 1
31811 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31816 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31817 y : minY + (this.unitWidth + this.gutter) * 2
31821 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31822 y : minY + (this.unitWidth + this.gutter) * 2
31829 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31833 if(box[0].size == 'xs'){
31836 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31841 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31846 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),
31851 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31852 y : minY + (this.unitWidth + this.gutter) * 1
31860 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31865 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31866 y : minY + (this.unitWidth + this.gutter) * 2
31870 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31871 y : minY + (this.unitWidth + this.gutter) * 2
31875 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),
31876 y : minY + (this.unitWidth + this.gutter) * 2
31884 * remove a Masonry Brick
31885 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31887 removeBrick : function(brick_id)
31893 for (var i = 0; i<this.bricks.length; i++) {
31894 if (this.bricks[i].id == brick_id) {
31895 this.bricks.splice(i,1);
31896 this.el.dom.removeChild(Roo.get(brick_id).dom);
31903 * adds a Masonry Brick
31904 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31906 addBrick : function(cfg)
31908 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31909 //this.register(cn);
31910 cn.parentId = this.id;
31911 cn.onRender(this.el, null);
31916 * register a Masonry Brick
31917 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31920 register : function(brick)
31922 this.bricks.push(brick);
31923 brick.masonryId = this.id;
31927 * clear all the Masonry Brick
31929 clearAll : function()
31932 //this.getChildContainer().dom.innerHTML = "";
31933 this.el.dom.innerHTML = '';
31936 getSelected : function()
31938 if (!this.selectedBrick) {
31942 return this.selectedBrick;
31946 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31950 * register a Masonry Layout
31951 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31954 register : function(layout)
31956 this.groups[layout.id] = layout;
31959 * fetch a Masonry Layout based on the masonry layout ID
31960 * @param {string} the masonry layout to add
31961 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31964 get: function(layout_id) {
31965 if (typeof(this.groups[layout_id]) == 'undefined') {
31968 return this.groups[layout_id] ;
31980 * http://masonry.desandro.com
31982 * The idea is to render all the bricks based on vertical width...
31984 * The original code extends 'outlayer' - we might need to use that....
31990 * @class Roo.bootstrap.LayoutMasonryAuto
31991 * @extends Roo.bootstrap.Component
31992 * Bootstrap Layout Masonry class
31995 * Create a new Element
31996 * @param {Object} config The config object
31999 Roo.bootstrap.LayoutMasonryAuto = function(config){
32000 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32003 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
32006 * @cfg {Boolean} isFitWidth - resize the width..
32008 isFitWidth : false, // options..
32010 * @cfg {Boolean} isOriginLeft = left align?
32012 isOriginLeft : true,
32014 * @cfg {Boolean} isOriginTop = top align?
32016 isOriginTop : false,
32018 * @cfg {Boolean} isLayoutInstant = no animation?
32020 isLayoutInstant : false, // needed?
32022 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32024 isResizingContainer : true,
32026 * @cfg {Number} columnWidth width of the columns
32032 * @cfg {Number} maxCols maximum number of columns
32037 * @cfg {Number} padHeight padding below box..
32043 * @cfg {Boolean} isAutoInitial defalut true
32046 isAutoInitial : true,
32052 initialColumnWidth : 0,
32053 currentSize : null,
32055 colYs : null, // array.
32062 bricks: null, //CompositeElement
32063 cols : 0, // array?
32064 // element : null, // wrapped now this.el
32065 _isLayoutInited : null,
32068 getAutoCreate : function(){
32072 cls: 'blog-masonary-wrapper ' + this.cls,
32074 cls : 'mas-boxes masonary'
32081 getChildContainer: function( )
32083 if (this.boxesEl) {
32084 return this.boxesEl;
32087 this.boxesEl = this.el.select('.mas-boxes').first();
32089 return this.boxesEl;
32093 initEvents : function()
32097 if(this.isAutoInitial){
32098 Roo.log('hook children rendered');
32099 this.on('childrenrendered', function() {
32100 Roo.log('children rendered');
32107 initial : function()
32109 this.reloadItems();
32111 this.currentSize = this.el.getBox(true);
32113 /// was window resize... - let's see if this works..
32114 Roo.EventManager.onWindowResize(this.resize, this);
32116 if(!this.isAutoInitial){
32121 this.layout.defer(500,this);
32124 reloadItems: function()
32126 this.bricks = this.el.select('.masonry-brick', true);
32128 this.bricks.each(function(b) {
32129 //Roo.log(b.getSize());
32130 if (!b.attr('originalwidth')) {
32131 b.attr('originalwidth', b.getSize().width);
32136 Roo.log(this.bricks.elements.length);
32139 resize : function()
32142 var cs = this.el.getBox(true);
32144 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32145 Roo.log("no change in with or X");
32148 this.currentSize = cs;
32152 layout : function()
32155 this._resetLayout();
32156 //this._manageStamps();
32158 // don't animate first layout
32159 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32160 this.layoutItems( isInstant );
32162 // flag for initalized
32163 this._isLayoutInited = true;
32166 layoutItems : function( isInstant )
32168 //var items = this._getItemsForLayout( this.items );
32169 // original code supports filtering layout items.. we just ignore it..
32171 this._layoutItems( this.bricks , isInstant );
32173 this._postLayout();
32175 _layoutItems : function ( items , isInstant)
32177 //this.fireEvent( 'layout', this, items );
32180 if ( !items || !items.elements.length ) {
32181 // no items, emit event with empty array
32186 items.each(function(item) {
32187 Roo.log("layout item");
32189 // get x/y object from method
32190 var position = this._getItemLayoutPosition( item );
32192 position.item = item;
32193 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32194 queue.push( position );
32197 this._processLayoutQueue( queue );
32199 /** Sets position of item in DOM
32200 * @param {Element} item
32201 * @param {Number} x - horizontal position
32202 * @param {Number} y - vertical position
32203 * @param {Boolean} isInstant - disables transitions
32205 _processLayoutQueue : function( queue )
32207 for ( var i=0, len = queue.length; i < len; i++ ) {
32208 var obj = queue[i];
32209 obj.item.position('absolute');
32210 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32216 * Any logic you want to do after each layout,
32217 * i.e. size the container
32219 _postLayout : function()
32221 this.resizeContainer();
32224 resizeContainer : function()
32226 if ( !this.isResizingContainer ) {
32229 var size = this._getContainerSize();
32231 this.el.setSize(size.width,size.height);
32232 this.boxesEl.setSize(size.width,size.height);
32238 _resetLayout : function()
32240 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32241 this.colWidth = this.el.getWidth();
32242 //this.gutter = this.el.getWidth();
32244 this.measureColumns();
32250 this.colYs.push( 0 );
32256 measureColumns : function()
32258 this.getContainerWidth();
32259 // if columnWidth is 0, default to outerWidth of first item
32260 if ( !this.columnWidth ) {
32261 var firstItem = this.bricks.first();
32262 Roo.log(firstItem);
32263 this.columnWidth = this.containerWidth;
32264 if (firstItem && firstItem.attr('originalwidth') ) {
32265 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32267 // columnWidth fall back to item of first element
32268 Roo.log("set column width?");
32269 this.initialColumnWidth = this.columnWidth ;
32271 // if first elem has no width, default to size of container
32276 if (this.initialColumnWidth) {
32277 this.columnWidth = this.initialColumnWidth;
32282 // column width is fixed at the top - however if container width get's smaller we should
32285 // this bit calcs how man columns..
32287 var columnWidth = this.columnWidth += this.gutter;
32289 // calculate columns
32290 var containerWidth = this.containerWidth + this.gutter;
32292 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32293 // fix rounding errors, typically with gutters
32294 var excess = columnWidth - containerWidth % columnWidth;
32297 // if overshoot is less than a pixel, round up, otherwise floor it
32298 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32299 cols = Math[ mathMethod ]( cols );
32300 this.cols = Math.max( cols, 1 );
32301 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32303 // padding positioning..
32304 var totalColWidth = this.cols * this.columnWidth;
32305 var padavail = this.containerWidth - totalColWidth;
32306 // so for 2 columns - we need 3 'pads'
32308 var padNeeded = (1+this.cols) * this.padWidth;
32310 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32312 this.columnWidth += padExtra
32313 //this.padWidth = Math.floor(padavail / ( this.cols));
32315 // adjust colum width so that padding is fixed??
32317 // we have 3 columns ... total = width * 3
32318 // we have X left over... that should be used by
32320 //if (this.expandC) {
32328 getContainerWidth : function()
32330 /* // container is parent if fit width
32331 var container = this.isFitWidth ? this.element.parentNode : this.element;
32332 // check that this.size and size are there
32333 // IE8 triggers resize on body size change, so they might not be
32335 var size = getSize( container ); //FIXME
32336 this.containerWidth = size && size.innerWidth; //FIXME
32339 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32343 _getItemLayoutPosition : function( item ) // what is item?
32345 // we resize the item to our columnWidth..
32347 item.setWidth(this.columnWidth);
32348 item.autoBoxAdjust = false;
32350 var sz = item.getSize();
32352 // how many columns does this brick span
32353 var remainder = this.containerWidth % this.columnWidth;
32355 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32356 // round if off by 1 pixel, otherwise use ceil
32357 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32358 colSpan = Math.min( colSpan, this.cols );
32360 // normally this should be '1' as we dont' currently allow multi width columns..
32362 var colGroup = this._getColGroup( colSpan );
32363 // get the minimum Y value from the columns
32364 var minimumY = Math.min.apply( Math, colGroup );
32365 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32367 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32369 // position the brick
32371 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32372 y: this.currentSize.y + minimumY + this.padHeight
32376 // apply setHeight to necessary columns
32377 var setHeight = minimumY + sz.height + this.padHeight;
32378 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32380 var setSpan = this.cols + 1 - colGroup.length;
32381 for ( var i = 0; i < setSpan; i++ ) {
32382 this.colYs[ shortColIndex + i ] = setHeight ;
32389 * @param {Number} colSpan - number of columns the element spans
32390 * @returns {Array} colGroup
32392 _getColGroup : function( colSpan )
32394 if ( colSpan < 2 ) {
32395 // if brick spans only one column, use all the column Ys
32400 // how many different places could this brick fit horizontally
32401 var groupCount = this.cols + 1 - colSpan;
32402 // for each group potential horizontal position
32403 for ( var i = 0; i < groupCount; i++ ) {
32404 // make an array of colY values for that one group
32405 var groupColYs = this.colYs.slice( i, i + colSpan );
32406 // and get the max value of the array
32407 colGroup[i] = Math.max.apply( Math, groupColYs );
32412 _manageStamp : function( stamp )
32414 var stampSize = stamp.getSize();
32415 var offset = stamp.getBox();
32416 // get the columns that this stamp affects
32417 var firstX = this.isOriginLeft ? offset.x : offset.right;
32418 var lastX = firstX + stampSize.width;
32419 var firstCol = Math.floor( firstX / this.columnWidth );
32420 firstCol = Math.max( 0, firstCol );
32422 var lastCol = Math.floor( lastX / this.columnWidth );
32423 // lastCol should not go over if multiple of columnWidth #425
32424 lastCol -= lastX % this.columnWidth ? 0 : 1;
32425 lastCol = Math.min( this.cols - 1, lastCol );
32427 // set colYs to bottom of the stamp
32428 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32431 for ( var i = firstCol; i <= lastCol; i++ ) {
32432 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32437 _getContainerSize : function()
32439 this.maxY = Math.max.apply( Math, this.colYs );
32444 if ( this.isFitWidth ) {
32445 size.width = this._getContainerFitWidth();
32451 _getContainerFitWidth : function()
32453 var unusedCols = 0;
32454 // count unused columns
32457 if ( this.colYs[i] !== 0 ) {
32462 // fit container to columns that have been used
32463 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32466 needsResizeLayout : function()
32468 var previousWidth = this.containerWidth;
32469 this.getContainerWidth();
32470 return previousWidth !== this.containerWidth;
32485 * @class Roo.bootstrap.MasonryBrick
32486 * @extends Roo.bootstrap.Component
32487 * Bootstrap MasonryBrick class
32490 * Create a new MasonryBrick
32491 * @param {Object} config The config object
32494 Roo.bootstrap.MasonryBrick = function(config){
32496 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32498 Roo.bootstrap.MasonryBrick.register(this);
32504 * When a MasonryBrick is clcik
32505 * @param {Roo.bootstrap.MasonryBrick} this
32506 * @param {Roo.EventObject} e
32512 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32515 * @cfg {String} title
32519 * @cfg {String} html
32523 * @cfg {String} bgimage
32527 * @cfg {String} videourl
32531 * @cfg {String} cls
32535 * @cfg {String} href
32539 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32544 * @cfg {String} placetitle (center|bottom)
32549 * @cfg {Boolean} isFitContainer defalut true
32551 isFitContainer : true,
32554 * @cfg {Boolean} preventDefault defalut false
32556 preventDefault : false,
32559 * @cfg {Boolean} inverse defalut false
32561 maskInverse : false,
32563 getAutoCreate : function()
32565 if(!this.isFitContainer){
32566 return this.getSplitAutoCreate();
32569 var cls = 'masonry-brick masonry-brick-full';
32571 if(this.href.length){
32572 cls += ' masonry-brick-link';
32575 if(this.bgimage.length){
32576 cls += ' masonry-brick-image';
32579 if(this.maskInverse){
32580 cls += ' mask-inverse';
32583 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32584 cls += ' enable-mask';
32588 cls += ' masonry-' + this.size + '-brick';
32591 if(this.placetitle.length){
32593 switch (this.placetitle) {
32595 cls += ' masonry-center-title';
32598 cls += ' masonry-bottom-title';
32605 if(!this.html.length && !this.bgimage.length){
32606 cls += ' masonry-center-title';
32609 if(!this.html.length && this.bgimage.length){
32610 cls += ' masonry-bottom-title';
32615 cls += ' ' + this.cls;
32619 tag: (this.href.length) ? 'a' : 'div',
32624 cls: 'masonry-brick-mask'
32628 cls: 'masonry-brick-paragraph',
32634 if(this.href.length){
32635 cfg.href = this.href;
32638 var cn = cfg.cn[1].cn;
32640 if(this.title.length){
32643 cls: 'masonry-brick-title',
32648 if(this.html.length){
32651 cls: 'masonry-brick-text',
32656 if (!this.title.length && !this.html.length) {
32657 cfg.cn[1].cls += ' hide';
32660 if(this.bgimage.length){
32663 cls: 'masonry-brick-image-view',
32668 if(this.videourl.length){
32669 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32670 // youtube support only?
32673 cls: 'masonry-brick-image-view',
32676 allowfullscreen : true
32684 getSplitAutoCreate : function()
32686 var cls = 'masonry-brick masonry-brick-split';
32688 if(this.href.length){
32689 cls += ' masonry-brick-link';
32692 if(this.bgimage.length){
32693 cls += ' masonry-brick-image';
32697 cls += ' masonry-' + this.size + '-brick';
32700 switch (this.placetitle) {
32702 cls += ' masonry-center-title';
32705 cls += ' masonry-bottom-title';
32708 if(!this.bgimage.length){
32709 cls += ' masonry-center-title';
32712 if(this.bgimage.length){
32713 cls += ' masonry-bottom-title';
32719 cls += ' ' + this.cls;
32723 tag: (this.href.length) ? 'a' : 'div',
32728 cls: 'masonry-brick-split-head',
32732 cls: 'masonry-brick-paragraph',
32739 cls: 'masonry-brick-split-body',
32745 if(this.href.length){
32746 cfg.href = this.href;
32749 if(this.title.length){
32750 cfg.cn[0].cn[0].cn.push({
32752 cls: 'masonry-brick-title',
32757 if(this.html.length){
32758 cfg.cn[1].cn.push({
32760 cls: 'masonry-brick-text',
32765 if(this.bgimage.length){
32766 cfg.cn[0].cn.push({
32768 cls: 'masonry-brick-image-view',
32773 if(this.videourl.length){
32774 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32775 // youtube support only?
32776 cfg.cn[0].cn.cn.push({
32778 cls: 'masonry-brick-image-view',
32781 allowfullscreen : true
32788 initEvents: function()
32790 switch (this.size) {
32823 this.el.on('touchstart', this.onTouchStart, this);
32824 this.el.on('touchmove', this.onTouchMove, this);
32825 this.el.on('touchend', this.onTouchEnd, this);
32826 this.el.on('contextmenu', this.onContextMenu, this);
32828 this.el.on('mouseenter' ,this.enter, this);
32829 this.el.on('mouseleave', this.leave, this);
32830 this.el.on('click', this.onClick, this);
32833 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32834 this.parent().bricks.push(this);
32839 onClick: function(e, el)
32841 var time = this.endTimer - this.startTimer;
32842 // Roo.log(e.preventDefault());
32845 e.preventDefault();
32850 if(!this.preventDefault){
32854 e.preventDefault();
32856 if (this.activeClass != '') {
32857 this.selectBrick();
32860 this.fireEvent('click', this, e);
32863 enter: function(e, el)
32865 e.preventDefault();
32867 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32871 if(this.bgimage.length && this.html.length){
32872 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32876 leave: function(e, el)
32878 e.preventDefault();
32880 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32884 if(this.bgimage.length && this.html.length){
32885 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32889 onTouchStart: function(e, el)
32891 // e.preventDefault();
32893 this.touchmoved = false;
32895 if(!this.isFitContainer){
32899 if(!this.bgimage.length || !this.html.length){
32903 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32905 this.timer = new Date().getTime();
32909 onTouchMove: function(e, el)
32911 this.touchmoved = true;
32914 onContextMenu : function(e,el)
32916 e.preventDefault();
32917 e.stopPropagation();
32921 onTouchEnd: function(e, el)
32923 // e.preventDefault();
32925 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32932 if(!this.bgimage.length || !this.html.length){
32934 if(this.href.length){
32935 window.location.href = this.href;
32941 if(!this.isFitContainer){
32945 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32947 window.location.href = this.href;
32950 //selection on single brick only
32951 selectBrick : function() {
32953 if (!this.parentId) {
32957 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32958 var index = m.selectedBrick.indexOf(this.id);
32961 m.selectedBrick.splice(index,1);
32962 this.el.removeClass(this.activeClass);
32966 for(var i = 0; i < m.selectedBrick.length; i++) {
32967 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32968 b.el.removeClass(b.activeClass);
32971 m.selectedBrick = [];
32973 m.selectedBrick.push(this.id);
32974 this.el.addClass(this.activeClass);
32978 isSelected : function(){
32979 return this.el.hasClass(this.activeClass);
32984 Roo.apply(Roo.bootstrap.MasonryBrick, {
32987 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32989 * register a Masonry Brick
32990 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32993 register : function(brick)
32995 //this.groups[brick.id] = brick;
32996 this.groups.add(brick.id, brick);
32999 * fetch a masonry brick based on the masonry brick ID
33000 * @param {string} the masonry brick to add
33001 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33004 get: function(brick_id)
33006 // if (typeof(this.groups[brick_id]) == 'undefined') {
33009 // return this.groups[brick_id] ;
33011 if(this.groups.key(brick_id)) {
33012 return this.groups.key(brick_id);
33030 * @class Roo.bootstrap.Brick
33031 * @extends Roo.bootstrap.Component
33032 * Bootstrap Brick class
33035 * Create a new Brick
33036 * @param {Object} config The config object
33039 Roo.bootstrap.Brick = function(config){
33040 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33046 * When a Brick is click
33047 * @param {Roo.bootstrap.Brick} this
33048 * @param {Roo.EventObject} e
33054 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33057 * @cfg {String} title
33061 * @cfg {String} html
33065 * @cfg {String} bgimage
33069 * @cfg {String} cls
33073 * @cfg {String} href
33077 * @cfg {String} video
33081 * @cfg {Boolean} square
33085 getAutoCreate : function()
33087 var cls = 'roo-brick';
33089 if(this.href.length){
33090 cls += ' roo-brick-link';
33093 if(this.bgimage.length){
33094 cls += ' roo-brick-image';
33097 if(!this.html.length && !this.bgimage.length){
33098 cls += ' roo-brick-center-title';
33101 if(!this.html.length && this.bgimage.length){
33102 cls += ' roo-brick-bottom-title';
33106 cls += ' ' + this.cls;
33110 tag: (this.href.length) ? 'a' : 'div',
33115 cls: 'roo-brick-paragraph',
33121 if(this.href.length){
33122 cfg.href = this.href;
33125 var cn = cfg.cn[0].cn;
33127 if(this.title.length){
33130 cls: 'roo-brick-title',
33135 if(this.html.length){
33138 cls: 'roo-brick-text',
33145 if(this.bgimage.length){
33148 cls: 'roo-brick-image-view',
33156 initEvents: function()
33158 if(this.title.length || this.html.length){
33159 this.el.on('mouseenter' ,this.enter, this);
33160 this.el.on('mouseleave', this.leave, this);
33163 Roo.EventManager.onWindowResize(this.resize, this);
33165 if(this.bgimage.length){
33166 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33167 this.imageEl.on('load', this.onImageLoad, this);
33174 onImageLoad : function()
33179 resize : function()
33181 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33183 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33185 if(this.bgimage.length){
33186 var image = this.el.select('.roo-brick-image-view', true).first();
33188 image.setWidth(paragraph.getWidth());
33191 image.setHeight(paragraph.getWidth());
33194 this.el.setHeight(image.getHeight());
33195 paragraph.setHeight(image.getHeight());
33201 enter: function(e, el)
33203 e.preventDefault();
33205 if(this.bgimage.length){
33206 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33207 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33211 leave: function(e, el)
33213 e.preventDefault();
33215 if(this.bgimage.length){
33216 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33217 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33232 * @class Roo.bootstrap.NumberField
33233 * @extends Roo.bootstrap.Input
33234 * Bootstrap NumberField class
33240 * Create a new NumberField
33241 * @param {Object} config The config object
33244 Roo.bootstrap.NumberField = function(config){
33245 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33248 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33251 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33253 allowDecimals : true,
33255 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33257 decimalSeparator : ".",
33259 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33261 decimalPrecision : 2,
33263 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33265 allowNegative : true,
33268 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33272 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33274 minValue : Number.NEGATIVE_INFINITY,
33276 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33278 maxValue : Number.MAX_VALUE,
33280 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33282 minText : "The minimum value for this field is {0}",
33284 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33286 maxText : "The maximum value for this field is {0}",
33288 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33289 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33291 nanText : "{0} is not a valid number",
33293 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33295 thousandsDelimiter : false,
33297 * @cfg {String} valueAlign alignment of value
33299 valueAlign : "left",
33301 getAutoCreate : function()
33303 var hiddenInput = {
33307 cls: 'hidden-number-input'
33311 hiddenInput.name = this.name;
33316 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33318 this.name = hiddenInput.name;
33320 if(cfg.cn.length > 0) {
33321 cfg.cn.push(hiddenInput);
33328 initEvents : function()
33330 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33332 var allowed = "0123456789";
33334 if(this.allowDecimals){
33335 allowed += this.decimalSeparator;
33338 if(this.allowNegative){
33342 if(this.thousandsDelimiter) {
33346 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33348 var keyPress = function(e){
33350 var k = e.getKey();
33352 var c = e.getCharCode();
33355 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33356 allowed.indexOf(String.fromCharCode(c)) === -1
33362 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33366 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33371 this.el.on("keypress", keyPress, this);
33374 validateValue : function(value)
33377 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33381 var num = this.parseValue(value);
33384 this.markInvalid(String.format(this.nanText, value));
33388 if(num < this.minValue){
33389 this.markInvalid(String.format(this.minText, this.minValue));
33393 if(num > this.maxValue){
33394 this.markInvalid(String.format(this.maxText, this.maxValue));
33401 getValue : function()
33403 var v = this.hiddenEl().getValue();
33405 return this.fixPrecision(this.parseValue(v));
33408 parseValue : function(value)
33410 if(this.thousandsDelimiter) {
33412 r = new RegExp(",", "g");
33413 value = value.replace(r, "");
33416 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33417 return isNaN(value) ? '' : value;
33420 fixPrecision : function(value)
33422 if(this.thousandsDelimiter) {
33424 r = new RegExp(",", "g");
33425 value = value.replace(r, "");
33428 var nan = isNaN(value);
33430 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33431 return nan ? '' : value;
33433 return parseFloat(value).toFixed(this.decimalPrecision);
33436 setValue : function(v)
33438 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33444 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33446 this.inputEl().dom.value = (v == '') ? '' :
33447 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33449 if(!this.allowZero && v === '0') {
33450 this.hiddenEl().dom.value = '';
33451 this.inputEl().dom.value = '';
33458 decimalPrecisionFcn : function(v)
33460 return Math.floor(v);
33463 beforeBlur : function()
33465 var v = this.parseValue(this.getRawValue());
33467 if(v || v === 0 || v === ''){
33472 hiddenEl : function()
33474 return this.el.select('input.hidden-number-input',true).first();
33486 * @class Roo.bootstrap.DocumentSlider
33487 * @extends Roo.bootstrap.Component
33488 * Bootstrap DocumentSlider class
33491 * Create a new DocumentViewer
33492 * @param {Object} config The config object
33495 Roo.bootstrap.DocumentSlider = function(config){
33496 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33503 * Fire after initEvent
33504 * @param {Roo.bootstrap.DocumentSlider} this
33509 * Fire after update
33510 * @param {Roo.bootstrap.DocumentSlider} this
33516 * @param {Roo.bootstrap.DocumentSlider} this
33522 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33528 getAutoCreate : function()
33532 cls : 'roo-document-slider',
33536 cls : 'roo-document-slider-header',
33540 cls : 'roo-document-slider-header-title'
33546 cls : 'roo-document-slider-body',
33550 cls : 'roo-document-slider-prev',
33554 cls : 'fa fa-chevron-left'
33560 cls : 'roo-document-slider-thumb',
33564 cls : 'roo-document-slider-image'
33570 cls : 'roo-document-slider-next',
33574 cls : 'fa fa-chevron-right'
33586 initEvents : function()
33588 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33589 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33591 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33592 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33594 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33595 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33597 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33598 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33600 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33601 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33603 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33604 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33606 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33607 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33609 this.thumbEl.on('click', this.onClick, this);
33611 this.prevIndicator.on('click', this.prev, this);
33613 this.nextIndicator.on('click', this.next, this);
33617 initial : function()
33619 if(this.files.length){
33620 this.indicator = 1;
33624 this.fireEvent('initial', this);
33627 update : function()
33629 this.imageEl.attr('src', this.files[this.indicator - 1]);
33631 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33633 this.prevIndicator.show();
33635 if(this.indicator == 1){
33636 this.prevIndicator.hide();
33639 this.nextIndicator.show();
33641 if(this.indicator == this.files.length){
33642 this.nextIndicator.hide();
33645 this.thumbEl.scrollTo('top');
33647 this.fireEvent('update', this);
33650 onClick : function(e)
33652 e.preventDefault();
33654 this.fireEvent('click', this);
33659 e.preventDefault();
33661 this.indicator = Math.max(1, this.indicator - 1);
33668 e.preventDefault();
33670 this.indicator = Math.min(this.files.length, this.indicator + 1);
33684 * @class Roo.bootstrap.RadioSet
33685 * @extends Roo.bootstrap.Input
33686 * Bootstrap RadioSet class
33687 * @cfg {String} indicatorpos (left|right) default left
33688 * @cfg {Boolean} inline (true|false) inline the element (default true)
33689 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33691 * Create a new RadioSet
33692 * @param {Object} config The config object
33695 Roo.bootstrap.RadioSet = function(config){
33697 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33701 Roo.bootstrap.RadioSet.register(this);
33706 * Fires when the element is checked or unchecked.
33707 * @param {Roo.bootstrap.RadioSet} this This radio
33708 * @param {Roo.bootstrap.Radio} item The checked item
33713 * Fires when the element is click.
33714 * @param {Roo.bootstrap.RadioSet} this This radio set
33715 * @param {Roo.bootstrap.Radio} item The checked item
33716 * @param {Roo.EventObject} e The event object
33723 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33731 indicatorpos : 'left',
33733 getAutoCreate : function()
33737 cls : 'roo-radio-set-label',
33741 html : this.fieldLabel
33746 if(this.indicatorpos == 'left'){
33749 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33750 tooltip : 'This field is required'
33755 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33756 tooltip : 'This field is required'
33762 cls : 'roo-radio-set-items'
33765 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33767 if (align === 'left' && this.fieldLabel.length) {
33770 cls : "roo-radio-set-right",
33776 if(this.labelWidth > 12){
33777 label.style = "width: " + this.labelWidth + 'px';
33780 if(this.labelWidth < 13 && this.labelmd == 0){
33781 this.labelmd = this.labelWidth;
33784 if(this.labellg > 0){
33785 label.cls += ' col-lg-' + this.labellg;
33786 items.cls += ' col-lg-' + (12 - this.labellg);
33789 if(this.labelmd > 0){
33790 label.cls += ' col-md-' + this.labelmd;
33791 items.cls += ' col-md-' + (12 - this.labelmd);
33794 if(this.labelsm > 0){
33795 label.cls += ' col-sm-' + this.labelsm;
33796 items.cls += ' col-sm-' + (12 - this.labelsm);
33799 if(this.labelxs > 0){
33800 label.cls += ' col-xs-' + this.labelxs;
33801 items.cls += ' col-xs-' + (12 - this.labelxs);
33807 cls : 'roo-radio-set',
33811 cls : 'roo-radio-set-input',
33814 value : this.value ? this.value : ''
33821 if(this.weight.length){
33822 cfg.cls += ' roo-radio-' + this.weight;
33826 cfg.cls += ' roo-radio-set-inline';
33830 ['xs','sm','md','lg'].map(function(size){
33831 if (settings[size]) {
33832 cfg.cls += ' col-' + size + '-' + settings[size];
33840 initEvents : function()
33842 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33843 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33845 if(!this.fieldLabel.length){
33846 this.labelEl.hide();
33849 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33850 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33852 this.indicator = this.indicatorEl();
33854 if(this.indicator){
33855 this.indicator.addClass('invisible');
33858 this.originalValue = this.getValue();
33862 inputEl: function ()
33864 return this.el.select('.roo-radio-set-input', true).first();
33867 getChildContainer : function()
33869 return this.itemsEl;
33872 register : function(item)
33874 this.radioes.push(item);
33878 validate : function()
33880 if(this.getVisibilityEl().hasClass('hidden')){
33886 Roo.each(this.radioes, function(i){
33895 if(this.allowBlank) {
33899 if(this.disabled || valid){
33904 this.markInvalid();
33909 markValid : function()
33911 if(this.labelEl.isVisible(true)){
33912 this.indicatorEl().removeClass('visible');
33913 this.indicatorEl().addClass('invisible');
33916 this.el.removeClass([this.invalidClass, this.validClass]);
33917 this.el.addClass(this.validClass);
33919 this.fireEvent('valid', this);
33922 markInvalid : function(msg)
33924 if(this.allowBlank || this.disabled){
33928 if(this.labelEl.isVisible(true)){
33929 this.indicatorEl().removeClass('invisible');
33930 this.indicatorEl().addClass('visible');
33933 this.el.removeClass([this.invalidClass, this.validClass]);
33934 this.el.addClass(this.invalidClass);
33936 this.fireEvent('invalid', this, msg);
33940 setValue : function(v, suppressEvent)
33942 if(this.value === v){
33949 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33952 Roo.each(this.radioes, function(i){
33954 i.el.removeClass('checked');
33957 Roo.each(this.radioes, function(i){
33959 if(i.value === v || i.value.toString() === v.toString()){
33961 i.el.addClass('checked');
33963 if(suppressEvent !== true){
33964 this.fireEvent('check', this, i);
33975 clearInvalid : function(){
33977 if(!this.el || this.preventMark){
33981 this.el.removeClass([this.invalidClass]);
33983 this.fireEvent('valid', this);
33988 Roo.apply(Roo.bootstrap.RadioSet, {
33992 register : function(set)
33994 this.groups[set.name] = set;
33997 get: function(name)
33999 if (typeof(this.groups[name]) == 'undefined') {
34003 return this.groups[name] ;
34009 * Ext JS Library 1.1.1
34010 * Copyright(c) 2006-2007, Ext JS, LLC.
34012 * Originally Released Under LGPL - original licence link has changed is not relivant.
34015 * <script type="text/javascript">
34020 * @class Roo.bootstrap.SplitBar
34021 * @extends Roo.util.Observable
34022 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34026 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34027 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34028 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34029 split.minSize = 100;
34030 split.maxSize = 600;
34031 split.animate = true;
34032 split.on('moved', splitterMoved);
34035 * Create a new SplitBar
34036 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34037 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34038 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34039 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34040 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34041 position of the SplitBar).
34043 Roo.bootstrap.SplitBar = function(cfg){
34048 // dragElement : elm
34049 // resizingElement: el,
34051 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34052 // placement : Roo.bootstrap.SplitBar.LEFT ,
34053 // existingProxy ???
34056 this.el = Roo.get(cfg.dragElement, true);
34057 this.el.dom.unselectable = "on";
34059 this.resizingEl = Roo.get(cfg.resizingElement, true);
34063 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34064 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34067 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34070 * The minimum size of the resizing element. (Defaults to 0)
34076 * The maximum size of the resizing element. (Defaults to 2000)
34079 this.maxSize = 2000;
34082 * Whether to animate the transition to the new size
34085 this.animate = false;
34088 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34091 this.useShim = false;
34096 if(!cfg.existingProxy){
34098 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34100 this.proxy = Roo.get(cfg.existingProxy).dom;
34103 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34106 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34109 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34112 this.dragSpecs = {};
34115 * @private The adapter to use to positon and resize elements
34117 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34118 this.adapter.init(this);
34120 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34122 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34123 this.el.addClass("roo-splitbar-h");
34126 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34127 this.el.addClass("roo-splitbar-v");
34133 * Fires when the splitter is moved (alias for {@link #event-moved})
34134 * @param {Roo.bootstrap.SplitBar} this
34135 * @param {Number} newSize the new width or height
34140 * Fires when the splitter is moved
34141 * @param {Roo.bootstrap.SplitBar} this
34142 * @param {Number} newSize the new width or height
34146 * @event beforeresize
34147 * Fires before the splitter is dragged
34148 * @param {Roo.bootstrap.SplitBar} this
34150 "beforeresize" : true,
34152 "beforeapply" : true
34155 Roo.util.Observable.call(this);
34158 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34159 onStartProxyDrag : function(x, y){
34160 this.fireEvent("beforeresize", this);
34162 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34164 o.enableDisplayMode("block");
34165 // all splitbars share the same overlay
34166 Roo.bootstrap.SplitBar.prototype.overlay = o;
34168 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34169 this.overlay.show();
34170 Roo.get(this.proxy).setDisplayed("block");
34171 var size = this.adapter.getElementSize(this);
34172 this.activeMinSize = this.getMinimumSize();;
34173 this.activeMaxSize = this.getMaximumSize();;
34174 var c1 = size - this.activeMinSize;
34175 var c2 = Math.max(this.activeMaxSize - size, 0);
34176 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34177 this.dd.resetConstraints();
34178 this.dd.setXConstraint(
34179 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34180 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34182 this.dd.setYConstraint(0, 0);
34184 this.dd.resetConstraints();
34185 this.dd.setXConstraint(0, 0);
34186 this.dd.setYConstraint(
34187 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34188 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34191 this.dragSpecs.startSize = size;
34192 this.dragSpecs.startPoint = [x, y];
34193 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34197 * @private Called after the drag operation by the DDProxy
34199 onEndProxyDrag : function(e){
34200 Roo.get(this.proxy).setDisplayed(false);
34201 var endPoint = Roo.lib.Event.getXY(e);
34203 this.overlay.hide();
34206 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34207 newSize = this.dragSpecs.startSize +
34208 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34209 endPoint[0] - this.dragSpecs.startPoint[0] :
34210 this.dragSpecs.startPoint[0] - endPoint[0]
34213 newSize = this.dragSpecs.startSize +
34214 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34215 endPoint[1] - this.dragSpecs.startPoint[1] :
34216 this.dragSpecs.startPoint[1] - endPoint[1]
34219 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34220 if(newSize != this.dragSpecs.startSize){
34221 if(this.fireEvent('beforeapply', this, newSize) !== false){
34222 this.adapter.setElementSize(this, newSize);
34223 this.fireEvent("moved", this, newSize);
34224 this.fireEvent("resize", this, newSize);
34230 * Get the adapter this SplitBar uses
34231 * @return The adapter object
34233 getAdapter : function(){
34234 return this.adapter;
34238 * Set the adapter this SplitBar uses
34239 * @param {Object} adapter A SplitBar adapter object
34241 setAdapter : function(adapter){
34242 this.adapter = adapter;
34243 this.adapter.init(this);
34247 * Gets the minimum size for the resizing element
34248 * @return {Number} The minimum size
34250 getMinimumSize : function(){
34251 return this.minSize;
34255 * Sets the minimum size for the resizing element
34256 * @param {Number} minSize The minimum size
34258 setMinimumSize : function(minSize){
34259 this.minSize = minSize;
34263 * Gets the maximum size for the resizing element
34264 * @return {Number} The maximum size
34266 getMaximumSize : function(){
34267 return this.maxSize;
34271 * Sets the maximum size for the resizing element
34272 * @param {Number} maxSize The maximum size
34274 setMaximumSize : function(maxSize){
34275 this.maxSize = maxSize;
34279 * Sets the initialize size for the resizing element
34280 * @param {Number} size The initial size
34282 setCurrentSize : function(size){
34283 var oldAnimate = this.animate;
34284 this.animate = false;
34285 this.adapter.setElementSize(this, size);
34286 this.animate = oldAnimate;
34290 * Destroy this splitbar.
34291 * @param {Boolean} removeEl True to remove the element
34293 destroy : function(removeEl){
34295 this.shim.remove();
34298 this.proxy.parentNode.removeChild(this.proxy);
34306 * @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.
34308 Roo.bootstrap.SplitBar.createProxy = function(dir){
34309 var proxy = new Roo.Element(document.createElement("div"));
34310 proxy.unselectable();
34311 var cls = 'roo-splitbar-proxy';
34312 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34313 document.body.appendChild(proxy.dom);
34318 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34319 * Default Adapter. It assumes the splitter and resizing element are not positioned
34320 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34322 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34325 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34326 // do nothing for now
34327 init : function(s){
34331 * Called before drag operations to get the current size of the resizing element.
34332 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34334 getElementSize : function(s){
34335 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34336 return s.resizingEl.getWidth();
34338 return s.resizingEl.getHeight();
34343 * Called after drag operations to set the size of the resizing element.
34344 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34345 * @param {Number} newSize The new size to set
34346 * @param {Function} onComplete A function to be invoked when resizing is complete
34348 setElementSize : function(s, newSize, onComplete){
34349 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34351 s.resizingEl.setWidth(newSize);
34353 onComplete(s, newSize);
34356 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34361 s.resizingEl.setHeight(newSize);
34363 onComplete(s, newSize);
34366 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34373 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34374 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34375 * Adapter that moves the splitter element to align with the resized sizing element.
34376 * Used with an absolute positioned SplitBar.
34377 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34378 * document.body, make sure you assign an id to the body element.
34380 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34381 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34382 this.container = Roo.get(container);
34385 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34386 init : function(s){
34387 this.basic.init(s);
34390 getElementSize : function(s){
34391 return this.basic.getElementSize(s);
34394 setElementSize : function(s, newSize, onComplete){
34395 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34398 moveSplitter : function(s){
34399 var yes = Roo.bootstrap.SplitBar;
34400 switch(s.placement){
34402 s.el.setX(s.resizingEl.getRight());
34405 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34408 s.el.setY(s.resizingEl.getBottom());
34411 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34418 * Orientation constant - Create a vertical SplitBar
34422 Roo.bootstrap.SplitBar.VERTICAL = 1;
34425 * Orientation constant - Create a horizontal SplitBar
34429 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34432 * Placement constant - The resizing element is to the left of the splitter element
34436 Roo.bootstrap.SplitBar.LEFT = 1;
34439 * Placement constant - The resizing element is to the right of the splitter element
34443 Roo.bootstrap.SplitBar.RIGHT = 2;
34446 * Placement constant - The resizing element is positioned above the splitter element
34450 Roo.bootstrap.SplitBar.TOP = 3;
34453 * Placement constant - The resizing element is positioned under splitter element
34457 Roo.bootstrap.SplitBar.BOTTOM = 4;
34458 Roo.namespace("Roo.bootstrap.layout");/*
34460 * Ext JS Library 1.1.1
34461 * Copyright(c) 2006-2007, Ext JS, LLC.
34463 * Originally Released Under LGPL - original licence link has changed is not relivant.
34466 * <script type="text/javascript">
34470 * @class Roo.bootstrap.layout.Manager
34471 * @extends Roo.bootstrap.Component
34472 * Base class for layout managers.
34474 Roo.bootstrap.layout.Manager = function(config)
34476 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34482 /** false to disable window resize monitoring @type Boolean */
34483 this.monitorWindowResize = true;
34488 * Fires when a layout is performed.
34489 * @param {Roo.LayoutManager} this
34493 * @event regionresized
34494 * Fires when the user resizes a region.
34495 * @param {Roo.LayoutRegion} region The resized region
34496 * @param {Number} newSize The new size (width for east/west, height for north/south)
34498 "regionresized" : true,
34500 * @event regioncollapsed
34501 * Fires when a region is collapsed.
34502 * @param {Roo.LayoutRegion} region The collapsed region
34504 "regioncollapsed" : true,
34506 * @event regionexpanded
34507 * Fires when a region is expanded.
34508 * @param {Roo.LayoutRegion} region The expanded region
34510 "regionexpanded" : true
34512 this.updating = false;
34515 this.el = Roo.get(config.el);
34521 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34526 monitorWindowResize : true,
34532 onRender : function(ct, position)
34535 this.el = Roo.get(ct);
34538 //this.fireEvent('render',this);
34542 initEvents: function()
34546 // ie scrollbar fix
34547 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34548 document.body.scroll = "no";
34549 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34550 this.el.position('relative');
34552 this.id = this.el.id;
34553 this.el.addClass("roo-layout-container");
34554 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34555 if(this.el.dom != document.body ) {
34556 this.el.on('resize', this.layout,this);
34557 this.el.on('show', this.layout,this);
34563 * Returns true if this layout is currently being updated
34564 * @return {Boolean}
34566 isUpdating : function(){
34567 return this.updating;
34571 * Suspend the LayoutManager from doing auto-layouts while
34572 * making multiple add or remove calls
34574 beginUpdate : function(){
34575 this.updating = true;
34579 * Restore auto-layouts and optionally disable the manager from performing a layout
34580 * @param {Boolean} noLayout true to disable a layout update
34582 endUpdate : function(noLayout){
34583 this.updating = false;
34589 layout: function(){
34593 onRegionResized : function(region, newSize){
34594 this.fireEvent("regionresized", region, newSize);
34598 onRegionCollapsed : function(region){
34599 this.fireEvent("regioncollapsed", region);
34602 onRegionExpanded : function(region){
34603 this.fireEvent("regionexpanded", region);
34607 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34608 * performs box-model adjustments.
34609 * @return {Object} The size as an object {width: (the width), height: (the height)}
34611 getViewSize : function()
34614 if(this.el.dom != document.body){
34615 size = this.el.getSize();
34617 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34619 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34620 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34625 * Returns the Element this layout is bound to.
34626 * @return {Roo.Element}
34628 getEl : function(){
34633 * Returns the specified region.
34634 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34635 * @return {Roo.LayoutRegion}
34637 getRegion : function(target){
34638 return this.regions[target.toLowerCase()];
34641 onWindowResize : function(){
34642 if(this.monitorWindowResize){
34649 * Ext JS Library 1.1.1
34650 * Copyright(c) 2006-2007, Ext JS, LLC.
34652 * Originally Released Under LGPL - original licence link has changed is not relivant.
34655 * <script type="text/javascript">
34658 * @class Roo.bootstrap.layout.Border
34659 * @extends Roo.bootstrap.layout.Manager
34660 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34661 * please see: examples/bootstrap/nested.html<br><br>
34663 <b>The container the layout is rendered into can be either the body element or any other element.
34664 If it is not the body element, the container needs to either be an absolute positioned element,
34665 or you will need to add "position:relative" to the css of the container. You will also need to specify
34666 the container size if it is not the body element.</b>
34669 * Create a new Border
34670 * @param {Object} config Configuration options
34672 Roo.bootstrap.layout.Border = function(config){
34673 config = config || {};
34674 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34678 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34679 if(config[region]){
34680 config[region].region = region;
34681 this.addRegion(config[region]);
34687 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34689 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34691 * Creates and adds a new region if it doesn't already exist.
34692 * @param {String} target The target region key (north, south, east, west or center).
34693 * @param {Object} config The regions config object
34694 * @return {BorderLayoutRegion} The new region
34696 addRegion : function(config)
34698 if(!this.regions[config.region]){
34699 var r = this.factory(config);
34700 this.bindRegion(r);
34702 return this.regions[config.region];
34706 bindRegion : function(r){
34707 this.regions[r.config.region] = r;
34709 r.on("visibilitychange", this.layout, this);
34710 r.on("paneladded", this.layout, this);
34711 r.on("panelremoved", this.layout, this);
34712 r.on("invalidated", this.layout, this);
34713 r.on("resized", this.onRegionResized, this);
34714 r.on("collapsed", this.onRegionCollapsed, this);
34715 r.on("expanded", this.onRegionExpanded, this);
34719 * Performs a layout update.
34721 layout : function()
34723 if(this.updating) {
34727 // render all the rebions if they have not been done alreayd?
34728 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34729 if(this.regions[region] && !this.regions[region].bodyEl){
34730 this.regions[region].onRender(this.el)
34734 var size = this.getViewSize();
34735 var w = size.width;
34736 var h = size.height;
34741 //var x = 0, y = 0;
34743 var rs = this.regions;
34744 var north = rs["north"];
34745 var south = rs["south"];
34746 var west = rs["west"];
34747 var east = rs["east"];
34748 var center = rs["center"];
34749 //if(this.hideOnLayout){ // not supported anymore
34750 //c.el.setStyle("display", "none");
34752 if(north && north.isVisible()){
34753 var b = north.getBox();
34754 var m = north.getMargins();
34755 b.width = w - (m.left+m.right);
34758 centerY = b.height + b.y + m.bottom;
34759 centerH -= centerY;
34760 north.updateBox(this.safeBox(b));
34762 if(south && south.isVisible()){
34763 var b = south.getBox();
34764 var m = south.getMargins();
34765 b.width = w - (m.left+m.right);
34767 var totalHeight = (b.height + m.top + m.bottom);
34768 b.y = h - totalHeight + m.top;
34769 centerH -= totalHeight;
34770 south.updateBox(this.safeBox(b));
34772 if(west && west.isVisible()){
34773 var b = west.getBox();
34774 var m = west.getMargins();
34775 b.height = centerH - (m.top+m.bottom);
34777 b.y = centerY + m.top;
34778 var totalWidth = (b.width + m.left + m.right);
34779 centerX += totalWidth;
34780 centerW -= totalWidth;
34781 west.updateBox(this.safeBox(b));
34783 if(east && east.isVisible()){
34784 var b = east.getBox();
34785 var m = east.getMargins();
34786 b.height = centerH - (m.top+m.bottom);
34787 var totalWidth = (b.width + m.left + m.right);
34788 b.x = w - totalWidth + m.left;
34789 b.y = centerY + m.top;
34790 centerW -= totalWidth;
34791 east.updateBox(this.safeBox(b));
34794 var m = center.getMargins();
34796 x: centerX + m.left,
34797 y: centerY + m.top,
34798 width: centerW - (m.left+m.right),
34799 height: centerH - (m.top+m.bottom)
34801 //if(this.hideOnLayout){
34802 //center.el.setStyle("display", "block");
34804 center.updateBox(this.safeBox(centerBox));
34807 this.fireEvent("layout", this);
34811 safeBox : function(box){
34812 box.width = Math.max(0, box.width);
34813 box.height = Math.max(0, box.height);
34818 * Adds a ContentPanel (or subclass) to this layout.
34819 * @param {String} target The target region key (north, south, east, west or center).
34820 * @param {Roo.ContentPanel} panel The panel to add
34821 * @return {Roo.ContentPanel} The added panel
34823 add : function(target, panel){
34825 target = target.toLowerCase();
34826 return this.regions[target].add(panel);
34830 * Remove a ContentPanel (or subclass) to this layout.
34831 * @param {String} target The target region key (north, south, east, west or center).
34832 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34833 * @return {Roo.ContentPanel} The removed panel
34835 remove : function(target, panel){
34836 target = target.toLowerCase();
34837 return this.regions[target].remove(panel);
34841 * Searches all regions for a panel with the specified id
34842 * @param {String} panelId
34843 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34845 findPanel : function(panelId){
34846 var rs = this.regions;
34847 for(var target in rs){
34848 if(typeof rs[target] != "function"){
34849 var p = rs[target].getPanel(panelId);
34859 * Searches all regions for a panel with the specified id and activates (shows) it.
34860 * @param {String/ContentPanel} panelId The panels id or the panel itself
34861 * @return {Roo.ContentPanel} The shown panel or null
34863 showPanel : function(panelId) {
34864 var rs = this.regions;
34865 for(var target in rs){
34866 var r = rs[target];
34867 if(typeof r != "function"){
34868 if(r.hasPanel(panelId)){
34869 return r.showPanel(panelId);
34877 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34878 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34881 restoreState : function(provider){
34883 provider = Roo.state.Manager;
34885 var sm = new Roo.LayoutStateManager();
34886 sm.init(this, provider);
34892 * Adds a xtype elements to the layout.
34896 xtype : 'ContentPanel',
34903 xtype : 'NestedLayoutPanel',
34909 items : [ ... list of content panels or nested layout panels.. ]
34913 * @param {Object} cfg Xtype definition of item to add.
34915 addxtype : function(cfg)
34917 // basically accepts a pannel...
34918 // can accept a layout region..!?!?
34919 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34922 // theory? children can only be panels??
34924 //if (!cfg.xtype.match(/Panel$/)) {
34929 if (typeof(cfg.region) == 'undefined') {
34930 Roo.log("Failed to add Panel, region was not set");
34934 var region = cfg.region;
34940 xitems = cfg.items;
34947 case 'Content': // ContentPanel (el, cfg)
34948 case 'Scroll': // ContentPanel (el, cfg)
34950 cfg.autoCreate = true;
34951 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34953 // var el = this.el.createChild();
34954 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34957 this.add(region, ret);
34961 case 'TreePanel': // our new panel!
34962 cfg.el = this.el.createChild();
34963 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34964 this.add(region, ret);
34969 // create a new Layout (which is a Border Layout...
34971 var clayout = cfg.layout;
34972 clayout.el = this.el.createChild();
34973 clayout.items = clayout.items || [];
34977 // replace this exitems with the clayout ones..
34978 xitems = clayout.items;
34980 // force background off if it's in center...
34981 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34982 cfg.background = false;
34984 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34987 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34988 //console.log('adding nested layout panel ' + cfg.toSource());
34989 this.add(region, ret);
34990 nb = {}; /// find first...
34995 // needs grid and region
34997 //var el = this.getRegion(region).el.createChild();
34999 *var el = this.el.createChild();
35000 // create the grid first...
35001 cfg.grid.container = el;
35002 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35005 if (region == 'center' && this.active ) {
35006 cfg.background = false;
35009 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35011 this.add(region, ret);
35013 if (cfg.background) {
35014 // render grid on panel activation (if panel background)
35015 ret.on('activate', function(gp) {
35016 if (!gp.grid.rendered) {
35017 // gp.grid.render(el);
35021 // cfg.grid.render(el);
35027 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35028 // it was the old xcomponent building that caused this before.
35029 // espeically if border is the top element in the tree.
35039 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35041 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35042 this.add(region, ret);
35046 throw "Can not add '" + cfg.xtype + "' to Border";
35052 this.beginUpdate();
35056 Roo.each(xitems, function(i) {
35057 region = nb && i.region ? i.region : false;
35059 var add = ret.addxtype(i);
35062 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35063 if (!i.background) {
35064 abn[region] = nb[region] ;
35071 // make the last non-background panel active..
35072 //if (nb) { Roo.log(abn); }
35075 for(var r in abn) {
35076 region = this.getRegion(r);
35078 // tried using nb[r], but it does not work..
35080 region.showPanel(abn[r]);
35091 factory : function(cfg)
35094 var validRegions = Roo.bootstrap.layout.Border.regions;
35096 var target = cfg.region;
35099 var r = Roo.bootstrap.layout;
35103 return new r.North(cfg);
35105 return new r.South(cfg);
35107 return new r.East(cfg);
35109 return new r.West(cfg);
35111 return new r.Center(cfg);
35113 throw 'Layout region "'+target+'" not supported.';
35120 * Ext JS Library 1.1.1
35121 * Copyright(c) 2006-2007, Ext JS, LLC.
35123 * Originally Released Under LGPL - original licence link has changed is not relivant.
35126 * <script type="text/javascript">
35130 * @class Roo.bootstrap.layout.Basic
35131 * @extends Roo.util.Observable
35132 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35133 * and does not have a titlebar, tabs or any other features. All it does is size and position
35134 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35135 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35136 * @cfg {string} region the region that it inhabits..
35137 * @cfg {bool} skipConfig skip config?
35141 Roo.bootstrap.layout.Basic = function(config){
35143 this.mgr = config.mgr;
35145 this.position = config.region;
35147 var skipConfig = config.skipConfig;
35151 * @scope Roo.BasicLayoutRegion
35155 * @event beforeremove
35156 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35157 * @param {Roo.LayoutRegion} this
35158 * @param {Roo.ContentPanel} panel The panel
35159 * @param {Object} e The cancel event object
35161 "beforeremove" : true,
35163 * @event invalidated
35164 * Fires when the layout for this region is changed.
35165 * @param {Roo.LayoutRegion} this
35167 "invalidated" : true,
35169 * @event visibilitychange
35170 * Fires when this region is shown or hidden
35171 * @param {Roo.LayoutRegion} this
35172 * @param {Boolean} visibility true or false
35174 "visibilitychange" : true,
35176 * @event paneladded
35177 * Fires when a panel is added.
35178 * @param {Roo.LayoutRegion} this
35179 * @param {Roo.ContentPanel} panel The panel
35181 "paneladded" : true,
35183 * @event panelremoved
35184 * Fires when a panel is removed.
35185 * @param {Roo.LayoutRegion} this
35186 * @param {Roo.ContentPanel} panel The panel
35188 "panelremoved" : true,
35190 * @event beforecollapse
35191 * Fires when this region before collapse.
35192 * @param {Roo.LayoutRegion} this
35194 "beforecollapse" : true,
35197 * Fires when this region is collapsed.
35198 * @param {Roo.LayoutRegion} this
35200 "collapsed" : true,
35203 * Fires when this region is expanded.
35204 * @param {Roo.LayoutRegion} this
35209 * Fires when this region is slid into view.
35210 * @param {Roo.LayoutRegion} this
35212 "slideshow" : true,
35215 * Fires when this region slides out of view.
35216 * @param {Roo.LayoutRegion} this
35218 "slidehide" : true,
35220 * @event panelactivated
35221 * Fires when a panel is activated.
35222 * @param {Roo.LayoutRegion} this
35223 * @param {Roo.ContentPanel} panel The activated panel
35225 "panelactivated" : true,
35228 * Fires when the user resizes this region.
35229 * @param {Roo.LayoutRegion} this
35230 * @param {Number} newSize The new size (width for east/west, height for north/south)
35234 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35235 this.panels = new Roo.util.MixedCollection();
35236 this.panels.getKey = this.getPanelId.createDelegate(this);
35238 this.activePanel = null;
35239 // ensure listeners are added...
35241 if (config.listeners || config.events) {
35242 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35243 listeners : config.listeners || {},
35244 events : config.events || {}
35248 if(skipConfig !== true){
35249 this.applyConfig(config);
35253 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35255 getPanelId : function(p){
35259 applyConfig : function(config){
35260 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35261 this.config = config;
35266 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35267 * the width, for horizontal (north, south) the height.
35268 * @param {Number} newSize The new width or height
35270 resizeTo : function(newSize){
35271 var el = this.el ? this.el :
35272 (this.activePanel ? this.activePanel.getEl() : null);
35274 switch(this.position){
35277 el.setWidth(newSize);
35278 this.fireEvent("resized", this, newSize);
35282 el.setHeight(newSize);
35283 this.fireEvent("resized", this, newSize);
35289 getBox : function(){
35290 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35293 getMargins : function(){
35294 return this.margins;
35297 updateBox : function(box){
35299 var el = this.activePanel.getEl();
35300 el.dom.style.left = box.x + "px";
35301 el.dom.style.top = box.y + "px";
35302 this.activePanel.setSize(box.width, box.height);
35306 * Returns the container element for this region.
35307 * @return {Roo.Element}
35309 getEl : function(){
35310 return this.activePanel;
35314 * Returns true if this region is currently visible.
35315 * @return {Boolean}
35317 isVisible : function(){
35318 return this.activePanel ? true : false;
35321 setActivePanel : function(panel){
35322 panel = this.getPanel(panel);
35323 if(this.activePanel && this.activePanel != panel){
35324 this.activePanel.setActiveState(false);
35325 this.activePanel.getEl().setLeftTop(-10000,-10000);
35327 this.activePanel = panel;
35328 panel.setActiveState(true);
35330 panel.setSize(this.box.width, this.box.height);
35332 this.fireEvent("panelactivated", this, panel);
35333 this.fireEvent("invalidated");
35337 * Show the specified panel.
35338 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35339 * @return {Roo.ContentPanel} The shown panel or null
35341 showPanel : function(panel){
35342 panel = this.getPanel(panel);
35344 this.setActivePanel(panel);
35350 * Get the active panel for this region.
35351 * @return {Roo.ContentPanel} The active panel or null
35353 getActivePanel : function(){
35354 return this.activePanel;
35358 * Add the passed ContentPanel(s)
35359 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35360 * @return {Roo.ContentPanel} The panel added (if only one was added)
35362 add : function(panel){
35363 if(arguments.length > 1){
35364 for(var i = 0, len = arguments.length; i < len; i++) {
35365 this.add(arguments[i]);
35369 if(this.hasPanel(panel)){
35370 this.showPanel(panel);
35373 var el = panel.getEl();
35374 if(el.dom.parentNode != this.mgr.el.dom){
35375 this.mgr.el.dom.appendChild(el.dom);
35377 if(panel.setRegion){
35378 panel.setRegion(this);
35380 this.panels.add(panel);
35381 el.setStyle("position", "absolute");
35382 if(!panel.background){
35383 this.setActivePanel(panel);
35384 if(this.config.initialSize && this.panels.getCount()==1){
35385 this.resizeTo(this.config.initialSize);
35388 this.fireEvent("paneladded", this, panel);
35393 * Returns true if the panel is in this region.
35394 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35395 * @return {Boolean}
35397 hasPanel : function(panel){
35398 if(typeof panel == "object"){ // must be panel obj
35399 panel = panel.getId();
35401 return this.getPanel(panel) ? true : false;
35405 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35406 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35407 * @param {Boolean} preservePanel Overrides the config preservePanel option
35408 * @return {Roo.ContentPanel} The panel that was removed
35410 remove : function(panel, preservePanel){
35411 panel = this.getPanel(panel);
35416 this.fireEvent("beforeremove", this, panel, e);
35417 if(e.cancel === true){
35420 var panelId = panel.getId();
35421 this.panels.removeKey(panelId);
35426 * Returns the panel specified or null if it's not in this region.
35427 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35428 * @return {Roo.ContentPanel}
35430 getPanel : function(id){
35431 if(typeof id == "object"){ // must be panel obj
35434 return this.panels.get(id);
35438 * Returns this regions position (north/south/east/west/center).
35441 getPosition: function(){
35442 return this.position;
35446 * Ext JS Library 1.1.1
35447 * Copyright(c) 2006-2007, Ext JS, LLC.
35449 * Originally Released Under LGPL - original licence link has changed is not relivant.
35452 * <script type="text/javascript">
35456 * @class Roo.bootstrap.layout.Region
35457 * @extends Roo.bootstrap.layout.Basic
35458 * This class represents a region in a layout manager.
35460 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35461 * @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})
35462 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35463 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35464 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35465 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35466 * @cfg {String} title The title for the region (overrides panel titles)
35467 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35468 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35469 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35470 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35471 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35472 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35473 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35474 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35475 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35476 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35478 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35479 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35480 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35481 * @cfg {Number} width For East/West panels
35482 * @cfg {Number} height For North/South panels
35483 * @cfg {Boolean} split To show the splitter
35484 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35486 * @cfg {string} cls Extra CSS classes to add to region
35488 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35489 * @cfg {string} region the region that it inhabits..
35492 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35493 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35495 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35496 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35497 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35499 Roo.bootstrap.layout.Region = function(config)
35501 this.applyConfig(config);
35503 var mgr = config.mgr;
35504 var pos = config.region;
35505 config.skipConfig = true;
35506 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35509 this.onRender(mgr.el);
35512 this.visible = true;
35513 this.collapsed = false;
35514 this.unrendered_panels = [];
35517 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35519 position: '', // set by wrapper (eg. north/south etc..)
35520 unrendered_panels : null, // unrendered panels.
35521 createBody : function(){
35522 /** This region's body element
35523 * @type Roo.Element */
35524 this.bodyEl = this.el.createChild({
35526 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35530 onRender: function(ctr, pos)
35532 var dh = Roo.DomHelper;
35533 /** This region's container element
35534 * @type Roo.Element */
35535 this.el = dh.append(ctr.dom, {
35537 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35539 /** This region's title element
35540 * @type Roo.Element */
35542 this.titleEl = dh.append(this.el.dom,
35545 unselectable: "on",
35546 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35548 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35549 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35552 this.titleEl.enableDisplayMode();
35553 /** This region's title text element
35554 * @type HTMLElement */
35555 this.titleTextEl = this.titleEl.dom.firstChild;
35556 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35558 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35559 this.closeBtn.enableDisplayMode();
35560 this.closeBtn.on("click", this.closeClicked, this);
35561 this.closeBtn.hide();
35563 this.createBody(this.config);
35564 if(this.config.hideWhenEmpty){
35566 this.on("paneladded", this.validateVisibility, this);
35567 this.on("panelremoved", this.validateVisibility, this);
35569 if(this.autoScroll){
35570 this.bodyEl.setStyle("overflow", "auto");
35572 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35574 //if(c.titlebar !== false){
35575 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35576 this.titleEl.hide();
35578 this.titleEl.show();
35579 if(this.config.title){
35580 this.titleTextEl.innerHTML = this.config.title;
35584 if(this.config.collapsed){
35585 this.collapse(true);
35587 if(this.config.hidden){
35591 if (this.unrendered_panels && this.unrendered_panels.length) {
35592 for (var i =0;i< this.unrendered_panels.length; i++) {
35593 this.add(this.unrendered_panels[i]);
35595 this.unrendered_panels = null;
35601 applyConfig : function(c)
35604 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35605 var dh = Roo.DomHelper;
35606 if(c.titlebar !== false){
35607 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35608 this.collapseBtn.on("click", this.collapse, this);
35609 this.collapseBtn.enableDisplayMode();
35611 if(c.showPin === true || this.showPin){
35612 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35613 this.stickBtn.enableDisplayMode();
35614 this.stickBtn.on("click", this.expand, this);
35615 this.stickBtn.hide();
35620 /** This region's collapsed element
35621 * @type Roo.Element */
35624 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35625 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35628 if(c.floatable !== false){
35629 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35630 this.collapsedEl.on("click", this.collapseClick, this);
35633 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35634 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35635 id: "message", unselectable: "on", style:{"float":"left"}});
35636 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35638 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35639 this.expandBtn.on("click", this.expand, this);
35643 if(this.collapseBtn){
35644 this.collapseBtn.setVisible(c.collapsible == true);
35647 this.cmargins = c.cmargins || this.cmargins ||
35648 (this.position == "west" || this.position == "east" ?
35649 {top: 0, left: 2, right:2, bottom: 0} :
35650 {top: 2, left: 0, right:0, bottom: 2});
35652 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35655 this.bottomTabs = c.tabPosition != "top";
35657 this.autoScroll = c.autoScroll || false;
35662 this.duration = c.duration || .30;
35663 this.slideDuration = c.slideDuration || .45;
35668 * Returns true if this region is currently visible.
35669 * @return {Boolean}
35671 isVisible : function(){
35672 return this.visible;
35676 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35677 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35679 //setCollapsedTitle : function(title){
35680 // title = title || " ";
35681 // if(this.collapsedTitleTextEl){
35682 // this.collapsedTitleTextEl.innerHTML = title;
35686 getBox : function(){
35688 // if(!this.collapsed){
35689 b = this.el.getBox(false, true);
35691 // b = this.collapsedEl.getBox(false, true);
35696 getMargins : function(){
35697 return this.margins;
35698 //return this.collapsed ? this.cmargins : this.margins;
35701 highlight : function(){
35702 this.el.addClass("x-layout-panel-dragover");
35705 unhighlight : function(){
35706 this.el.removeClass("x-layout-panel-dragover");
35709 updateBox : function(box)
35711 if (!this.bodyEl) {
35712 return; // not rendered yet..
35716 if(!this.collapsed){
35717 this.el.dom.style.left = box.x + "px";
35718 this.el.dom.style.top = box.y + "px";
35719 this.updateBody(box.width, box.height);
35721 this.collapsedEl.dom.style.left = box.x + "px";
35722 this.collapsedEl.dom.style.top = box.y + "px";
35723 this.collapsedEl.setSize(box.width, box.height);
35726 this.tabs.autoSizeTabs();
35730 updateBody : function(w, h)
35733 this.el.setWidth(w);
35734 w -= this.el.getBorderWidth("rl");
35735 if(this.config.adjustments){
35736 w += this.config.adjustments[0];
35739 if(h !== null && h > 0){
35740 this.el.setHeight(h);
35741 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35742 h -= this.el.getBorderWidth("tb");
35743 if(this.config.adjustments){
35744 h += this.config.adjustments[1];
35746 this.bodyEl.setHeight(h);
35748 h = this.tabs.syncHeight(h);
35751 if(this.panelSize){
35752 w = w !== null ? w : this.panelSize.width;
35753 h = h !== null ? h : this.panelSize.height;
35755 if(this.activePanel){
35756 var el = this.activePanel.getEl();
35757 w = w !== null ? w : el.getWidth();
35758 h = h !== null ? h : el.getHeight();
35759 this.panelSize = {width: w, height: h};
35760 this.activePanel.setSize(w, h);
35762 if(Roo.isIE && this.tabs){
35763 this.tabs.el.repaint();
35768 * Returns the container element for this region.
35769 * @return {Roo.Element}
35771 getEl : function(){
35776 * Hides this region.
35779 //if(!this.collapsed){
35780 this.el.dom.style.left = "-2000px";
35783 // this.collapsedEl.dom.style.left = "-2000px";
35784 // this.collapsedEl.hide();
35786 this.visible = false;
35787 this.fireEvent("visibilitychange", this, false);
35791 * Shows this region if it was previously hidden.
35794 //if(!this.collapsed){
35797 // this.collapsedEl.show();
35799 this.visible = true;
35800 this.fireEvent("visibilitychange", this, true);
35803 closeClicked : function(){
35804 if(this.activePanel){
35805 this.remove(this.activePanel);
35809 collapseClick : function(e){
35811 e.stopPropagation();
35814 e.stopPropagation();
35820 * Collapses this region.
35821 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35824 collapse : function(skipAnim, skipCheck = false){
35825 if(this.collapsed) {
35829 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35831 this.collapsed = true;
35833 this.split.el.hide();
35835 if(this.config.animate && skipAnim !== true){
35836 this.fireEvent("invalidated", this);
35837 this.animateCollapse();
35839 this.el.setLocation(-20000,-20000);
35841 this.collapsedEl.show();
35842 this.fireEvent("collapsed", this);
35843 this.fireEvent("invalidated", this);
35849 animateCollapse : function(){
35854 * Expands this region if it was previously collapsed.
35855 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35856 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35859 expand : function(e, skipAnim){
35861 e.stopPropagation();
35863 if(!this.collapsed || this.el.hasActiveFx()) {
35867 this.afterSlideIn();
35870 this.collapsed = false;
35871 if(this.config.animate && skipAnim !== true){
35872 this.animateExpand();
35876 this.split.el.show();
35878 this.collapsedEl.setLocation(-2000,-2000);
35879 this.collapsedEl.hide();
35880 this.fireEvent("invalidated", this);
35881 this.fireEvent("expanded", this);
35885 animateExpand : function(){
35889 initTabs : function()
35891 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35893 var ts = new Roo.bootstrap.panel.Tabs({
35894 el: this.bodyEl.dom,
35895 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35896 disableTooltips: this.config.disableTabTips,
35897 toolbar : this.config.toolbar
35900 if(this.config.hideTabs){
35901 ts.stripWrap.setDisplayed(false);
35904 ts.resizeTabs = this.config.resizeTabs === true;
35905 ts.minTabWidth = this.config.minTabWidth || 40;
35906 ts.maxTabWidth = this.config.maxTabWidth || 250;
35907 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35908 ts.monitorResize = false;
35909 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35910 ts.bodyEl.addClass('roo-layout-tabs-body');
35911 this.panels.each(this.initPanelAsTab, this);
35914 initPanelAsTab : function(panel){
35915 var ti = this.tabs.addTab(
35919 this.config.closeOnTab && panel.isClosable(),
35922 if(panel.tabTip !== undefined){
35923 ti.setTooltip(panel.tabTip);
35925 ti.on("activate", function(){
35926 this.setActivePanel(panel);
35929 if(this.config.closeOnTab){
35930 ti.on("beforeclose", function(t, e){
35932 this.remove(panel);
35936 panel.tabItem = ti;
35941 updatePanelTitle : function(panel, title)
35943 if(this.activePanel == panel){
35944 this.updateTitle(title);
35947 var ti = this.tabs.getTab(panel.getEl().id);
35949 if(panel.tabTip !== undefined){
35950 ti.setTooltip(panel.tabTip);
35955 updateTitle : function(title){
35956 if(this.titleTextEl && !this.config.title){
35957 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35961 setActivePanel : function(panel)
35963 panel = this.getPanel(panel);
35964 if(this.activePanel && this.activePanel != panel){
35965 if(this.activePanel.setActiveState(false) === false){
35969 this.activePanel = panel;
35970 panel.setActiveState(true);
35971 if(this.panelSize){
35972 panel.setSize(this.panelSize.width, this.panelSize.height);
35975 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35977 this.updateTitle(panel.getTitle());
35979 this.fireEvent("invalidated", this);
35981 this.fireEvent("panelactivated", this, panel);
35985 * Shows the specified panel.
35986 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35987 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35989 showPanel : function(panel)
35991 panel = this.getPanel(panel);
35994 var tab = this.tabs.getTab(panel.getEl().id);
35995 if(tab.isHidden()){
35996 this.tabs.unhideTab(tab.id);
36000 this.setActivePanel(panel);
36007 * Get the active panel for this region.
36008 * @return {Roo.ContentPanel} The active panel or null
36010 getActivePanel : function(){
36011 return this.activePanel;
36014 validateVisibility : function(){
36015 if(this.panels.getCount() < 1){
36016 this.updateTitle(" ");
36017 this.closeBtn.hide();
36020 if(!this.isVisible()){
36027 * Adds the passed ContentPanel(s) to this region.
36028 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36029 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36031 add : function(panel)
36033 if(arguments.length > 1){
36034 for(var i = 0, len = arguments.length; i < len; i++) {
36035 this.add(arguments[i]);
36040 // if we have not been rendered yet, then we can not really do much of this..
36041 if (!this.bodyEl) {
36042 this.unrendered_panels.push(panel);
36049 if(this.hasPanel(panel)){
36050 this.showPanel(panel);
36053 panel.setRegion(this);
36054 this.panels.add(panel);
36055 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36056 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36057 // and hide them... ???
36058 this.bodyEl.dom.appendChild(panel.getEl().dom);
36059 if(panel.background !== true){
36060 this.setActivePanel(panel);
36062 this.fireEvent("paneladded", this, panel);
36069 this.initPanelAsTab(panel);
36073 if(panel.background !== true){
36074 this.tabs.activate(panel.getEl().id);
36076 this.fireEvent("paneladded", this, panel);
36081 * Hides the tab for the specified panel.
36082 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36084 hidePanel : function(panel){
36085 if(this.tabs && (panel = this.getPanel(panel))){
36086 this.tabs.hideTab(panel.getEl().id);
36091 * Unhides the tab for a previously hidden panel.
36092 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36094 unhidePanel : function(panel){
36095 if(this.tabs && (panel = this.getPanel(panel))){
36096 this.tabs.unhideTab(panel.getEl().id);
36100 clearPanels : function(){
36101 while(this.panels.getCount() > 0){
36102 this.remove(this.panels.first());
36107 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36108 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36109 * @param {Boolean} preservePanel Overrides the config preservePanel option
36110 * @return {Roo.ContentPanel} The panel that was removed
36112 remove : function(panel, preservePanel)
36114 panel = this.getPanel(panel);
36119 this.fireEvent("beforeremove", this, panel, e);
36120 if(e.cancel === true){
36123 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36124 var panelId = panel.getId();
36125 this.panels.removeKey(panelId);
36127 document.body.appendChild(panel.getEl().dom);
36130 this.tabs.removeTab(panel.getEl().id);
36131 }else if (!preservePanel){
36132 this.bodyEl.dom.removeChild(panel.getEl().dom);
36134 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36135 var p = this.panels.first();
36136 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36137 tempEl.appendChild(p.getEl().dom);
36138 this.bodyEl.update("");
36139 this.bodyEl.dom.appendChild(p.getEl().dom);
36141 this.updateTitle(p.getTitle());
36143 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36144 this.setActivePanel(p);
36146 panel.setRegion(null);
36147 if(this.activePanel == panel){
36148 this.activePanel = null;
36150 if(this.config.autoDestroy !== false && preservePanel !== true){
36151 try{panel.destroy();}catch(e){}
36153 this.fireEvent("panelremoved", this, panel);
36158 * Returns the TabPanel component used by this region
36159 * @return {Roo.TabPanel}
36161 getTabs : function(){
36165 createTool : function(parentEl, className){
36166 var btn = Roo.DomHelper.append(parentEl, {
36168 cls: "x-layout-tools-button",
36171 cls: "roo-layout-tools-button-inner " + className,
36175 btn.addClassOnOver("roo-layout-tools-button-over");
36180 * Ext JS Library 1.1.1
36181 * Copyright(c) 2006-2007, Ext JS, LLC.
36183 * Originally Released Under LGPL - original licence link has changed is not relivant.
36186 * <script type="text/javascript">
36192 * @class Roo.SplitLayoutRegion
36193 * @extends Roo.LayoutRegion
36194 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36196 Roo.bootstrap.layout.Split = function(config){
36197 this.cursor = config.cursor;
36198 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36201 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36203 splitTip : "Drag to resize.",
36204 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36205 useSplitTips : false,
36207 applyConfig : function(config){
36208 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36211 onRender : function(ctr,pos) {
36213 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36214 if(!this.config.split){
36219 var splitEl = Roo.DomHelper.append(ctr.dom, {
36221 id: this.el.id + "-split",
36222 cls: "roo-layout-split roo-layout-split-"+this.position,
36225 /** The SplitBar for this region
36226 * @type Roo.SplitBar */
36227 // does not exist yet...
36228 Roo.log([this.position, this.orientation]);
36230 this.split = new Roo.bootstrap.SplitBar({
36231 dragElement : splitEl,
36232 resizingElement: this.el,
36233 orientation : this.orientation
36236 this.split.on("moved", this.onSplitMove, this);
36237 this.split.useShim = this.config.useShim === true;
36238 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36239 if(this.useSplitTips){
36240 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36242 //if(config.collapsible){
36243 // this.split.el.on("dblclick", this.collapse, this);
36246 if(typeof this.config.minSize != "undefined"){
36247 this.split.minSize = this.config.minSize;
36249 if(typeof this.config.maxSize != "undefined"){
36250 this.split.maxSize = this.config.maxSize;
36252 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36253 this.hideSplitter();
36258 getHMaxSize : function(){
36259 var cmax = this.config.maxSize || 10000;
36260 var center = this.mgr.getRegion("center");
36261 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36264 getVMaxSize : function(){
36265 var cmax = this.config.maxSize || 10000;
36266 var center = this.mgr.getRegion("center");
36267 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36270 onSplitMove : function(split, newSize){
36271 this.fireEvent("resized", this, newSize);
36275 * Returns the {@link Roo.SplitBar} for this region.
36276 * @return {Roo.SplitBar}
36278 getSplitBar : function(){
36283 this.hideSplitter();
36284 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36287 hideSplitter : function(){
36289 this.split.el.setLocation(-2000,-2000);
36290 this.split.el.hide();
36296 this.split.el.show();
36298 Roo.bootstrap.layout.Split.superclass.show.call(this);
36301 beforeSlide: function(){
36302 if(Roo.isGecko){// firefox overflow auto bug workaround
36303 this.bodyEl.clip();
36305 this.tabs.bodyEl.clip();
36307 if(this.activePanel){
36308 this.activePanel.getEl().clip();
36310 if(this.activePanel.beforeSlide){
36311 this.activePanel.beforeSlide();
36317 afterSlide : function(){
36318 if(Roo.isGecko){// firefox overflow auto bug workaround
36319 this.bodyEl.unclip();
36321 this.tabs.bodyEl.unclip();
36323 if(this.activePanel){
36324 this.activePanel.getEl().unclip();
36325 if(this.activePanel.afterSlide){
36326 this.activePanel.afterSlide();
36332 initAutoHide : function(){
36333 if(this.autoHide !== false){
36334 if(!this.autoHideHd){
36335 var st = new Roo.util.DelayedTask(this.slideIn, this);
36336 this.autoHideHd = {
36337 "mouseout": function(e){
36338 if(!e.within(this.el, true)){
36342 "mouseover" : function(e){
36348 this.el.on(this.autoHideHd);
36352 clearAutoHide : function(){
36353 if(this.autoHide !== false){
36354 this.el.un("mouseout", this.autoHideHd.mouseout);
36355 this.el.un("mouseover", this.autoHideHd.mouseover);
36359 clearMonitor : function(){
36360 Roo.get(document).un("click", this.slideInIf, this);
36363 // these names are backwards but not changed for compat
36364 slideOut : function(){
36365 if(this.isSlid || this.el.hasActiveFx()){
36368 this.isSlid = true;
36369 if(this.collapseBtn){
36370 this.collapseBtn.hide();
36372 this.closeBtnState = this.closeBtn.getStyle('display');
36373 this.closeBtn.hide();
36375 this.stickBtn.show();
36378 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36379 this.beforeSlide();
36380 this.el.setStyle("z-index", 10001);
36381 this.el.slideIn(this.getSlideAnchor(), {
36382 callback: function(){
36384 this.initAutoHide();
36385 Roo.get(document).on("click", this.slideInIf, this);
36386 this.fireEvent("slideshow", this);
36393 afterSlideIn : function(){
36394 this.clearAutoHide();
36395 this.isSlid = false;
36396 this.clearMonitor();
36397 this.el.setStyle("z-index", "");
36398 if(this.collapseBtn){
36399 this.collapseBtn.show();
36401 this.closeBtn.setStyle('display', this.closeBtnState);
36403 this.stickBtn.hide();
36405 this.fireEvent("slidehide", this);
36408 slideIn : function(cb){
36409 if(!this.isSlid || this.el.hasActiveFx()){
36413 this.isSlid = false;
36414 this.beforeSlide();
36415 this.el.slideOut(this.getSlideAnchor(), {
36416 callback: function(){
36417 this.el.setLeftTop(-10000, -10000);
36419 this.afterSlideIn();
36427 slideInIf : function(e){
36428 if(!e.within(this.el)){
36433 animateCollapse : function(){
36434 this.beforeSlide();
36435 this.el.setStyle("z-index", 20000);
36436 var anchor = this.getSlideAnchor();
36437 this.el.slideOut(anchor, {
36438 callback : function(){
36439 this.el.setStyle("z-index", "");
36440 this.collapsedEl.slideIn(anchor, {duration:.3});
36442 this.el.setLocation(-10000,-10000);
36444 this.fireEvent("collapsed", this);
36451 animateExpand : function(){
36452 this.beforeSlide();
36453 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36454 this.el.setStyle("z-index", 20000);
36455 this.collapsedEl.hide({
36458 this.el.slideIn(this.getSlideAnchor(), {
36459 callback : function(){
36460 this.el.setStyle("z-index", "");
36463 this.split.el.show();
36465 this.fireEvent("invalidated", this);
36466 this.fireEvent("expanded", this);
36494 getAnchor : function(){
36495 return this.anchors[this.position];
36498 getCollapseAnchor : function(){
36499 return this.canchors[this.position];
36502 getSlideAnchor : function(){
36503 return this.sanchors[this.position];
36506 getAlignAdj : function(){
36507 var cm = this.cmargins;
36508 switch(this.position){
36524 getExpandAdj : function(){
36525 var c = this.collapsedEl, cm = this.cmargins;
36526 switch(this.position){
36528 return [-(cm.right+c.getWidth()+cm.left), 0];
36531 return [cm.right+c.getWidth()+cm.left, 0];
36534 return [0, -(cm.top+cm.bottom+c.getHeight())];
36537 return [0, cm.top+cm.bottom+c.getHeight()];
36543 * Ext JS Library 1.1.1
36544 * Copyright(c) 2006-2007, Ext JS, LLC.
36546 * Originally Released Under LGPL - original licence link has changed is not relivant.
36549 * <script type="text/javascript">
36552 * These classes are private internal classes
36554 Roo.bootstrap.layout.Center = function(config){
36555 config.region = "center";
36556 Roo.bootstrap.layout.Region.call(this, config);
36557 this.visible = true;
36558 this.minWidth = config.minWidth || 20;
36559 this.minHeight = config.minHeight || 20;
36562 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36564 // center panel can't be hidden
36568 // center panel can't be hidden
36571 getMinWidth: function(){
36572 return this.minWidth;
36575 getMinHeight: function(){
36576 return this.minHeight;
36589 Roo.bootstrap.layout.North = function(config)
36591 config.region = 'north';
36592 config.cursor = 'n-resize';
36594 Roo.bootstrap.layout.Split.call(this, config);
36598 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36599 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36600 this.split.el.addClass("roo-layout-split-v");
36602 var size = config.initialSize || config.height;
36603 if(typeof size != "undefined"){
36604 this.el.setHeight(size);
36607 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36609 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36613 getBox : function(){
36614 if(this.collapsed){
36615 return this.collapsedEl.getBox();
36617 var box = this.el.getBox();
36619 box.height += this.split.el.getHeight();
36624 updateBox : function(box){
36625 if(this.split && !this.collapsed){
36626 box.height -= this.split.el.getHeight();
36627 this.split.el.setLeft(box.x);
36628 this.split.el.setTop(box.y+box.height);
36629 this.split.el.setWidth(box.width);
36631 if(this.collapsed){
36632 this.updateBody(box.width, null);
36634 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36642 Roo.bootstrap.layout.South = function(config){
36643 config.region = 'south';
36644 config.cursor = 's-resize';
36645 Roo.bootstrap.layout.Split.call(this, config);
36647 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36648 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36649 this.split.el.addClass("roo-layout-split-v");
36651 var size = config.initialSize || config.height;
36652 if(typeof size != "undefined"){
36653 this.el.setHeight(size);
36657 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36658 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36659 getBox : function(){
36660 if(this.collapsed){
36661 return this.collapsedEl.getBox();
36663 var box = this.el.getBox();
36665 var sh = this.split.el.getHeight();
36672 updateBox : function(box){
36673 if(this.split && !this.collapsed){
36674 var sh = this.split.el.getHeight();
36677 this.split.el.setLeft(box.x);
36678 this.split.el.setTop(box.y-sh);
36679 this.split.el.setWidth(box.width);
36681 if(this.collapsed){
36682 this.updateBody(box.width, null);
36684 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36688 Roo.bootstrap.layout.East = function(config){
36689 config.region = "east";
36690 config.cursor = "e-resize";
36691 Roo.bootstrap.layout.Split.call(this, config);
36693 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36694 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36695 this.split.el.addClass("roo-layout-split-h");
36697 var size = config.initialSize || config.width;
36698 if(typeof size != "undefined"){
36699 this.el.setWidth(size);
36702 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36703 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36704 getBox : function(){
36705 if(this.collapsed){
36706 return this.collapsedEl.getBox();
36708 var box = this.el.getBox();
36710 var sw = this.split.el.getWidth();
36717 updateBox : function(box){
36718 if(this.split && !this.collapsed){
36719 var sw = this.split.el.getWidth();
36721 this.split.el.setLeft(box.x);
36722 this.split.el.setTop(box.y);
36723 this.split.el.setHeight(box.height);
36726 if(this.collapsed){
36727 this.updateBody(null, box.height);
36729 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36733 Roo.bootstrap.layout.West = function(config){
36734 config.region = "west";
36735 config.cursor = "w-resize";
36737 Roo.bootstrap.layout.Split.call(this, config);
36739 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36740 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36741 this.split.el.addClass("roo-layout-split-h");
36745 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36746 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36748 onRender: function(ctr, pos)
36750 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36751 var size = this.config.initialSize || this.config.width;
36752 if(typeof size != "undefined"){
36753 this.el.setWidth(size);
36757 getBox : function(){
36758 if(this.collapsed){
36759 return this.collapsedEl.getBox();
36761 var box = this.el.getBox();
36763 box.width += this.split.el.getWidth();
36768 updateBox : function(box){
36769 if(this.split && !this.collapsed){
36770 var sw = this.split.el.getWidth();
36772 this.split.el.setLeft(box.x+box.width);
36773 this.split.el.setTop(box.y);
36774 this.split.el.setHeight(box.height);
36776 if(this.collapsed){
36777 this.updateBody(null, box.height);
36779 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36782 Roo.namespace("Roo.bootstrap.panel");/*
36784 * Ext JS Library 1.1.1
36785 * Copyright(c) 2006-2007, Ext JS, LLC.
36787 * Originally Released Under LGPL - original licence link has changed is not relivant.
36790 * <script type="text/javascript">
36793 * @class Roo.ContentPanel
36794 * @extends Roo.util.Observable
36795 * A basic ContentPanel element.
36796 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36797 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36798 * @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
36799 * @cfg {Boolean} closable True if the panel can be closed/removed
36800 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36801 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36802 * @cfg {Toolbar} toolbar A toolbar for this panel
36803 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36804 * @cfg {String} title The title for this panel
36805 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36806 * @cfg {String} url Calls {@link #setUrl} with this value
36807 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36808 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36809 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36810 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36811 * @cfg {Boolean} badges render the badges
36814 * Create a new ContentPanel.
36815 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36816 * @param {String/Object} config A string to set only the title or a config object
36817 * @param {String} content (optional) Set the HTML content for this panel
36818 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36820 Roo.bootstrap.panel.Content = function( config){
36822 this.tpl = config.tpl || false;
36824 var el = config.el;
36825 var content = config.content;
36827 if(config.autoCreate){ // xtype is available if this is called from factory
36830 this.el = Roo.get(el);
36831 if(!this.el && config && config.autoCreate){
36832 if(typeof config.autoCreate == "object"){
36833 if(!config.autoCreate.id){
36834 config.autoCreate.id = config.id||el;
36836 this.el = Roo.DomHelper.append(document.body,
36837 config.autoCreate, true);
36839 var elcfg = { tag: "div",
36840 cls: "roo-layout-inactive-content",
36844 elcfg.html = config.html;
36848 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36851 this.closable = false;
36852 this.loaded = false;
36853 this.active = false;
36856 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36858 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36860 this.wrapEl = this.el; //this.el.wrap();
36862 if (config.toolbar.items) {
36863 ti = config.toolbar.items ;
36864 delete config.toolbar.items ;
36868 this.toolbar.render(this.wrapEl, 'before');
36869 for(var i =0;i < ti.length;i++) {
36870 // Roo.log(['add child', items[i]]);
36871 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36873 this.toolbar.items = nitems;
36874 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36875 delete config.toolbar;
36879 // xtype created footer. - not sure if will work as we normally have to render first..
36880 if (this.footer && !this.footer.el && this.footer.xtype) {
36881 if (!this.wrapEl) {
36882 this.wrapEl = this.el.wrap();
36885 this.footer.container = this.wrapEl.createChild();
36887 this.footer = Roo.factory(this.footer, Roo);
36892 if(typeof config == "string"){
36893 this.title = config;
36895 Roo.apply(this, config);
36899 this.resizeEl = Roo.get(this.resizeEl, true);
36901 this.resizeEl = this.el;
36903 // handle view.xtype
36911 * Fires when this panel is activated.
36912 * @param {Roo.ContentPanel} this
36916 * @event deactivate
36917 * Fires when this panel is activated.
36918 * @param {Roo.ContentPanel} this
36920 "deactivate" : true,
36924 * Fires when this panel is resized if fitToFrame is true.
36925 * @param {Roo.ContentPanel} this
36926 * @param {Number} width The width after any component adjustments
36927 * @param {Number} height The height after any component adjustments
36933 * Fires when this tab is created
36934 * @param {Roo.ContentPanel} this
36945 if(this.autoScroll){
36946 this.resizeEl.setStyle("overflow", "auto");
36948 // fix randome scrolling
36949 //this.el.on('scroll', function() {
36950 // Roo.log('fix random scolling');
36951 // this.scrollTo('top',0);
36954 content = content || this.content;
36956 this.setContent(content);
36958 if(config && config.url){
36959 this.setUrl(this.url, this.params, this.loadOnce);
36964 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36966 if (this.view && typeof(this.view.xtype) != 'undefined') {
36967 this.view.el = this.el.appendChild(document.createElement("div"));
36968 this.view = Roo.factory(this.view);
36969 this.view.render && this.view.render(false, '');
36973 this.fireEvent('render', this);
36976 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36980 setRegion : function(region){
36981 this.region = region;
36982 this.setActiveClass(region && !this.background);
36986 setActiveClass: function(state)
36989 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36990 this.el.setStyle('position','relative');
36992 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36993 this.el.setStyle('position', 'absolute');
36998 * Returns the toolbar for this Panel if one was configured.
36999 * @return {Roo.Toolbar}
37001 getToolbar : function(){
37002 return this.toolbar;
37005 setActiveState : function(active)
37007 this.active = active;
37008 this.setActiveClass(active);
37010 if(this.fireEvent("deactivate", this) === false){
37015 this.fireEvent("activate", this);
37019 * Updates this panel's element
37020 * @param {String} content The new content
37021 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37023 setContent : function(content, loadScripts){
37024 this.el.update(content, loadScripts);
37027 ignoreResize : function(w, h){
37028 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37031 this.lastSize = {width: w, height: h};
37036 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37037 * @return {Roo.UpdateManager} The UpdateManager
37039 getUpdateManager : function(){
37040 return this.el.getUpdateManager();
37043 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37044 * @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:
37047 url: "your-url.php",
37048 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37049 callback: yourFunction,
37050 scope: yourObject, //(optional scope)
37053 text: "Loading...",
37058 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37059 * 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.
37060 * @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}
37061 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37062 * @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.
37063 * @return {Roo.ContentPanel} this
37066 var um = this.el.getUpdateManager();
37067 um.update.apply(um, arguments);
37073 * 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.
37074 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37075 * @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)
37076 * @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)
37077 * @return {Roo.UpdateManager} The UpdateManager
37079 setUrl : function(url, params, loadOnce){
37080 if(this.refreshDelegate){
37081 this.removeListener("activate", this.refreshDelegate);
37083 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37084 this.on("activate", this.refreshDelegate);
37085 return this.el.getUpdateManager();
37088 _handleRefresh : function(url, params, loadOnce){
37089 if(!loadOnce || !this.loaded){
37090 var updater = this.el.getUpdateManager();
37091 updater.update(url, params, this._setLoaded.createDelegate(this));
37095 _setLoaded : function(){
37096 this.loaded = true;
37100 * Returns this panel's id
37103 getId : function(){
37108 * Returns this panel's element - used by regiosn to add.
37109 * @return {Roo.Element}
37111 getEl : function(){
37112 return this.wrapEl || this.el;
37117 adjustForComponents : function(width, height)
37119 //Roo.log('adjustForComponents ');
37120 if(this.resizeEl != this.el){
37121 width -= this.el.getFrameWidth('lr');
37122 height -= this.el.getFrameWidth('tb');
37125 var te = this.toolbar.getEl();
37126 te.setWidth(width);
37127 height -= te.getHeight();
37130 var te = this.footer.getEl();
37131 te.setWidth(width);
37132 height -= te.getHeight();
37136 if(this.adjustments){
37137 width += this.adjustments[0];
37138 height += this.adjustments[1];
37140 return {"width": width, "height": height};
37143 setSize : function(width, height){
37144 if(this.fitToFrame && !this.ignoreResize(width, height)){
37145 if(this.fitContainer && this.resizeEl != this.el){
37146 this.el.setSize(width, height);
37148 var size = this.adjustForComponents(width, height);
37149 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37150 this.fireEvent('resize', this, size.width, size.height);
37155 * Returns this panel's title
37158 getTitle : function(){
37160 if (typeof(this.title) != 'object') {
37165 for (var k in this.title) {
37166 if (!this.title.hasOwnProperty(k)) {
37170 if (k.indexOf('-') >= 0) {
37171 var s = k.split('-');
37172 for (var i = 0; i<s.length; i++) {
37173 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37176 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37183 * Set this panel's title
37184 * @param {String} title
37186 setTitle : function(title){
37187 this.title = title;
37189 this.region.updatePanelTitle(this, title);
37194 * Returns true is this panel was configured to be closable
37195 * @return {Boolean}
37197 isClosable : function(){
37198 return this.closable;
37201 beforeSlide : function(){
37203 this.resizeEl.clip();
37206 afterSlide : function(){
37208 this.resizeEl.unclip();
37212 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37213 * Will fail silently if the {@link #setUrl} method has not been called.
37214 * This does not activate the panel, just updates its content.
37216 refresh : function(){
37217 if(this.refreshDelegate){
37218 this.loaded = false;
37219 this.refreshDelegate();
37224 * Destroys this panel
37226 destroy : function(){
37227 this.el.removeAllListeners();
37228 var tempEl = document.createElement("span");
37229 tempEl.appendChild(this.el.dom);
37230 tempEl.innerHTML = "";
37236 * form - if the content panel contains a form - this is a reference to it.
37237 * @type {Roo.form.Form}
37241 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37242 * This contains a reference to it.
37248 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37258 * @param {Object} cfg Xtype definition of item to add.
37262 getChildContainer: function () {
37263 return this.getEl();
37268 var ret = new Roo.factory(cfg);
37273 if (cfg.xtype.match(/^Form$/)) {
37276 //if (this.footer) {
37277 // el = this.footer.container.insertSibling(false, 'before');
37279 el = this.el.createChild();
37282 this.form = new Roo.form.Form(cfg);
37285 if ( this.form.allItems.length) {
37286 this.form.render(el.dom);
37290 // should only have one of theses..
37291 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37292 // views.. should not be just added - used named prop 'view''
37294 cfg.el = this.el.appendChild(document.createElement("div"));
37297 var ret = new Roo.factory(cfg);
37299 ret.render && ret.render(false, ''); // render blank..
37309 * @class Roo.bootstrap.panel.Grid
37310 * @extends Roo.bootstrap.panel.Content
37312 * Create a new GridPanel.
37313 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37314 * @param {Object} config A the config object
37320 Roo.bootstrap.panel.Grid = function(config)
37324 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37325 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37327 config.el = this.wrapper;
37328 //this.el = this.wrapper;
37330 if (config.container) {
37331 // ctor'ed from a Border/panel.grid
37334 this.wrapper.setStyle("overflow", "hidden");
37335 this.wrapper.addClass('roo-grid-container');
37340 if(config.toolbar){
37341 var tool_el = this.wrapper.createChild();
37342 this.toolbar = Roo.factory(config.toolbar);
37344 if (config.toolbar.items) {
37345 ti = config.toolbar.items ;
37346 delete config.toolbar.items ;
37350 this.toolbar.render(tool_el);
37351 for(var i =0;i < ti.length;i++) {
37352 // Roo.log(['add child', items[i]]);
37353 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37355 this.toolbar.items = nitems;
37357 delete config.toolbar;
37360 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37361 config.grid.scrollBody = true;;
37362 config.grid.monitorWindowResize = false; // turn off autosizing
37363 config.grid.autoHeight = false;
37364 config.grid.autoWidth = false;
37366 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37368 if (config.background) {
37369 // render grid on panel activation (if panel background)
37370 this.on('activate', function(gp) {
37371 if (!gp.grid.rendered) {
37372 gp.grid.render(this.wrapper);
37373 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37378 this.grid.render(this.wrapper);
37379 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37382 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37383 // ??? needed ??? config.el = this.wrapper;
37388 // xtype created footer. - not sure if will work as we normally have to render first..
37389 if (this.footer && !this.footer.el && this.footer.xtype) {
37391 var ctr = this.grid.getView().getFooterPanel(true);
37392 this.footer.dataSource = this.grid.dataSource;
37393 this.footer = Roo.factory(this.footer, Roo);
37394 this.footer.render(ctr);
37404 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37405 getId : function(){
37406 return this.grid.id;
37410 * Returns the grid for this panel
37411 * @return {Roo.bootstrap.Table}
37413 getGrid : function(){
37417 setSize : function(width, height){
37418 if(!this.ignoreResize(width, height)){
37419 var grid = this.grid;
37420 var size = this.adjustForComponents(width, height);
37421 var gridel = grid.getGridEl();
37422 gridel.setSize(size.width, size.height);
37424 var thd = grid.getGridEl().select('thead',true).first();
37425 var tbd = grid.getGridEl().select('tbody', true).first();
37427 tbd.setSize(width, height - thd.getHeight());
37436 beforeSlide : function(){
37437 this.grid.getView().scroller.clip();
37440 afterSlide : function(){
37441 this.grid.getView().scroller.unclip();
37444 destroy : function(){
37445 this.grid.destroy();
37447 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37452 * @class Roo.bootstrap.panel.Nest
37453 * @extends Roo.bootstrap.panel.Content
37455 * Create a new Panel, that can contain a layout.Border.
37458 * @param {Roo.BorderLayout} layout The layout for this panel
37459 * @param {String/Object} config A string to set only the title or a config object
37461 Roo.bootstrap.panel.Nest = function(config)
37463 // construct with only one argument..
37464 /* FIXME - implement nicer consturctors
37465 if (layout.layout) {
37467 layout = config.layout;
37468 delete config.layout;
37470 if (layout.xtype && !layout.getEl) {
37471 // then layout needs constructing..
37472 layout = Roo.factory(layout, Roo);
37476 config.el = config.layout.getEl();
37478 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37480 config.layout.monitorWindowResize = false; // turn off autosizing
37481 this.layout = config.layout;
37482 this.layout.getEl().addClass("roo-layout-nested-layout");
37489 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37491 setSize : function(width, height){
37492 if(!this.ignoreResize(width, height)){
37493 var size = this.adjustForComponents(width, height);
37494 var el = this.layout.getEl();
37495 if (size.height < 1) {
37496 el.setWidth(size.width);
37498 el.setSize(size.width, size.height);
37500 var touch = el.dom.offsetWidth;
37501 this.layout.layout();
37502 // ie requires a double layout on the first pass
37503 if(Roo.isIE && !this.initialized){
37504 this.initialized = true;
37505 this.layout.layout();
37510 // activate all subpanels if not currently active..
37512 setActiveState : function(active){
37513 this.active = active;
37514 this.setActiveClass(active);
37517 this.fireEvent("deactivate", this);
37521 this.fireEvent("activate", this);
37522 // not sure if this should happen before or after..
37523 if (!this.layout) {
37524 return; // should not happen..
37527 for (var r in this.layout.regions) {
37528 reg = this.layout.getRegion(r);
37529 if (reg.getActivePanel()) {
37530 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37531 reg.setActivePanel(reg.getActivePanel());
37534 if (!reg.panels.length) {
37537 reg.showPanel(reg.getPanel(0));
37546 * Returns the nested BorderLayout for this panel
37547 * @return {Roo.BorderLayout}
37549 getLayout : function(){
37550 return this.layout;
37554 * Adds a xtype elements to the layout of the nested panel
37558 xtype : 'ContentPanel',
37565 xtype : 'NestedLayoutPanel',
37571 items : [ ... list of content panels or nested layout panels.. ]
37575 * @param {Object} cfg Xtype definition of item to add.
37577 addxtype : function(cfg) {
37578 return this.layout.addxtype(cfg);
37583 * Ext JS Library 1.1.1
37584 * Copyright(c) 2006-2007, Ext JS, LLC.
37586 * Originally Released Under LGPL - original licence link has changed is not relivant.
37589 * <script type="text/javascript">
37592 * @class Roo.TabPanel
37593 * @extends Roo.util.Observable
37594 * A lightweight tab container.
37598 // basic tabs 1, built from existing content
37599 var tabs = new Roo.TabPanel("tabs1");
37600 tabs.addTab("script", "View Script");
37601 tabs.addTab("markup", "View Markup");
37602 tabs.activate("script");
37604 // more advanced tabs, built from javascript
37605 var jtabs = new Roo.TabPanel("jtabs");
37606 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37608 // set up the UpdateManager
37609 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37610 var updater = tab2.getUpdateManager();
37611 updater.setDefaultUrl("ajax1.htm");
37612 tab2.on('activate', updater.refresh, updater, true);
37614 // Use setUrl for Ajax loading
37615 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37616 tab3.setUrl("ajax2.htm", null, true);
37619 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37622 jtabs.activate("jtabs-1");
37625 * Create a new TabPanel.
37626 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37627 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37629 Roo.bootstrap.panel.Tabs = function(config){
37631 * The container element for this TabPanel.
37632 * @type Roo.Element
37634 this.el = Roo.get(config.el);
37637 if(typeof config == "boolean"){
37638 this.tabPosition = config ? "bottom" : "top";
37640 Roo.apply(this, config);
37644 if(this.tabPosition == "bottom"){
37645 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37646 this.el.addClass("roo-tabs-bottom");
37648 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37649 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37650 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37652 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37654 if(this.tabPosition != "bottom"){
37655 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37656 * @type Roo.Element
37658 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37659 this.el.addClass("roo-tabs-top");
37663 this.bodyEl.setStyle("position", "relative");
37665 this.active = null;
37666 this.activateDelegate = this.activate.createDelegate(this);
37671 * Fires when the active tab changes
37672 * @param {Roo.TabPanel} this
37673 * @param {Roo.TabPanelItem} activePanel The new active tab
37677 * @event beforetabchange
37678 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37679 * @param {Roo.TabPanel} this
37680 * @param {Object} e Set cancel to true on this object to cancel the tab change
37681 * @param {Roo.TabPanelItem} tab The tab being changed to
37683 "beforetabchange" : true
37686 Roo.EventManager.onWindowResize(this.onResize, this);
37687 this.cpad = this.el.getPadding("lr");
37688 this.hiddenCount = 0;
37691 // toolbar on the tabbar support...
37692 if (this.toolbar) {
37693 alert("no toolbar support yet");
37694 this.toolbar = false;
37696 var tcfg = this.toolbar;
37697 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37698 this.toolbar = new Roo.Toolbar(tcfg);
37699 if (Roo.isSafari) {
37700 var tbl = tcfg.container.child('table', true);
37701 tbl.setAttribute('width', '100%');
37709 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37712 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37714 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37716 tabPosition : "top",
37718 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37720 currentTabWidth : 0,
37722 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37726 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37730 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37732 preferredTabWidth : 175,
37734 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37736 resizeTabs : false,
37738 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37740 monitorResize : true,
37742 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37747 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37748 * @param {String} id The id of the div to use <b>or create</b>
37749 * @param {String} text The text for the tab
37750 * @param {String} content (optional) Content to put in the TabPanelItem body
37751 * @param {Boolean} closable (optional) True to create a close icon on the tab
37752 * @return {Roo.TabPanelItem} The created TabPanelItem
37754 addTab : function(id, text, content, closable, tpl)
37756 var item = new Roo.bootstrap.panel.TabItem({
37760 closable : closable,
37763 this.addTabItem(item);
37765 item.setContent(content);
37771 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37772 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37773 * @return {Roo.TabPanelItem}
37775 getTab : function(id){
37776 return this.items[id];
37780 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37781 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37783 hideTab : function(id){
37784 var t = this.items[id];
37787 this.hiddenCount++;
37788 this.autoSizeTabs();
37793 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37794 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37796 unhideTab : function(id){
37797 var t = this.items[id];
37799 t.setHidden(false);
37800 this.hiddenCount--;
37801 this.autoSizeTabs();
37806 * Adds an existing {@link Roo.TabPanelItem}.
37807 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37809 addTabItem : function(item){
37810 this.items[item.id] = item;
37811 this.items.push(item);
37812 // if(this.resizeTabs){
37813 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37814 // this.autoSizeTabs();
37816 // item.autoSize();
37821 * Removes a {@link Roo.TabPanelItem}.
37822 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37824 removeTab : function(id){
37825 var items = this.items;
37826 var tab = items[id];
37827 if(!tab) { return; }
37828 var index = items.indexOf(tab);
37829 if(this.active == tab && items.length > 1){
37830 var newTab = this.getNextAvailable(index);
37835 this.stripEl.dom.removeChild(tab.pnode.dom);
37836 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37837 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37839 items.splice(index, 1);
37840 delete this.items[tab.id];
37841 tab.fireEvent("close", tab);
37842 tab.purgeListeners();
37843 this.autoSizeTabs();
37846 getNextAvailable : function(start){
37847 var items = this.items;
37849 // look for a next tab that will slide over to
37850 // replace the one being removed
37851 while(index < items.length){
37852 var item = items[++index];
37853 if(item && !item.isHidden()){
37857 // if one isn't found select the previous tab (on the left)
37860 var item = items[--index];
37861 if(item && !item.isHidden()){
37869 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37870 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37872 disableTab : function(id){
37873 var tab = this.items[id];
37874 if(tab && this.active != tab){
37880 * Enables a {@link Roo.TabPanelItem} that is disabled.
37881 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37883 enableTab : function(id){
37884 var tab = this.items[id];
37889 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37890 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37891 * @return {Roo.TabPanelItem} The TabPanelItem.
37893 activate : function(id){
37894 var tab = this.items[id];
37898 if(tab == this.active || tab.disabled){
37902 this.fireEvent("beforetabchange", this, e, tab);
37903 if(e.cancel !== true && !tab.disabled){
37905 this.active.hide();
37907 this.active = this.items[id];
37908 this.active.show();
37909 this.fireEvent("tabchange", this, this.active);
37915 * Gets the active {@link Roo.TabPanelItem}.
37916 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37918 getActiveTab : function(){
37919 return this.active;
37923 * Updates the tab body element to fit the height of the container element
37924 * for overflow scrolling
37925 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37927 syncHeight : function(targetHeight){
37928 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37929 var bm = this.bodyEl.getMargins();
37930 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37931 this.bodyEl.setHeight(newHeight);
37935 onResize : function(){
37936 if(this.monitorResize){
37937 this.autoSizeTabs();
37942 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37944 beginUpdate : function(){
37945 this.updating = true;
37949 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37951 endUpdate : function(){
37952 this.updating = false;
37953 this.autoSizeTabs();
37957 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37959 autoSizeTabs : function(){
37960 var count = this.items.length;
37961 var vcount = count - this.hiddenCount;
37962 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37965 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37966 var availWidth = Math.floor(w / vcount);
37967 var b = this.stripBody;
37968 if(b.getWidth() > w){
37969 var tabs = this.items;
37970 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37971 if(availWidth < this.minTabWidth){
37972 /*if(!this.sleft){ // incomplete scrolling code
37973 this.createScrollButtons();
37976 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37979 if(this.currentTabWidth < this.preferredTabWidth){
37980 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37986 * Returns the number of tabs in this TabPanel.
37989 getCount : function(){
37990 return this.items.length;
37994 * Resizes all the tabs to the passed width
37995 * @param {Number} The new width
37997 setTabWidth : function(width){
37998 this.currentTabWidth = width;
37999 for(var i = 0, len = this.items.length; i < len; i++) {
38000 if(!this.items[i].isHidden()) {
38001 this.items[i].setWidth(width);
38007 * Destroys this TabPanel
38008 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38010 destroy : function(removeEl){
38011 Roo.EventManager.removeResizeListener(this.onResize, this);
38012 for(var i = 0, len = this.items.length; i < len; i++){
38013 this.items[i].purgeListeners();
38015 if(removeEl === true){
38016 this.el.update("");
38021 createStrip : function(container)
38023 var strip = document.createElement("nav");
38024 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38025 container.appendChild(strip);
38029 createStripList : function(strip)
38031 // div wrapper for retard IE
38032 // returns the "tr" element.
38033 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38034 //'<div class="x-tabs-strip-wrap">'+
38035 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38036 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38037 return strip.firstChild; //.firstChild.firstChild.firstChild;
38039 createBody : function(container)
38041 var body = document.createElement("div");
38042 Roo.id(body, "tab-body");
38043 //Roo.fly(body).addClass("x-tabs-body");
38044 Roo.fly(body).addClass("tab-content");
38045 container.appendChild(body);
38048 createItemBody :function(bodyEl, id){
38049 var body = Roo.getDom(id);
38051 body = document.createElement("div");
38054 //Roo.fly(body).addClass("x-tabs-item-body");
38055 Roo.fly(body).addClass("tab-pane");
38056 bodyEl.insertBefore(body, bodyEl.firstChild);
38060 createStripElements : function(stripEl, text, closable, tpl)
38062 var td = document.createElement("li"); // was td..
38065 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38068 stripEl.appendChild(td);
38070 td.className = "x-tabs-closable";
38071 if(!this.closeTpl){
38072 this.closeTpl = new Roo.Template(
38073 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38074 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38075 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38078 var el = this.closeTpl.overwrite(td, {"text": text});
38079 var close = el.getElementsByTagName("div")[0];
38080 var inner = el.getElementsByTagName("em")[0];
38081 return {"el": el, "close": close, "inner": inner};
38084 // not sure what this is..
38085 // if(!this.tabTpl){
38086 //this.tabTpl = new Roo.Template(
38087 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38088 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38090 // this.tabTpl = new Roo.Template(
38091 // '<a href="#">' +
38092 // '<span unselectable="on"' +
38093 // (this.disableTooltips ? '' : ' title="{text}"') +
38094 // ' >{text}</span></a>'
38100 var template = tpl || this.tabTpl || false;
38104 template = new Roo.Template(
38106 '<span unselectable="on"' +
38107 (this.disableTooltips ? '' : ' title="{text}"') +
38108 ' >{text}</span></a>'
38112 switch (typeof(template)) {
38116 template = new Roo.Template(template);
38122 var el = template.overwrite(td, {"text": text});
38124 var inner = el.getElementsByTagName("span")[0];
38126 return {"el": el, "inner": inner};
38134 * @class Roo.TabPanelItem
38135 * @extends Roo.util.Observable
38136 * Represents an individual item (tab plus body) in a TabPanel.
38137 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38138 * @param {String} id The id of this TabPanelItem
38139 * @param {String} text The text for the tab of this TabPanelItem
38140 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38142 Roo.bootstrap.panel.TabItem = function(config){
38144 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38145 * @type Roo.TabPanel
38147 this.tabPanel = config.panel;
38149 * The id for this TabPanelItem
38152 this.id = config.id;
38154 this.disabled = false;
38156 this.text = config.text;
38158 this.loaded = false;
38159 this.closable = config.closable;
38162 * The body element for this TabPanelItem.
38163 * @type Roo.Element
38165 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38166 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38167 this.bodyEl.setStyle("display", "block");
38168 this.bodyEl.setStyle("zoom", "1");
38169 //this.hideAction();
38171 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38173 this.el = Roo.get(els.el);
38174 this.inner = Roo.get(els.inner, true);
38175 this.textEl = Roo.get(this.el.dom.firstChild, true);
38176 this.pnode = Roo.get(els.el.parentNode, true);
38177 // this.el.on("mousedown", this.onTabMouseDown, this);
38178 this.el.on("click", this.onTabClick, this);
38180 if(config.closable){
38181 var c = Roo.get(els.close, true);
38182 c.dom.title = this.closeText;
38183 c.addClassOnOver("close-over");
38184 c.on("click", this.closeClick, this);
38190 * Fires when this tab becomes the active tab.
38191 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38192 * @param {Roo.TabPanelItem} this
38196 * @event beforeclose
38197 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38198 * @param {Roo.TabPanelItem} this
38199 * @param {Object} e Set cancel to true on this object to cancel the close.
38201 "beforeclose": true,
38204 * Fires when this tab is closed.
38205 * @param {Roo.TabPanelItem} this
38209 * @event deactivate
38210 * Fires when this tab is no longer the active tab.
38211 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38212 * @param {Roo.TabPanelItem} this
38214 "deactivate" : true
38216 this.hidden = false;
38218 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38221 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38223 purgeListeners : function(){
38224 Roo.util.Observable.prototype.purgeListeners.call(this);
38225 this.el.removeAllListeners();
38228 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38231 this.pnode.addClass("active");
38234 this.tabPanel.stripWrap.repaint();
38236 this.fireEvent("activate", this.tabPanel, this);
38240 * Returns true if this tab is the active tab.
38241 * @return {Boolean}
38243 isActive : function(){
38244 return this.tabPanel.getActiveTab() == this;
38248 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38251 this.pnode.removeClass("active");
38253 this.fireEvent("deactivate", this.tabPanel, this);
38256 hideAction : function(){
38257 this.bodyEl.hide();
38258 this.bodyEl.setStyle("position", "absolute");
38259 this.bodyEl.setLeft("-20000px");
38260 this.bodyEl.setTop("-20000px");
38263 showAction : function(){
38264 this.bodyEl.setStyle("position", "relative");
38265 this.bodyEl.setTop("");
38266 this.bodyEl.setLeft("");
38267 this.bodyEl.show();
38271 * Set the tooltip for the tab.
38272 * @param {String} tooltip The tab's tooltip
38274 setTooltip : function(text){
38275 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38276 this.textEl.dom.qtip = text;
38277 this.textEl.dom.removeAttribute('title');
38279 this.textEl.dom.title = text;
38283 onTabClick : function(e){
38284 e.preventDefault();
38285 this.tabPanel.activate(this.id);
38288 onTabMouseDown : function(e){
38289 e.preventDefault();
38290 this.tabPanel.activate(this.id);
38293 getWidth : function(){
38294 return this.inner.getWidth();
38297 setWidth : function(width){
38298 var iwidth = width - this.pnode.getPadding("lr");
38299 this.inner.setWidth(iwidth);
38300 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38301 this.pnode.setWidth(width);
38305 * Show or hide the tab
38306 * @param {Boolean} hidden True to hide or false to show.
38308 setHidden : function(hidden){
38309 this.hidden = hidden;
38310 this.pnode.setStyle("display", hidden ? "none" : "");
38314 * Returns true if this tab is "hidden"
38315 * @return {Boolean}
38317 isHidden : function(){
38318 return this.hidden;
38322 * Returns the text for this tab
38325 getText : function(){
38329 autoSize : function(){
38330 //this.el.beginMeasure();
38331 this.textEl.setWidth(1);
38333 * #2804 [new] Tabs in Roojs
38334 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38336 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38337 //this.el.endMeasure();
38341 * Sets the text for the tab (Note: this also sets the tooltip text)
38342 * @param {String} text The tab's text and tooltip
38344 setText : function(text){
38346 this.textEl.update(text);
38347 this.setTooltip(text);
38348 //if(!this.tabPanel.resizeTabs){
38349 // this.autoSize();
38353 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38355 activate : function(){
38356 this.tabPanel.activate(this.id);
38360 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38362 disable : function(){
38363 if(this.tabPanel.active != this){
38364 this.disabled = true;
38365 this.pnode.addClass("disabled");
38370 * Enables this TabPanelItem if it was previously disabled.
38372 enable : function(){
38373 this.disabled = false;
38374 this.pnode.removeClass("disabled");
38378 * Sets the content for this TabPanelItem.
38379 * @param {String} content The content
38380 * @param {Boolean} loadScripts true to look for and load scripts
38382 setContent : function(content, loadScripts){
38383 this.bodyEl.update(content, loadScripts);
38387 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38388 * @return {Roo.UpdateManager} The UpdateManager
38390 getUpdateManager : function(){
38391 return this.bodyEl.getUpdateManager();
38395 * Set a URL to be used to load the content for this TabPanelItem.
38396 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38397 * @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)
38398 * @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)
38399 * @return {Roo.UpdateManager} The UpdateManager
38401 setUrl : function(url, params, loadOnce){
38402 if(this.refreshDelegate){
38403 this.un('activate', this.refreshDelegate);
38405 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38406 this.on("activate", this.refreshDelegate);
38407 return this.bodyEl.getUpdateManager();
38411 _handleRefresh : function(url, params, loadOnce){
38412 if(!loadOnce || !this.loaded){
38413 var updater = this.bodyEl.getUpdateManager();
38414 updater.update(url, params, this._setLoaded.createDelegate(this));
38419 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38420 * Will fail silently if the setUrl method has not been called.
38421 * This does not activate the panel, just updates its content.
38423 refresh : function(){
38424 if(this.refreshDelegate){
38425 this.loaded = false;
38426 this.refreshDelegate();
38431 _setLoaded : function(){
38432 this.loaded = true;
38436 closeClick : function(e){
38439 this.fireEvent("beforeclose", this, o);
38440 if(o.cancel !== true){
38441 this.tabPanel.removeTab(this.id);
38445 * The text displayed in the tooltip for the close icon.
38448 closeText : "Close this tab"
38451 * This script refer to:
38452 * Title: International Telephone Input
38453 * Author: Jack O'Connor
38454 * Code version: v12.1.12
38455 * Availability: https://github.com/jackocnr/intl-tel-input.git
38458 Roo.bootstrap.PhoneInputData = function() {
38461 "Afghanistan (افغانستان)",
38466 "Albania (Shqipëri)",
38471 "Algeria (الجزائر)",
38496 "Antigua and Barbuda",
38506 "Armenia (Հայաստան)",
38522 "Austria (Österreich)",
38527 "Azerbaijan (Azərbaycan)",
38537 "Bahrain (البحرين)",
38542 "Bangladesh (বাংলাদেশ)",
38552 "Belarus (Беларусь)",
38557 "Belgium (België)",
38587 "Bosnia and Herzegovina (Босна и Херцеговина)",
38602 "British Indian Ocean Territory",
38607 "British Virgin Islands",
38617 "Bulgaria (България)",
38627 "Burundi (Uburundi)",
38632 "Cambodia (កម្ពុជា)",
38637 "Cameroon (Cameroun)",
38646 ["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"]
38649 "Cape Verde (Kabu Verdi)",
38654 "Caribbean Netherlands",
38665 "Central African Republic (République centrafricaine)",
38685 "Christmas Island",
38691 "Cocos (Keeling) Islands",
38702 "Comoros (جزر القمر)",
38707 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38712 "Congo (Republic) (Congo-Brazzaville)",
38732 "Croatia (Hrvatska)",
38753 "Czech Republic (Česká republika)",
38758 "Denmark (Danmark)",
38773 "Dominican Republic (República Dominicana)",
38777 ["809", "829", "849"]
38795 "Equatorial Guinea (Guinea Ecuatorial)",
38815 "Falkland Islands (Islas Malvinas)",
38820 "Faroe Islands (Føroyar)",
38841 "French Guiana (Guyane française)",
38846 "French Polynesia (Polynésie française)",
38861 "Georgia (საქართველო)",
38866 "Germany (Deutschland)",
38886 "Greenland (Kalaallit Nunaat)",
38923 "Guinea-Bissau (Guiné Bissau)",
38948 "Hungary (Magyarország)",
38953 "Iceland (Ísland)",
38973 "Iraq (العراق)",
38989 "Israel (ישראל)",
39016 "Jordan (الأردن)",
39021 "Kazakhstan (Казахстан)",
39042 "Kuwait (الكويت)",
39047 "Kyrgyzstan (Кыргызстан)",
39057 "Latvia (Latvija)",
39062 "Lebanon (لبنان)",
39077 "Libya (ليبيا)",
39087 "Lithuania (Lietuva)",
39102 "Macedonia (FYROM) (Македонија)",
39107 "Madagascar (Madagasikara)",
39137 "Marshall Islands",
39147 "Mauritania (موريتانيا)",
39152 "Mauritius (Moris)",
39173 "Moldova (Republica Moldova)",
39183 "Mongolia (Монгол)",
39188 "Montenegro (Crna Gora)",
39198 "Morocco (المغرب)",
39204 "Mozambique (Moçambique)",
39209 "Myanmar (Burma) (မြန်မာ)",
39214 "Namibia (Namibië)",
39229 "Netherlands (Nederland)",
39234 "New Caledonia (Nouvelle-Calédonie)",
39269 "North Korea (조선 민주주의 인민 공화국)",
39274 "Northern Mariana Islands",
39290 "Pakistan (پاکستان)",
39300 "Palestine (فلسطين)",
39310 "Papua New Guinea",
39352 "Réunion (La Réunion)",
39358 "Romania (România)",
39374 "Saint Barthélemy",
39385 "Saint Kitts and Nevis",
39395 "Saint Martin (Saint-Martin (partie française))",
39401 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39406 "Saint Vincent and the Grenadines",
39421 "São Tomé and Príncipe (São Tomé e Príncipe)",
39426 "Saudi Arabia (المملكة العربية السعودية)",
39431 "Senegal (Sénégal)",
39461 "Slovakia (Slovensko)",
39466 "Slovenia (Slovenija)",
39476 "Somalia (Soomaaliya)",
39486 "South Korea (대한민국)",
39491 "South Sudan (جنوب السودان)",
39501 "Sri Lanka (ශ්රී ලංකාව)",
39506 "Sudan (السودان)",
39516 "Svalbard and Jan Mayen",
39527 "Sweden (Sverige)",
39532 "Switzerland (Schweiz)",
39537 "Syria (سوريا)",
39582 "Trinidad and Tobago",
39587 "Tunisia (تونس)",
39592 "Turkey (Türkiye)",
39602 "Turks and Caicos Islands",
39612 "U.S. Virgin Islands",
39622 "Ukraine (Україна)",
39627 "United Arab Emirates (الإمارات العربية المتحدة)",
39649 "Uzbekistan (Oʻzbekiston)",
39659 "Vatican City (Città del Vaticano)",
39670 "Vietnam (Việt Nam)",
39675 "Wallis and Futuna (Wallis-et-Futuna)",
39680 "Western Sahara (الصحراء الغربية)",
39686 "Yemen (اليمن)",
39710 * This script refer to:
39711 * Title: International Telephone Input
39712 * Author: Jack O'Connor
39713 * Code version: v12.1.12
39714 * Availability: https://github.com/jackocnr/intl-tel-input.git
39718 * @class Roo.bootstrap.PhoneInput
39719 * @extends Roo.bootstrap.TriggerField
39720 * An input with International dial-code selection
39722 * @cfg {String} defaultDialCode default '+852'
39723 * @cfg {Array} preferedCountries default []
39726 * Create a new PhoneInput.
39727 * @param {Object} config Configuration options
39730 Roo.bootstrap.PhoneInput = function(config) {
39731 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39734 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39736 listWidth: undefined,
39738 selectedClass: 'active',
39740 invalidClass : "has-warning",
39742 validClass: 'has-success',
39744 allowed: '0123456789',
39747 * @cfg {String} defaultDialCode The default dial code when initializing the input
39749 defaultDialCode: '+852',
39752 * @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
39754 preferedCountries: false,
39756 getAutoCreate : function()
39758 var data = Roo.bootstrap.PhoneInputData();
39759 var align = this.labelAlign || this.parentLabelAlign();
39762 this.allCountries = [];
39763 this.dialCodeMapping = [];
39765 for (var i = 0; i < data.length; i++) {
39767 this.allCountries[i] = {
39771 priority: c[3] || 0,
39772 areaCodes: c[4] || null
39774 this.dialCodeMapping[c[2]] = {
39777 priority: c[3] || 0,
39778 areaCodes: c[4] || null
39790 cls : 'form-control tel-input',
39791 autocomplete: 'new-password'
39794 var hiddenInput = {
39797 cls: 'hidden-tel-input'
39801 hiddenInput.name = this.name;
39804 if (this.disabled) {
39805 input.disabled = true;
39808 var flag_container = {
39825 cls: this.hasFeedback ? 'has-feedback' : '',
39831 cls: 'dial-code-holder',
39838 cls: 'roo-select2-container input-group',
39845 if (this.fieldLabel.length) {
39848 tooltip: 'This field is required'
39854 cls: 'control-label',
39860 html: this.fieldLabel
39863 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39869 if(this.indicatorpos == 'right') {
39870 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39877 if(align == 'left') {
39885 if(this.labelWidth > 12){
39886 label.style = "width: " + this.labelWidth + 'px';
39888 if(this.labelWidth < 13 && this.labelmd == 0){
39889 this.labelmd = this.labelWidth;
39891 if(this.labellg > 0){
39892 label.cls += ' col-lg-' + this.labellg;
39893 input.cls += ' col-lg-' + (12 - this.labellg);
39895 if(this.labelmd > 0){
39896 label.cls += ' col-md-' + this.labelmd;
39897 container.cls += ' col-md-' + (12 - this.labelmd);
39899 if(this.labelsm > 0){
39900 label.cls += ' col-sm-' + this.labelsm;
39901 container.cls += ' col-sm-' + (12 - this.labelsm);
39903 if(this.labelxs > 0){
39904 label.cls += ' col-xs-' + this.labelxs;
39905 container.cls += ' col-xs-' + (12 - this.labelxs);
39915 var settings = this;
39917 ['xs','sm','md','lg'].map(function(size){
39918 if (settings[size]) {
39919 cfg.cls += ' col-' + size + '-' + settings[size];
39923 this.store = new Roo.data.Store({
39924 proxy : new Roo.data.MemoryProxy({}),
39925 reader : new Roo.data.JsonReader({
39936 'name' : 'dialCode',
39940 'name' : 'priority',
39944 'name' : 'areaCodes',
39951 if(!this.preferedCountries) {
39952 this.preferedCountries = [
39959 var p = this.preferedCountries.reverse();
39962 for (var i = 0; i < p.length; i++) {
39963 for (var j = 0; j < this.allCountries.length; j++) {
39964 if(this.allCountries[j].iso2 == p[i]) {
39965 var t = this.allCountries[j];
39966 this.allCountries.splice(j,1);
39967 this.allCountries.unshift(t);
39973 this.store.proxy.data = {
39975 data: this.allCountries
39981 initEvents : function()
39984 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39986 this.indicator = this.indicatorEl();
39987 this.flag = this.flagEl();
39988 this.dialCodeHolder = this.dialCodeHolderEl();
39990 this.trigger = this.el.select('div.flag-box',true).first();
39991 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39996 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39997 _this.list.setWidth(lw);
40000 this.list.on('mouseover', this.onViewOver, this);
40001 this.list.on('mousemove', this.onViewMove, this);
40002 this.inputEl().on("keyup", this.onKeyUp, this);
40004 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40006 this.view = new Roo.View(this.list, this.tpl, {
40007 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40010 this.view.on('click', this.onViewClick, this);
40011 this.setValue(this.defaultDialCode);
40014 onTriggerClick : function(e)
40016 Roo.log('trigger click');
40021 if(this.isExpanded()){
40023 this.hasFocus = false;
40025 this.store.load({});
40026 this.hasFocus = true;
40031 isExpanded : function()
40033 return this.list.isVisible();
40036 collapse : function()
40038 if(!this.isExpanded()){
40042 Roo.get(document).un('mousedown', this.collapseIf, this);
40043 Roo.get(document).un('mousewheel', this.collapseIf, this);
40044 this.fireEvent('collapse', this);
40048 expand : function()
40052 if(this.isExpanded() || !this.hasFocus){
40056 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40057 this.list.setWidth(lw);
40060 this.restrictHeight();
40062 Roo.get(document).on('mousedown', this.collapseIf, this);
40063 Roo.get(document).on('mousewheel', this.collapseIf, this);
40065 this.fireEvent('expand', this);
40068 restrictHeight : function()
40070 this.list.alignTo(this.inputEl(), this.listAlign);
40071 this.list.alignTo(this.inputEl(), this.listAlign);
40074 onViewOver : function(e, t)
40076 if(this.inKeyMode){
40079 var item = this.view.findItemFromChild(t);
40082 var index = this.view.indexOf(item);
40083 this.select(index, false);
40088 onViewClick : function(view, doFocus, el, e)
40090 var index = this.view.getSelectedIndexes()[0];
40092 var r = this.store.getAt(index);
40095 this.onSelect(r, index);
40097 if(doFocus !== false && !this.blockFocus){
40098 this.inputEl().focus();
40102 onViewMove : function(e, t)
40104 this.inKeyMode = false;
40107 select : function(index, scrollIntoView)
40109 this.selectedIndex = index;
40110 this.view.select(index);
40111 if(scrollIntoView !== false){
40112 var el = this.view.getNode(index);
40114 this.list.scrollChildIntoView(el, false);
40119 createList : function()
40121 this.list = Roo.get(document.body).createChild({
40123 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40124 style: 'display:none'
40127 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40130 collapseIf : function(e)
40132 var in_combo = e.within(this.el);
40133 var in_list = e.within(this.list);
40134 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40136 if (in_combo || in_list || is_list) {
40142 onSelect : function(record, index)
40144 if(this.fireEvent('beforeselect', this, record, index) !== false){
40146 this.setFlagClass(record.data.iso2);
40147 this.setDialCode(record.data.dialCode);
40148 this.hasFocus = false;
40150 this.fireEvent('select', this, record, index);
40154 flagEl : function()
40156 var flag = this.el.select('div.flag',true).first();
40163 dialCodeHolderEl : function()
40165 var d = this.el.select('input.dial-code-holder',true).first();
40172 setDialCode : function(v)
40174 this.dialCodeHolder.dom.value = '+'+v;
40177 setFlagClass : function(n)
40179 this.flag.dom.className = 'flag '+n;
40182 getValue : function()
40184 var v = this.inputEl().getValue();
40185 if(this.dialCodeHolder) {
40186 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40191 setValue : function(v)
40193 var d = this.getDialCode(v);
40195 //invalid dial code
40196 if(v.length == 0 || !d || d.length == 0) {
40198 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40199 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40205 this.setFlagClass(this.dialCodeMapping[d].iso2);
40206 this.setDialCode(d);
40207 this.inputEl().dom.value = v.replace('+'+d,'');
40208 this.hiddenEl().dom.value = this.getValue();
40213 getDialCode : function(v)
40217 if (v.length == 0) {
40218 return this.dialCodeHolder.dom.value;
40222 if (v.charAt(0) != "+") {
40225 var numericChars = "";
40226 for (var i = 1; i < v.length; i++) {
40227 var c = v.charAt(i);
40230 if (this.dialCodeMapping[numericChars]) {
40231 dialCode = v.substr(1, i);
40233 if (numericChars.length == 4) {
40243 this.setValue(this.defaultDialCode);
40247 hiddenEl : function()
40249 return this.el.select('input.hidden-tel-input',true).first();
40252 onKeyUp : function(e){
40254 var k = e.getKey();
40255 var c = e.getCharCode();
40258 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40259 this.allowed.indexOf(String.fromCharCode(c)) === -1
40264 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40267 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40271 this.setValue(this.getValue());
40276 * @class Roo.bootstrap.MoneyField
40277 * @extends Roo.bootstrap.ComboBox
40278 * Bootstrap MoneyField class
40281 * Create a new MoneyField.
40282 * @param {Object} config Configuration options
40285 Roo.bootstrap.MoneyField = function(config) {
40287 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40291 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40294 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40296 allowDecimals : true,
40298 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40300 decimalSeparator : ".",
40302 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40304 decimalPrecision : 0,
40306 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40308 allowNegative : true,
40310 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40314 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40316 minValue : Number.NEGATIVE_INFINITY,
40318 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40320 maxValue : Number.MAX_VALUE,
40322 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40324 minText : "The minimum value for this field is {0}",
40326 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40328 maxText : "The maximum value for this field is {0}",
40330 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40331 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40333 nanText : "{0} is not a valid number",
40335 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40339 * @cfg {String} defaults currency of the MoneyField
40340 * value should be in lkey
40342 defaultCurrency : false,
40344 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40346 thousandsDelimiter : false,
40356 getAutoCreate : function()
40358 var align = this.labelAlign || this.parentLabelAlign();
40370 cls : 'form-control roo-money-amount-input',
40371 autocomplete: 'new-password'
40374 var hiddenInput = {
40378 cls: 'hidden-number-input'
40382 hiddenInput.name = this.name;
40385 if (this.disabled) {
40386 input.disabled = true;
40389 var clg = 12 - this.inputlg;
40390 var cmd = 12 - this.inputmd;
40391 var csm = 12 - this.inputsm;
40392 var cxs = 12 - this.inputxs;
40396 cls : 'row roo-money-field',
40400 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40404 cls: 'roo-select2-container input-group',
40408 cls : 'form-control roo-money-currency-input',
40409 autocomplete: 'new-password',
40411 name : this.currencyName
40415 cls : 'input-group-addon',
40429 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40433 cls: this.hasFeedback ? 'has-feedback' : '',
40444 if (this.fieldLabel.length) {
40447 tooltip: 'This field is required'
40453 cls: 'control-label',
40459 html: this.fieldLabel
40462 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40468 if(this.indicatorpos == 'right') {
40469 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40476 if(align == 'left') {
40484 if(this.labelWidth > 12){
40485 label.style = "width: " + this.labelWidth + 'px';
40487 if(this.labelWidth < 13 && this.labelmd == 0){
40488 this.labelmd = this.labelWidth;
40490 if(this.labellg > 0){
40491 label.cls += ' col-lg-' + this.labellg;
40492 input.cls += ' col-lg-' + (12 - this.labellg);
40494 if(this.labelmd > 0){
40495 label.cls += ' col-md-' + this.labelmd;
40496 container.cls += ' col-md-' + (12 - this.labelmd);
40498 if(this.labelsm > 0){
40499 label.cls += ' col-sm-' + this.labelsm;
40500 container.cls += ' col-sm-' + (12 - this.labelsm);
40502 if(this.labelxs > 0){
40503 label.cls += ' col-xs-' + this.labelxs;
40504 container.cls += ' col-xs-' + (12 - this.labelxs);
40515 var settings = this;
40517 ['xs','sm','md','lg'].map(function(size){
40518 if (settings[size]) {
40519 cfg.cls += ' col-' + size + '-' + settings[size];
40526 initEvents : function()
40528 this.indicator = this.indicatorEl();
40530 this.initCurrencyEvent();
40532 this.initNumberEvent();
40535 initCurrencyEvent : function()
40538 throw "can not find store for combo";
40541 this.store = Roo.factory(this.store, Roo.data);
40542 this.store.parent = this;
40546 this.triggerEl = this.el.select('.input-group-addon', true).first();
40548 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40553 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40554 _this.list.setWidth(lw);
40557 this.list.on('mouseover', this.onViewOver, this);
40558 this.list.on('mousemove', this.onViewMove, this);
40559 this.list.on('scroll', this.onViewScroll, this);
40562 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40565 this.view = new Roo.View(this.list, this.tpl, {
40566 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40569 this.view.on('click', this.onViewClick, this);
40571 this.store.on('beforeload', this.onBeforeLoad, this);
40572 this.store.on('load', this.onLoad, this);
40573 this.store.on('loadexception', this.onLoadException, this);
40575 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40576 "up" : function(e){
40577 this.inKeyMode = true;
40581 "down" : function(e){
40582 if(!this.isExpanded()){
40583 this.onTriggerClick();
40585 this.inKeyMode = true;
40590 "enter" : function(e){
40593 if(this.fireEvent("specialkey", this, e)){
40594 this.onViewClick(false);
40600 "esc" : function(e){
40604 "tab" : function(e){
40607 if(this.fireEvent("specialkey", this, e)){
40608 this.onViewClick(false);
40616 doRelay : function(foo, bar, hname){
40617 if(hname == 'down' || this.scope.isExpanded()){
40618 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40626 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40630 initNumberEvent : function(e)
40632 this.inputEl().on("keydown" , this.fireKey, this);
40633 this.inputEl().on("focus", this.onFocus, this);
40634 this.inputEl().on("blur", this.onBlur, this);
40636 this.inputEl().relayEvent('keyup', this);
40638 if(this.indicator){
40639 this.indicator.addClass('invisible');
40642 this.originalValue = this.getValue();
40644 if(this.validationEvent == 'keyup'){
40645 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40646 this.inputEl().on('keyup', this.filterValidation, this);
40648 else if(this.validationEvent !== false){
40649 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40652 if(this.selectOnFocus){
40653 this.on("focus", this.preFocus, this);
40656 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40657 this.inputEl().on("keypress", this.filterKeys, this);
40659 this.inputEl().relayEvent('keypress', this);
40662 var allowed = "0123456789";
40664 if(this.allowDecimals){
40665 allowed += this.decimalSeparator;
40668 if(this.allowNegative){
40672 if(this.thousandsDelimiter) {
40676 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40678 var keyPress = function(e){
40680 var k = e.getKey();
40682 var c = e.getCharCode();
40685 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40686 allowed.indexOf(String.fromCharCode(c)) === -1
40692 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40696 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40701 this.inputEl().on("keypress", keyPress, this);
40705 onTriggerClick : function(e)
40712 this.loadNext = false;
40714 if(this.isExpanded()){
40719 this.hasFocus = true;
40721 if(this.triggerAction == 'all') {
40722 this.doQuery(this.allQuery, true);
40726 this.doQuery(this.getRawValue());
40729 getCurrency : function()
40731 var v = this.currencyEl().getValue();
40736 restrictHeight : function()
40738 this.list.alignTo(this.currencyEl(), this.listAlign);
40739 this.list.alignTo(this.currencyEl(), this.listAlign);
40742 onViewClick : function(view, doFocus, el, e)
40744 var index = this.view.getSelectedIndexes()[0];
40746 var r = this.store.getAt(index);
40749 this.onSelect(r, index);
40753 onSelect : function(record, index){
40755 if(this.fireEvent('beforeselect', this, record, index) !== false){
40757 this.setFromCurrencyData(index > -1 ? record.data : false);
40761 this.fireEvent('select', this, record, index);
40765 setFromCurrencyData : function(o)
40769 this.lastCurrency = o;
40771 if (this.currencyField) {
40772 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40774 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40777 this.lastSelectionText = currency;
40779 //setting default currency
40780 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40781 this.setCurrency(this.defaultCurrency);
40785 this.setCurrency(currency);
40788 setFromData : function(o)
40792 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40794 this.setFromCurrencyData(c);
40799 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40801 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40804 this.setValue(value);
40808 setCurrency : function(v)
40810 this.currencyValue = v;
40813 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40818 setValue : function(v)
40820 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40826 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40828 this.inputEl().dom.value = (v == '') ? '' :
40829 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40831 if(!this.allowZero && v === '0') {
40832 this.hiddenEl().dom.value = '';
40833 this.inputEl().dom.value = '';
40840 getRawValue : function()
40842 var v = this.inputEl().getValue();
40847 getValue : function()
40849 return this.fixPrecision(this.parseValue(this.getRawValue()));
40852 parseValue : function(value)
40854 if(this.thousandsDelimiter) {
40856 r = new RegExp(",", "g");
40857 value = value.replace(r, "");
40860 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40861 return isNaN(value) ? '' : value;
40865 fixPrecision : function(value)
40867 if(this.thousandsDelimiter) {
40869 r = new RegExp(",", "g");
40870 value = value.replace(r, "");
40873 var nan = isNaN(value);
40875 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40876 return nan ? '' : value;
40878 return parseFloat(value).toFixed(this.decimalPrecision);
40881 decimalPrecisionFcn : function(v)
40883 return Math.floor(v);
40886 validateValue : function(value)
40888 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40892 var num = this.parseValue(value);
40895 this.markInvalid(String.format(this.nanText, value));
40899 if(num < this.minValue){
40900 this.markInvalid(String.format(this.minText, this.minValue));
40904 if(num > this.maxValue){
40905 this.markInvalid(String.format(this.maxText, this.maxValue));
40912 validate : function()
40914 if(this.disabled || this.allowBlank){
40919 var currency = this.getCurrency();
40921 if(this.validateValue(this.getRawValue()) && currency.length){
40926 this.markInvalid();
40930 getName: function()
40935 beforeBlur : function()
40941 var v = this.parseValue(this.getRawValue());
40948 onBlur : function()
40952 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40953 //this.el.removeClass(this.focusClass);
40956 this.hasFocus = false;
40958 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40962 var v = this.getValue();
40964 if(String(v) !== String(this.startValue)){
40965 this.fireEvent('change', this, v, this.startValue);
40968 this.fireEvent("blur", this);
40971 inputEl : function()
40973 return this.el.select('.roo-money-amount-input', true).first();
40976 currencyEl : function()
40978 return this.el.select('.roo-money-currency-input', true).first();
40981 hiddenEl : function()
40983 return this.el.select('input.hidden-number-input',true).first();