4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
19 * @cfg {string} tooltip Text for the tooltip
20 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
21 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
24 * Do not use directly - it does not do anything..
25 * @param {Object} config The config object
30 Roo.bootstrap.Component = function(config){
31 Roo.bootstrap.Component.superclass.constructor.call(this, config);
35 * @event childrenrendered
36 * Fires when the children have been rendered..
37 * @param {Roo.bootstrap.Component} this
39 "childrenrendered" : true
48 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
51 allowDomMove : false, // to stop relocations in parent onRender...
61 * Initialize Events for the element
63 initEvents : function() { },
69 can_build_overlaid : true,
71 container_method : false,
78 // returns the parent component..
79 return Roo.ComponentMgr.get(this.parentId)
85 onRender : function(ct, position)
87 // Roo.log("Call onRender: " + this.xtype);
89 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
92 if (this.el.attr('xtype')) {
93 this.el.attr('xtypex', this.el.attr('xtype'));
94 this.el.dom.removeAttribute('xtype');
104 var cfg = Roo.apply({}, this.getAutoCreate());
106 cfg.id = this.id || Roo.id();
108 // fill in the extra attributes
109 if (this.xattr && typeof(this.xattr) =='object') {
110 for (var i in this.xattr) {
111 cfg[i] = this.xattr[i];
116 cfg.dataId = this.dataId;
120 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
123 if (this.style) { // fixme needs to support more complex style data.
124 cfg.style = this.style;
128 cfg.name = this.name;
131 this.el = ct.createChild(cfg, position);
134 this.tooltipEl().attr('tooltip', this.tooltip);
137 if(this.tabIndex !== undefined){
138 this.el.dom.setAttribute('tabIndex', this.tabIndex);
145 * Fetch the element to add children to
146 * @return {Roo.Element} defaults to this.el
148 getChildContainer : function()
153 * Fetch the element to display the tooltip on.
154 * @return {Roo.Element} defaults to this.el
156 tooltipEl : function()
161 addxtype : function(tree,cntr)
165 cn = Roo.factory(tree);
166 //Roo.log(['addxtype', cn]);
168 cn.parentType = this.xtype; //??
169 cn.parentId = this.id;
171 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
172 if (typeof(cn.container_method) == 'string') {
173 cntr = cn.container_method;
177 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
179 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
181 var build_from_html = Roo.XComponent.build_from_html;
183 var is_body = (tree.xtype == 'Body') ;
185 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
187 var self_cntr_el = Roo.get(this[cntr](false));
189 // do not try and build conditional elements
190 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
195 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
196 return this.addxtypeChild(tree,cntr, is_body);
199 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
202 return this.addxtypeChild(Roo.apply({}, tree),cntr);
205 Roo.log('skipping render');
211 if (!build_from_html) {
215 // this i think handles overlaying multiple children of the same type
216 // with the sam eelement.. - which might be buggy..
218 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
224 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
228 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
235 addxtypeChild : function (tree, cntr, is_body)
237 Roo.debug && Roo.log('addxtypeChild:' + cntr);
239 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
242 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
243 (typeof(tree['flexy:foreach']) != 'undefined');
247 skip_children = false;
248 // render the element if it's not BODY.
251 // if parent was disabled, then do not try and create the children..
252 if(!this[cntr](true)){
257 cn = Roo.factory(tree);
259 cn.parentType = this.xtype; //??
260 cn.parentId = this.id;
262 var build_from_html = Roo.XComponent.build_from_html;
265 // does the container contain child eleemnts with 'xtype' attributes.
266 // that match this xtype..
267 // note - when we render we create these as well..
268 // so we should check to see if body has xtype set.
269 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
271 var self_cntr_el = Roo.get(this[cntr](false));
272 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
274 //Roo.log(Roo.XComponent.build_from_html);
275 //Roo.log("got echild:");
278 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
279 // and are not displayed -this causes this to use up the wrong element when matching.
280 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
283 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
284 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
290 //echild.dom.removeAttribute('xtype');
292 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
293 Roo.debug && Roo.log(self_cntr_el);
294 Roo.debug && Roo.log(echild);
295 Roo.debug && Roo.log(cn);
301 // if object has flexy:if - then it may or may not be rendered.
302 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
303 // skip a flexy if element.
304 Roo.debug && Roo.log('skipping render');
305 Roo.debug && Roo.log(tree);
307 Roo.debug && Roo.log('skipping all children');
308 skip_children = true;
313 // actually if flexy:foreach is found, we really want to create
314 // multiple copies here...
316 //Roo.log(this[cntr]());
317 // some elements do not have render methods.. like the layouts...
319 if(this[cntr](true) === false){
324 cn.render && cn.render(this[cntr](true));
327 // then add the element..
334 if (typeof (tree.menu) != 'undefined') {
335 tree.menu.parentType = cn.xtype;
336 tree.menu.triggerEl = cn.el;
337 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
341 if (!tree.items || !tree.items.length) {
343 //Roo.log(["no children", this]);
348 var items = tree.items;
351 //Roo.log(items.length);
353 if (!skip_children) {
354 for(var i =0;i < items.length;i++) {
355 // Roo.log(['add child', items[i]]);
356 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
362 //Roo.log("fire childrenrendered");
364 cn.fireEvent('childrenrendered', this);
370 * Set the element that will be used to show or hide
372 setVisibilityEl : function(el)
374 this.visibilityEl = el;
378 * Get the element that will be used to show or hide
380 getVisibilityEl : function()
382 if (typeof(this.visibilityEl) == 'object') {
383 return this.visibilityEl;
386 if (typeof(this.visibilityEl) == 'string') {
387 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
394 * Show a component - removes 'hidden' class
398 if(!this.getVisibilityEl()){
402 this.getVisibilityEl().removeClass('hidden');
404 this.fireEvent('show', this);
409 * Hide a component - adds 'hidden' class
413 if(!this.getVisibilityEl()){
417 this.getVisibilityEl().addClass('hidden');
419 this.fireEvent('hide', this);
432 * @class Roo.bootstrap.Body
433 * @extends Roo.bootstrap.Component
434 * Bootstrap Body class
438 * @param {Object} config The config object
441 Roo.bootstrap.Body = function(config){
443 config = config || {};
445 Roo.bootstrap.Body.superclass.constructor.call(this, config);
446 this.el = Roo.get(config.el ? config.el : document.body );
447 if (this.cls && this.cls.length) {
448 Roo.get(document.body).addClass(this.cls);
452 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
454 is_body : true,// just to make sure it's constructed?
459 onRender : function(ct, position)
461 /* Roo.log("Roo.bootstrap.Body - onRender");
462 if (this.cls && this.cls.length) {
463 Roo.get(document.body).addClass(this.cls);
482 * @class Roo.bootstrap.ButtonGroup
483 * @extends Roo.bootstrap.Component
484 * Bootstrap ButtonGroup class
485 * @cfg {String} size lg | sm | xs (default empty normal)
486 * @cfg {String} align vertical | justified (default none)
487 * @cfg {String} direction up | down (default down)
488 * @cfg {Boolean} toolbar false | true
489 * @cfg {Boolean} btn true | false
494 * @param {Object} config The config object
497 Roo.bootstrap.ButtonGroup = function(config){
498 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
501 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
509 getAutoCreate : function(){
515 cfg.html = this.html || cfg.html;
526 if (['vertical','justified'].indexOf(this.align)!==-1) {
527 cfg.cls = 'btn-group-' + this.align;
529 if (this.align == 'justified') {
530 console.log(this.items);
534 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
535 cfg.cls += ' btn-group-' + this.size;
538 if (this.direction == 'up') {
539 cfg.cls += ' dropup' ;
555 * @class Roo.bootstrap.Button
556 * @extends Roo.bootstrap.Component
557 * Bootstrap Button class
558 * @cfg {String} html The button content
559 * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default
560 * @cfg {String} size ( lg | sm | xs)
561 * @cfg {String} tag ( a | input | submit)
562 * @cfg {String} href empty or href
563 * @cfg {Boolean} disabled default false;
564 * @cfg {Boolean} isClose default false;
565 * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
566 * @cfg {String} badge text for badge
567 * @cfg {String} theme (default|glow)
568 * @cfg {Boolean} inverse dark themed version
569 * @cfg {Boolean} toggle is it a slidy toggle button
570 * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
571 * @cfg {String} ontext text for on slidy toggle state
572 * @cfg {String} offtext text for off slidy toggle state
573 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
574 * @cfg {Boolean} removeClass remove the standard class..
575 * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
578 * Create a new button
579 * @param {Object} config The config object
583 Roo.bootstrap.Button = function(config){
584 Roo.bootstrap.Button.superclass.constructor.call(this, config);
585 this.weightClass = ["btn-default",
597 * When a butotn is pressed
598 * @param {Roo.bootstrap.Button} btn
599 * @param {Roo.EventObject} e
604 * After the button has been toggles
605 * @param {Roo.bootstrap.Button} btn
606 * @param {Roo.EventObject} e
607 * @param {boolean} pressed (also available as button.pressed)
613 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
631 preventDefault: true,
639 getAutoCreate : function(){
647 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
648 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
653 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
655 if (this.toggle == true) {
658 cls: 'slider-frame roo-button',
663 'data-off-text':'OFF',
664 cls: 'slider-button',
670 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
671 cfg.cls += ' '+this.weight;
680 cfg["aria-hidden"] = true;
682 cfg.html = "×";
688 if (this.theme==='default') {
689 cfg.cls = 'btn roo-button';
691 //if (this.parentType != 'Navbar') {
692 this.weight = this.weight.length ? this.weight : 'default';
694 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
696 cfg.cls += ' btn-' + this.weight;
698 } else if (this.theme==='glow') {
701 cfg.cls = 'btn-glow roo-button';
703 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
705 cfg.cls += ' ' + this.weight;
711 this.cls += ' inverse';
715 if (this.active || this.pressed === true) {
716 cfg.cls += ' active';
720 cfg.disabled = 'disabled';
724 Roo.log('changing to ul' );
726 this.glyphicon = 'caret';
729 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
731 //gsRoo.log(this.parentType);
732 if (this.parentType === 'Navbar' && !this.parent().bar) {
733 Roo.log('changing to li?');
742 href : this.href || '#'
745 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
746 cfg.cls += ' dropdown';
753 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
755 if (this.glyphicon) {
756 cfg.html = ' ' + cfg.html;
761 cls: 'glyphicon glyphicon-' + this.glyphicon
771 // cfg.cls='btn roo-button';
775 var value = cfg.html;
780 cls: 'glyphicon glyphicon-' + this.glyphicon,
799 cfg.cls += ' dropdown';
800 cfg.html = typeof(cfg.html) != 'undefined' ?
801 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
804 if (cfg.tag !== 'a' && this.href !== '') {
805 throw "Tag must be a to set href.";
806 } else if (this.href.length > 0) {
807 cfg.href = this.href;
810 if(this.removeClass){
815 cfg.target = this.target;
820 initEvents: function() {
821 // Roo.log('init events?');
822 // Roo.log(this.el.dom);
825 if (typeof (this.menu) != 'undefined') {
826 this.menu.parentType = this.xtype;
827 this.menu.triggerEl = this.el;
828 this.addxtype(Roo.apply({}, this.menu));
832 if (this.el.hasClass('roo-button')) {
833 this.el.on('click', this.onClick, this);
835 this.el.select('.roo-button').on('click', this.onClick, this);
838 if(this.removeClass){
839 this.el.on('click', this.onClick, this);
842 this.el.enableDisplayMode();
845 onClick : function(e)
851 Roo.log('button on click ');
852 if(this.preventDefault){
856 if (this.pressed === true || this.pressed === false) {
857 this.toggleActive(e);
861 this.fireEvent('click', this, e);
865 * Enables this button
869 this.disabled = false;
870 this.el.removeClass('disabled');
874 * Disable this button
878 this.disabled = true;
879 this.el.addClass('disabled');
882 * sets the active state on/off,
883 * @param {Boolean} state (optional) Force a particular state
885 setActive : function(v) {
887 this.el[v ? 'addClass' : 'removeClass']('active');
891 * toggles the current active state
893 toggleActive : function(e)
895 this.setActive(!this.pressed);
896 this.fireEvent('toggle', this, e, !this.pressed);
899 * get the current active state
900 * @return {boolean} true if it's active
902 isActive : function()
904 return this.el.hasClass('active');
907 * set the text of the first selected button
909 setText : function(str)
911 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
914 * get the text of the first selected button
918 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
929 setWeight : function(str)
931 this.el.removeClass(this.weightClass);
932 this.el.addClass('btn-' + str);
946 * @class Roo.bootstrap.Column
947 * @extends Roo.bootstrap.Component
948 * Bootstrap Column class
949 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
950 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
951 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
952 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
953 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
954 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
955 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
956 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
959 * @cfg {Boolean} hidden (true|false) hide the element
960 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
961 * @cfg {String} fa (ban|check|...) font awesome icon
962 * @cfg {Number} fasize (1|2|....) font awsome size
964 * @cfg {String} icon (info-sign|check|...) glyphicon name
966 * @cfg {String} html content of column.
969 * Create a new Column
970 * @param {Object} config The config object
973 Roo.bootstrap.Column = function(config){
974 Roo.bootstrap.Column.superclass.constructor.call(this, config);
977 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
995 getAutoCreate : function(){
996 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1004 ['xs','sm','md','lg'].map(function(size){
1005 //Roo.log( size + ':' + settings[size]);
1007 if (settings[size+'off'] !== false) {
1008 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1011 if (settings[size] === false) {
1015 if (!settings[size]) { // 0 = hidden
1016 cfg.cls += ' hidden-' + size;
1019 cfg.cls += ' col-' + size + '-' + settings[size];
1024 cfg.cls += ' hidden';
1027 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1028 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.html.length) {
1033 cfg.html = this.html;
1037 if (this.fasize > 1) {
1038 fasize = ' fa-' + this.fasize + 'x';
1040 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1045 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1064 * @class Roo.bootstrap.Container
1065 * @extends Roo.bootstrap.Component
1066 * Bootstrap Container class
1067 * @cfg {Boolean} jumbotron is it a jumbotron element
1068 * @cfg {String} html content of element
1069 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1070 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1071 * @cfg {String} header content of header (for panel)
1072 * @cfg {String} footer content of footer (for panel)
1073 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1074 * @cfg {String} tag (header|aside|section) type of HTML tag.
1075 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1076 * @cfg {String} fa font awesome icon
1077 * @cfg {String} icon (info-sign|check|...) glyphicon name
1078 * @cfg {Boolean} hidden (true|false) hide the element
1079 * @cfg {Boolean} expandable (true|false) default false
1080 * @cfg {Boolean} expanded (true|false) default true
1081 * @cfg {String} rheader contet on the right of header
1082 * @cfg {Boolean} clickable (true|false) default false
1086 * Create a new Container
1087 * @param {Object} config The config object
1090 Roo.bootstrap.Container = function(config){
1091 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1097 * After the panel has been expand
1099 * @param {Roo.bootstrap.Container} this
1104 * After the panel has been collapsed
1106 * @param {Roo.bootstrap.Container} this
1111 * When a element is chick
1112 * @param {Roo.bootstrap.Container} this
1113 * @param {Roo.EventObject} e
1119 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1137 getChildContainer : function() {
1143 if (this.panel.length) {
1144 return this.el.select('.panel-body',true).first();
1151 getAutoCreate : function(){
1154 tag : this.tag || 'div',
1158 if (this.jumbotron) {
1159 cfg.cls = 'jumbotron';
1164 // - this is applied by the parent..
1166 // cfg.cls = this.cls + '';
1169 if (this.sticky.length) {
1171 var bd = Roo.get(document.body);
1172 if (!bd.hasClass('bootstrap-sticky')) {
1173 bd.addClass('bootstrap-sticky');
1174 Roo.select('html',true).setStyle('height', '100%');
1177 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1181 if (this.well.length) {
1182 switch (this.well) {
1185 cfg.cls +=' well well-' +this.well;
1194 cfg.cls += ' hidden';
1198 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1199 cfg.cls +=' alert alert-' + this.alert;
1204 if (this.panel.length) {
1205 cfg.cls += ' panel panel-' + this.panel;
1207 if (this.header.length) {
1211 if(this.expandable){
1213 cfg.cls = cfg.cls + ' expandable';
1217 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1225 cls : 'panel-title',
1226 html : (this.expandable ? ' ' : '') + this.header
1230 cls: 'panel-header-right',
1236 cls : 'panel-heading',
1237 style : this.expandable ? 'cursor: pointer' : '',
1245 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1250 if (this.footer.length) {
1252 cls : 'panel-footer',
1261 body.html = this.html || cfg.html;
1262 // prefix with the icons..
1264 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1267 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1272 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1273 cfg.cls = 'container';
1279 initEvents: function()
1281 if(this.expandable){
1282 var headerEl = this.headerEl();
1285 headerEl.on('click', this.onToggleClick, this);
1290 this.el.on('click', this.onClick, this);
1295 onToggleClick : function()
1297 var headerEl = this.headerEl();
1313 if(this.fireEvent('expand', this)) {
1315 this.expanded = true;
1317 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1319 this.el.select('.panel-body',true).first().removeClass('hide');
1321 var toggleEl = this.toggleEl();
1327 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1332 collapse : function()
1334 if(this.fireEvent('collapse', this)) {
1336 this.expanded = false;
1338 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1339 this.el.select('.panel-body',true).first().addClass('hide');
1341 var toggleEl = this.toggleEl();
1347 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1351 toggleEl : function()
1353 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1357 return this.el.select('.panel-heading .fa',true).first();
1360 headerEl : function()
1362 if(!this.el || !this.panel.length || !this.header.length){
1366 return this.el.select('.panel-heading',true).first()
1371 if(!this.el || !this.panel.length){
1375 return this.el.select('.panel-body',true).first()
1378 titleEl : function()
1380 if(!this.el || !this.panel.length || !this.header.length){
1384 return this.el.select('.panel-title',true).first();
1387 setTitle : function(v)
1389 var titleEl = this.titleEl();
1395 titleEl.dom.innerHTML = v;
1398 getTitle : function()
1401 var titleEl = this.titleEl();
1407 return titleEl.dom.innerHTML;
1410 setRightTitle : function(v)
1412 var t = this.el.select('.panel-header-right',true).first();
1418 t.dom.innerHTML = v;
1421 onClick : function(e)
1425 this.fireEvent('click', this, e);
1438 * @class Roo.bootstrap.Img
1439 * @extends Roo.bootstrap.Component
1440 * Bootstrap Img class
1441 * @cfg {Boolean} imgResponsive false | true
1442 * @cfg {String} border rounded | circle | thumbnail
1443 * @cfg {String} src image source
1444 * @cfg {String} alt image alternative text
1445 * @cfg {String} href a tag href
1446 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1447 * @cfg {String} xsUrl xs image source
1448 * @cfg {String} smUrl sm image source
1449 * @cfg {String} mdUrl md image source
1450 * @cfg {String} lgUrl lg image source
1453 * Create a new Input
1454 * @param {Object} config The config object
1457 Roo.bootstrap.Img = function(config){
1458 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1464 * The img click event for the img.
1465 * @param {Roo.EventObject} e
1471 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1473 imgResponsive: true,
1483 getAutoCreate : function()
1485 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1486 return this.createSingleImg();
1491 cls: 'roo-image-responsive-group',
1496 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1498 if(!_this[size + 'Url']){
1504 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1505 html: _this.html || cfg.html,
1506 src: _this[size + 'Url']
1509 img.cls += ' roo-image-responsive-' + size;
1511 var s = ['xs', 'sm', 'md', 'lg'];
1513 s.splice(s.indexOf(size), 1);
1515 Roo.each(s, function(ss){
1516 img.cls += ' hidden-' + ss;
1519 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1520 cfg.cls += ' img-' + _this.border;
1524 cfg.alt = _this.alt;
1537 a.target = _this.target;
1541 cfg.cn.push((_this.href) ? a : img);
1548 createSingleImg : function()
1552 cls: (this.imgResponsive) ? 'img-responsive' : '',
1554 src : 'about:blank' // just incase src get's set to undefined?!?
1557 cfg.html = this.html || cfg.html;
1559 cfg.src = this.src || cfg.src;
1561 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1562 cfg.cls += ' img-' + this.border;
1579 a.target = this.target;
1584 return (this.href) ? a : cfg;
1587 initEvents: function()
1590 this.el.on('click', this.onClick, this);
1595 onClick : function(e)
1597 Roo.log('img onclick');
1598 this.fireEvent('click', this, e);
1601 * Sets the url of the image - used to update it
1602 * @param {String} url the url of the image
1605 setSrc : function(url)
1609 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1610 this.el.dom.src = url;
1614 this.el.select('img', true).first().dom.src = url;
1630 * @class Roo.bootstrap.Link
1631 * @extends Roo.bootstrap.Component
1632 * Bootstrap Link Class
1633 * @cfg {String} alt image alternative text
1634 * @cfg {String} href a tag href
1635 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1636 * @cfg {String} html the content of the link.
1637 * @cfg {String} anchor name for the anchor link
1638 * @cfg {String} fa - favicon
1640 * @cfg {Boolean} preventDefault (true | false) default false
1644 * Create a new Input
1645 * @param {Object} config The config object
1648 Roo.bootstrap.Link = function(config){
1649 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1655 * The img click event for the img.
1656 * @param {Roo.EventObject} e
1662 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1666 preventDefault: false,
1672 getAutoCreate : function()
1674 var html = this.html || '';
1676 if (this.fa !== false) {
1677 html = '<i class="fa fa-' + this.fa + '"></i>';
1682 // anchor's do not require html/href...
1683 if (this.anchor === false) {
1685 cfg.href = this.href || '#';
1687 cfg.name = this.anchor;
1688 if (this.html !== false || this.fa !== false) {
1691 if (this.href !== false) {
1692 cfg.href = this.href;
1696 if(this.alt !== false){
1701 if(this.target !== false) {
1702 cfg.target = this.target;
1708 initEvents: function() {
1710 if(!this.href || this.preventDefault){
1711 this.el.on('click', this.onClick, this);
1715 onClick : function(e)
1717 if(this.preventDefault){
1720 //Roo.log('img onclick');
1721 this.fireEvent('click', this, e);
1734 * @class Roo.bootstrap.Header
1735 * @extends Roo.bootstrap.Component
1736 * Bootstrap Header class
1737 * @cfg {String} html content of header
1738 * @cfg {Number} level (1|2|3|4|5|6) default 1
1741 * Create a new Header
1742 * @param {Object} config The config object
1746 Roo.bootstrap.Header = function(config){
1747 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1750 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1758 getAutoCreate : function(){
1763 tag: 'h' + (1 *this.level),
1764 html: this.html || ''
1776 * Ext JS Library 1.1.1
1777 * Copyright(c) 2006-2007, Ext JS, LLC.
1779 * Originally Released Under LGPL - original licence link has changed is not relivant.
1782 * <script type="text/javascript">
1786 * @class Roo.bootstrap.MenuMgr
1787 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1790 Roo.bootstrap.MenuMgr = function(){
1791 var menus, active, groups = {}, attached = false, lastShow = new Date();
1793 // private - called when first menu is created
1796 active = new Roo.util.MixedCollection();
1797 Roo.get(document).addKeyListener(27, function(){
1798 if(active.length > 0){
1806 if(active && active.length > 0){
1807 var c = active.clone();
1817 if(active.length < 1){
1818 Roo.get(document).un("mouseup", onMouseDown);
1826 var last = active.last();
1827 lastShow = new Date();
1830 Roo.get(document).on("mouseup", onMouseDown);
1835 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1836 m.parentMenu.activeChild = m;
1837 }else if(last && last.isVisible()){
1838 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1843 function onBeforeHide(m){
1845 m.activeChild.hide();
1847 if(m.autoHideTimer){
1848 clearTimeout(m.autoHideTimer);
1849 delete m.autoHideTimer;
1854 function onBeforeShow(m){
1855 var pm = m.parentMenu;
1856 if(!pm && !m.allowOtherMenus){
1858 }else if(pm && pm.activeChild && active != m){
1859 pm.activeChild.hide();
1863 // private this should really trigger on mouseup..
1864 function onMouseDown(e){
1865 Roo.log("on Mouse Up");
1867 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1868 Roo.log("MenuManager hideAll");
1877 function onBeforeCheck(mi, state){
1879 var g = groups[mi.group];
1880 for(var i = 0, l = g.length; i < l; i++){
1882 g[i].setChecked(false);
1891 * Hides all menus that are currently visible
1893 hideAll : function(){
1898 register : function(menu){
1902 menus[menu.id] = menu;
1903 menu.on("beforehide", onBeforeHide);
1904 menu.on("hide", onHide);
1905 menu.on("beforeshow", onBeforeShow);
1906 menu.on("show", onShow);
1908 if(g && menu.events["checkchange"]){
1912 groups[g].push(menu);
1913 menu.on("checkchange", onCheck);
1918 * Returns a {@link Roo.menu.Menu} object
1919 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1920 * be used to generate and return a new Menu instance.
1922 get : function(menu){
1923 if(typeof menu == "string"){ // menu id
1925 }else if(menu.events){ // menu instance
1928 /*else if(typeof menu.length == 'number'){ // array of menu items?
1929 return new Roo.bootstrap.Menu({items:menu});
1930 }else{ // otherwise, must be a config
1931 return new Roo.bootstrap.Menu(menu);
1938 unregister : function(menu){
1939 delete menus[menu.id];
1940 menu.un("beforehide", onBeforeHide);
1941 menu.un("hide", onHide);
1942 menu.un("beforeshow", onBeforeShow);
1943 menu.un("show", onShow);
1945 if(g && menu.events["checkchange"]){
1946 groups[g].remove(menu);
1947 menu.un("checkchange", onCheck);
1952 registerCheckable : function(menuItem){
1953 var g = menuItem.group;
1958 groups[g].push(menuItem);
1959 menuItem.on("beforecheckchange", onBeforeCheck);
1964 unregisterCheckable : function(menuItem){
1965 var g = menuItem.group;
1967 groups[g].remove(menuItem);
1968 menuItem.un("beforecheckchange", onBeforeCheck);
1980 * @class Roo.bootstrap.Menu
1981 * @extends Roo.bootstrap.Component
1982 * Bootstrap Menu class - container for MenuItems
1983 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1984 * @cfg {bool} hidden if the menu should be hidden when rendered.
1985 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
1986 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
1990 * @param {Object} config The config object
1994 Roo.bootstrap.Menu = function(config){
1995 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1996 if (this.registerMenu && this.type != 'treeview') {
1997 Roo.bootstrap.MenuMgr.register(this);
2002 * Fires before this menu is displayed
2003 * @param {Roo.menu.Menu} this
2008 * Fires before this menu is hidden
2009 * @param {Roo.menu.Menu} this
2014 * Fires after this menu is displayed
2015 * @param {Roo.menu.Menu} this
2020 * Fires after this menu is hidden
2021 * @param {Roo.menu.Menu} this
2026 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2027 * @param {Roo.menu.Menu} this
2028 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2029 * @param {Roo.EventObject} e
2034 * Fires when the mouse is hovering over this menu
2035 * @param {Roo.menu.Menu} this
2036 * @param {Roo.EventObject} e
2037 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2042 * Fires when the mouse exits this menu
2043 * @param {Roo.menu.Menu} this
2044 * @param {Roo.EventObject} e
2045 * @param {Roo.menu.Item} menuItem The menu item that was clicked
2050 * Fires when a menu item contained in this menu is clicked
2051 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2052 * @param {Roo.EventObject} e
2056 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2059 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
2063 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
2066 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2068 registerMenu : true,
2070 menuItems :false, // stores the menu items..
2080 getChildContainer : function() {
2084 getAutoCreate : function(){
2086 //if (['right'].indexOf(this.align)!==-1) {
2087 // cfg.cn[1].cls += ' pull-right'
2093 cls : 'dropdown-menu' ,
2094 style : 'z-index:1000'
2098 if (this.type === 'submenu') {
2099 cfg.cls = 'submenu active';
2101 if (this.type === 'treeview') {
2102 cfg.cls = 'treeview-menu';
2107 initEvents : function() {
2109 // Roo.log("ADD event");
2110 // Roo.log(this.triggerEl.dom);
2112 this.triggerEl.on('click', this.onTriggerClick, this);
2114 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2116 this.triggerEl.addClass('dropdown-toggle');
2119 this.el.on('touchstart' , this.onTouch, this);
2121 this.el.on('click' , this.onClick, this);
2123 this.el.on("mouseover", this.onMouseOver, this);
2124 this.el.on("mouseout", this.onMouseOut, this);
2128 findTargetItem : function(e)
2130 var t = e.getTarget(".dropdown-menu-item", this.el, true);
2134 //Roo.log(t); Roo.log(t.id);
2136 //Roo.log(this.menuitems);
2137 return this.menuitems.get(t.id);
2139 //return this.items.get(t.menuItemId);
2145 onTouch : function(e)
2147 Roo.log("menu.onTouch");
2148 //e.stopEvent(); this make the user popdown broken
2152 onClick : function(e)
2154 Roo.log("menu.onClick");
2156 var t = this.findTargetItem(e);
2157 if(!t || t.isContainer){
2162 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
2163 if(t == this.activeItem && t.shouldDeactivate(e)){
2164 this.activeItem.deactivate();
2165 delete this.activeItem;
2169 this.setActiveItem(t, true);
2177 Roo.log('pass click event');
2181 this.fireEvent("click", this, t, e);
2185 if(!t.href.length || t.href == '#'){
2186 (function() { _this.hide(); }).defer(100);
2191 onMouseOver : function(e){
2192 var t = this.findTargetItem(e);
2195 // if(t.canActivate && !t.disabled){
2196 // this.setActiveItem(t, true);
2200 this.fireEvent("mouseover", this, e, t);
2202 isVisible : function(){
2203 return !this.hidden;
2205 onMouseOut : function(e){
2206 var t = this.findTargetItem(e);
2209 // if(t == this.activeItem && t.shouldDeactivate(e)){
2210 // this.activeItem.deactivate();
2211 // delete this.activeItem;
2214 this.fireEvent("mouseout", this, e, t);
2219 * Displays this menu relative to another element
2220 * @param {String/HTMLElement/Roo.Element} element The element to align to
2221 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2222 * the element (defaults to this.defaultAlign)
2223 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2225 show : function(el, pos, parentMenu){
2226 this.parentMenu = parentMenu;
2230 this.fireEvent("beforeshow", this);
2231 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2234 * Displays this menu at a specific xy position
2235 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2236 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2238 showAt : function(xy, parentMenu, /* private: */_e){
2239 this.parentMenu = parentMenu;
2244 this.fireEvent("beforeshow", this);
2245 //xy = this.el.adjustForConstraints(xy);
2249 this.hideMenuItems();
2250 this.hidden = false;
2251 this.triggerEl.addClass('open');
2253 // reassign x when hitting right
2254 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2255 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2258 // reassign y when hitting bottom
2259 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2260 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2263 // but the list may align on trigger left or trigger top... should it be a properity?
2265 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2270 this.fireEvent("show", this);
2276 this.doFocus.defer(50, this);
2280 doFocus : function(){
2282 this.focusEl.focus();
2287 * Hides this menu and optionally all parent menus
2288 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2290 hide : function(deep)
2293 this.hideMenuItems();
2294 if(this.el && this.isVisible()){
2295 this.fireEvent("beforehide", this);
2296 if(this.activeItem){
2297 this.activeItem.deactivate();
2298 this.activeItem = null;
2300 this.triggerEl.removeClass('open');;
2302 this.fireEvent("hide", this);
2304 if(deep === true && this.parentMenu){
2305 this.parentMenu.hide(true);
2309 onTriggerClick : function(e)
2311 Roo.log('trigger click');
2313 var target = e.getTarget();
2315 Roo.log(target.nodeName.toLowerCase());
2317 if(target.nodeName.toLowerCase() === 'i'){
2323 onTriggerPress : function(e)
2325 Roo.log('trigger press');
2326 //Roo.log(e.getTarget());
2327 // Roo.log(this.triggerEl.dom);
2329 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2330 var pel = Roo.get(e.getTarget());
2331 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2332 Roo.log('is treeview or dropdown?');
2336 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2340 if (this.isVisible()) {
2345 this.show(this.triggerEl, false, false);
2348 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2355 hideMenuItems : function()
2357 Roo.log("hide Menu Items");
2361 //$(backdrop).remove()
2362 this.el.select('.open',true).each(function(aa) {
2364 aa.removeClass('open');
2365 //var parent = getParent($(this))
2366 //var relatedTarget = { relatedTarget: this }
2368 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2369 //if (e.isDefaultPrevented()) return
2370 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2373 addxtypeChild : function (tree, cntr) {
2374 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2376 this.menuitems.add(comp);
2388 this.getEl().dom.innerHTML = '';
2389 this.menuitems.clear();
2403 * @class Roo.bootstrap.MenuItem
2404 * @extends Roo.bootstrap.Component
2405 * Bootstrap MenuItem class
2406 * @cfg {String} html the menu label
2407 * @cfg {String} href the link
2408 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2409 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2410 * @cfg {Boolean} active used on sidebars to highlight active itesm
2411 * @cfg {String} fa favicon to show on left of menu item.
2412 * @cfg {Roo.bootsrap.Menu} menu the child menu.
2416 * Create a new MenuItem
2417 * @param {Object} config The config object
2421 Roo.bootstrap.MenuItem = function(config){
2422 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2427 * The raw click event for the entire grid.
2428 * @param {Roo.bootstrap.MenuItem} this
2429 * @param {Roo.EventObject} e
2435 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
2439 preventDefault: false,
2440 isContainer : false,
2444 getAutoCreate : function(){
2446 if(this.isContainer){
2449 cls: 'dropdown-menu-item'
2463 if (this.fa !== false) {
2466 cls : 'fa fa-' + this.fa
2475 cls: 'dropdown-menu-item',
2478 if (this.parent().type == 'treeview') {
2479 cfg.cls = 'treeview-menu';
2482 cfg.cls += ' active';
2487 anc.href = this.href || cfg.cn[0].href ;
2488 ctag.html = this.html || cfg.cn[0].html ;
2492 initEvents: function()
2494 if (this.parent().type == 'treeview') {
2495 this.el.select('a').on('click', this.onClick, this);
2499 this.menu.parentType = this.xtype;
2500 this.menu.triggerEl = this.el;
2501 this.menu = this.addxtype(Roo.apply({}, this.menu));
2505 onClick : function(e)
2507 Roo.log('item on click ');
2509 if(this.preventDefault){
2512 //this.parent().hideMenuItems();
2514 this.fireEvent('click', this, e);
2533 * @class Roo.bootstrap.MenuSeparator
2534 * @extends Roo.bootstrap.Component
2535 * Bootstrap MenuSeparator class
2538 * Create a new MenuItem
2539 * @param {Object} config The config object
2543 Roo.bootstrap.MenuSeparator = function(config){
2544 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2547 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2549 getAutoCreate : function(){
2568 * @class Roo.bootstrap.Modal
2569 * @extends Roo.bootstrap.Component
2570 * Bootstrap Modal class
2571 * @cfg {String} title Title of dialog
2572 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2573 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
2574 * @cfg {Boolean} specificTitle default false
2575 * @cfg {Array} buttons Array of buttons or standard button set..
2576 * @cfg {String} buttonPosition (left|right|center) default right
2577 * @cfg {Boolean} animate default true
2578 * @cfg {Boolean} allow_close default true
2579 * @cfg {Boolean} fitwindow default false
2580 * @cfg {String} size (sm|lg) default empty
2581 * @cfg {Number} max_width set the max width of modal
2585 * Create a new Modal Dialog
2586 * @param {Object} config The config object
2589 Roo.bootstrap.Modal = function(config){
2590 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2595 * The raw btnclick event for the button
2596 * @param {Roo.EventObject} e
2601 * Fire when dialog resize
2602 * @param {Roo.bootstrap.Modal} this
2603 * @param {Roo.EventObject} e
2607 this.buttons = this.buttons || [];
2610 this.tmpl = Roo.factory(this.tmpl);
2615 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2617 title : 'test dialog',
2627 specificTitle: false,
2629 buttonPosition: 'right',
2650 onRender : function(ct, position)
2652 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2655 var cfg = Roo.apply({}, this.getAutoCreate());
2658 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2660 //if (!cfg.name.length) {
2664 cfg.cls += ' ' + this.cls;
2667 cfg.style = this.style;
2669 this.el = Roo.get(document.body).createChild(cfg, position);
2671 //var type = this.el.dom.type;
2674 if(this.tabIndex !== undefined){
2675 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2678 this.dialogEl = this.el.select('.modal-dialog',true).first();
2679 this.bodyEl = this.el.select('.modal-body',true).first();
2680 this.closeEl = this.el.select('.modal-header .close', true).first();
2681 this.headerEl = this.el.select('.modal-header',true).first();
2682 this.titleEl = this.el.select('.modal-title',true).first();
2683 this.footerEl = this.el.select('.modal-footer',true).first();
2685 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2687 //this.el.addClass("x-dlg-modal");
2689 if (this.buttons.length) {
2690 Roo.each(this.buttons, function(bb) {
2691 var b = Roo.apply({}, bb);
2692 b.xns = b.xns || Roo.bootstrap;
2693 b.xtype = b.xtype || 'Button';
2694 if (typeof(b.listeners) == 'undefined') {
2695 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2698 var btn = Roo.factory(b);
2700 btn.render(this.el.select('.modal-footer div').first());
2704 // render the children.
2707 if(typeof(this.items) != 'undefined'){
2708 var items = this.items;
2711 for(var i =0;i < items.length;i++) {
2712 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2716 this.items = nitems;
2718 // where are these used - they used to be body/close/footer
2722 //this.el.addClass([this.fieldClass, this.cls]);
2726 getAutoCreate : function()
2730 html : this.html || ''
2735 cls : 'modal-title',
2739 if(this.specificTitle){
2745 if (this.allow_close) {
2757 if(this.size.length){
2758 size = 'modal-' + this.size;
2765 cls: "modal-dialog " + size,
2768 cls : "modal-content",
2771 cls : 'modal-header',
2776 cls : 'modal-footer',
2780 cls: 'btn-' + this.buttonPosition
2797 modal.cls += ' fade';
2803 getChildContainer : function() {
2808 getButtonContainer : function() {
2809 return this.el.select('.modal-footer div',true).first();
2812 initEvents : function()
2814 if (this.allow_close) {
2815 this.closeEl.on('click', this.hide, this);
2817 Roo.EventManager.onWindowResize(this.resize, this, true);
2824 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2826 if (this.fitwindow) {
2827 var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2828 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2832 if(!this.fitwindow && this.max_width !== 0){
2834 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2835 var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2841 setSize : function(w,h)
2851 if (!this.rendered) {
2855 //this.el.setStyle('display', 'block');
2856 this.el.removeClass('hideing');
2857 this.el.addClass('show');
2859 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2862 this.el.addClass('in');
2865 this.el.addClass('in');
2868 // not sure how we can show data in here..
2870 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2873 Roo.get(document.body).addClass("x-body-masked");
2875 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2876 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2877 this.maskEl.addClass('show');
2881 this.fireEvent('show', this);
2883 // set zindex here - otherwise it appears to be ignored...
2884 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2887 this.items.forEach( function(e) {
2888 e.layout ? e.layout() : false;
2896 if(this.fireEvent("beforehide", this) !== false){
2897 this.maskEl.removeClass('show');
2898 Roo.get(document.body).removeClass("x-body-masked");
2899 this.el.removeClass('in');
2900 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2902 if(this.animate){ // why
2903 this.el.addClass('hideing');
2905 if (!this.el.hasClass('hideing')) {
2906 return; // it's been shown again...
2908 this.el.removeClass('show');
2909 this.el.removeClass('hideing');
2913 this.el.removeClass('show');
2915 this.fireEvent('hide', this);
2918 isVisible : function()
2921 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2925 addButton : function(str, cb)
2929 var b = Roo.apply({}, { html : str } );
2930 b.xns = b.xns || Roo.bootstrap;
2931 b.xtype = b.xtype || 'Button';
2932 if (typeof(b.listeners) == 'undefined') {
2933 b.listeners = { click : cb.createDelegate(this) };
2936 var btn = Roo.factory(b);
2938 btn.render(this.el.select('.modal-footer div').first());
2944 setDefaultButton : function(btn)
2946 //this.el.select('.modal-footer').()
2950 resizeTo: function(w,h)
2954 this.dialogEl.setWidth(w);
2955 if (this.diff === false) {
2956 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2959 this.bodyEl.setHeight(h-this.diff);
2961 this.fireEvent('resize', this);
2964 setContentSize : function(w, h)
2968 onButtonClick: function(btn,e)
2971 this.fireEvent('btnclick', btn.name, e);
2974 * Set the title of the Dialog
2975 * @param {String} str new Title
2977 setTitle: function(str) {
2978 this.titleEl.dom.innerHTML = str;
2981 * Set the body of the Dialog
2982 * @param {String} str new Title
2984 setBody: function(str) {
2985 this.bodyEl.dom.innerHTML = str;
2988 * Set the body of the Dialog using the template
2989 * @param {Obj} data - apply this data to the template and replace the body contents.
2991 applyBody: function(obj)
2994 Roo.log("Error - using apply Body without a template");
2997 this.tmpl.overwrite(this.bodyEl, obj);
3003 Roo.apply(Roo.bootstrap.Modal, {
3005 * Button config that displays a single OK button
3014 * Button config that displays Yes and No buttons
3030 * Button config that displays OK and Cancel buttons
3045 * Button config that displays Yes, No and Cancel buttons
3069 * messagebox - can be used as a replace
3073 * @class Roo.MessageBox
3074 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3078 Roo.Msg.alert('Status', 'Changes saved successfully.');
3080 // Prompt for user data:
3081 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3083 // process text value...
3087 // Show a dialog using config options:
3089 title:'Save Changes?',
3090 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3091 buttons: Roo.Msg.YESNOCANCEL,
3098 Roo.bootstrap.MessageBox = function(){
3099 var dlg, opt, mask, waitTimer;
3100 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3101 var buttons, activeTextEl, bwidth;
3105 var handleButton = function(button){
3107 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3111 var handleHide = function(){
3113 dlg.el.removeClass(opt.cls);
3116 // Roo.TaskMgr.stop(waitTimer);
3117 // waitTimer = null;
3122 var updateButtons = function(b){
3125 buttons["ok"].hide();
3126 buttons["cancel"].hide();
3127 buttons["yes"].hide();
3128 buttons["no"].hide();
3129 //dlg.footer.dom.style.display = 'none';
3132 dlg.footerEl.dom.style.display = '';
3133 for(var k in buttons){
3134 if(typeof buttons[k] != "function"){
3137 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3138 width += buttons[k].el.getWidth()+15;
3148 var handleEsc = function(d, k, e){
3149 if(opt && opt.closable !== false){
3159 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3160 * @return {Roo.BasicDialog} The BasicDialog element
3162 getDialog : function(){
3164 dlg = new Roo.bootstrap.Modal( {
3167 //constraintoviewport:false,
3169 //collapsible : false,
3174 //buttonAlign:"center",
3175 closeClick : function(){
3176 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3179 handleButton("cancel");
3184 dlg.on("hide", handleHide);
3186 //dlg.addKeyListener(27, handleEsc);
3188 this.buttons = buttons;
3189 var bt = this.buttonText;
3190 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3191 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3192 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3193 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3195 bodyEl = dlg.bodyEl.createChild({
3197 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3198 '<textarea class="roo-mb-textarea"></textarea>' +
3199 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3201 msgEl = bodyEl.dom.firstChild;
3202 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3203 textboxEl.enableDisplayMode();
3204 textboxEl.addKeyListener([10,13], function(){
3205 if(dlg.isVisible() && opt && opt.buttons){
3208 }else if(opt.buttons.yes){
3209 handleButton("yes");
3213 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3214 textareaEl.enableDisplayMode();
3215 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3216 progressEl.enableDisplayMode();
3218 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3219 var pf = progressEl.dom.firstChild;
3221 pp = Roo.get(pf.firstChild);
3222 pp.setHeight(pf.offsetHeight);
3230 * Updates the message box body text
3231 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3232 * the XHTML-compliant non-breaking space character '&#160;')
3233 * @return {Roo.MessageBox} This message box
3235 updateText : function(text)
3237 if(!dlg.isVisible() && !opt.width){
3238 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3239 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3241 msgEl.innerHTML = text || ' ';
3243 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3244 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3246 Math.min(opt.width || cw , this.maxWidth),
3247 Math.max(opt.minWidth || this.minWidth, bwidth)
3250 activeTextEl.setWidth(w);
3252 if(dlg.isVisible()){
3253 dlg.fixedcenter = false;
3255 // to big, make it scroll. = But as usual stupid IE does not support
3258 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3259 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3260 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3262 bodyEl.dom.style.height = '';
3263 bodyEl.dom.style.overflowY = '';
3266 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3268 bodyEl.dom.style.overflowX = '';
3271 dlg.setContentSize(w, bodyEl.getHeight());
3272 if(dlg.isVisible()){
3273 dlg.fixedcenter = true;
3279 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3280 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3281 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3282 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3283 * @return {Roo.MessageBox} This message box
3285 updateProgress : function(value, text){
3287 this.updateText(text);
3290 if (pp) { // weird bug on my firefox - for some reason this is not defined
3291 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3292 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3298 * Returns true if the message box is currently displayed
3299 * @return {Boolean} True if the message box is visible, else false
3301 isVisible : function(){
3302 return dlg && dlg.isVisible();
3306 * Hides the message box if it is displayed
3309 if(this.isVisible()){
3315 * Displays a new message box, or reinitializes an existing message box, based on the config options
3316 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3317 * The following config object properties are supported:
3319 Property Type Description
3320 ---------- --------------- ------------------------------------------------------------------------------------
3321 animEl String/Element An id or Element from which the message box should animate as it opens and
3322 closes (defaults to undefined)
3323 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3324 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3325 closable Boolean False to hide the top-right close button (defaults to true). Note that
3326 progress and wait dialogs will ignore this property and always hide the
3327 close button as they can only be closed programmatically.
3328 cls String A custom CSS class to apply to the message box element
3329 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3330 displayed (defaults to 75)
3331 fn Function A callback function to execute after closing the dialog. The arguments to the
3332 function will be btn (the name of the button that was clicked, if applicable,
3333 e.g. "ok"), and text (the value of the active text field, if applicable).
3334 Progress and wait dialogs will ignore this option since they do not respond to
3335 user actions and can only be closed programmatically, so any required function
3336 should be called by the same code after it closes the dialog.
3337 icon String A CSS class that provides a background image to be used as an icon for
3338 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3339 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3340 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3341 modal Boolean False to allow user interaction with the page while the message box is
3342 displayed (defaults to true)
3343 msg String A string that will replace the existing message box body text (defaults
3344 to the XHTML-compliant non-breaking space character ' ')
3345 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3346 progress Boolean True to display a progress bar (defaults to false)
3347 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3348 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3349 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3350 title String The title text
3351 value String The string value to set into the active textbox element if displayed
3352 wait Boolean True to display a progress bar (defaults to false)
3353 width Number The width of the dialog in pixels
3360 msg: 'Please enter your address:',
3362 buttons: Roo.MessageBox.OKCANCEL,
3365 animEl: 'addAddressBtn'
3368 * @param {Object} config Configuration options
3369 * @return {Roo.MessageBox} This message box
3371 show : function(options)
3374 // this causes nightmares if you show one dialog after another
3375 // especially on callbacks..
3377 if(this.isVisible()){
3380 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3381 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3382 Roo.log("New Dialog Message:" + options.msg )
3383 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3384 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3387 var d = this.getDialog();
3389 d.setTitle(opt.title || " ");
3390 d.closeEl.setDisplayed(opt.closable !== false);
3391 activeTextEl = textboxEl;
3392 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3397 textareaEl.setHeight(typeof opt.multiline == "number" ?
3398 opt.multiline : this.defaultTextHeight);
3399 activeTextEl = textareaEl;
3408 progressEl.setDisplayed(opt.progress === true);
3409 this.updateProgress(0);
3410 activeTextEl.dom.value = opt.value || "";
3412 dlg.setDefaultButton(activeTextEl);
3414 var bs = opt.buttons;
3418 }else if(bs && bs.yes){
3419 db = buttons["yes"];
3421 dlg.setDefaultButton(db);
3423 bwidth = updateButtons(opt.buttons);
3424 this.updateText(opt.msg);
3426 d.el.addClass(opt.cls);
3428 d.proxyDrag = opt.proxyDrag === true;
3429 d.modal = opt.modal !== false;
3430 d.mask = opt.modal !== false ? mask : false;
3432 // force it to the end of the z-index stack so it gets a cursor in FF
3433 document.body.appendChild(dlg.el.dom);
3434 d.animateTarget = null;
3435 d.show(options.animEl);
3441 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3442 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3443 * and closing the message box when the process is complete.
3444 * @param {String} title The title bar text
3445 * @param {String} msg The message box body text
3446 * @return {Roo.MessageBox} This message box
3448 progress : function(title, msg){
3455 minWidth: this.minProgressWidth,
3462 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3463 * If a callback function is passed it will be called after the user clicks the button, and the
3464 * id of the button that was clicked will be passed as the only parameter to the callback
3465 * (could also be the top-right close button).
3466 * @param {String} title The title bar text
3467 * @param {String} msg The message box body text
3468 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3469 * @param {Object} scope (optional) The scope of the callback function
3470 * @return {Roo.MessageBox} This message box
3472 alert : function(title, msg, fn, scope)
3487 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3488 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3489 * You are responsible for closing the message box when the process is complete.
3490 * @param {String} msg The message box body text
3491 * @param {String} title (optional) The title bar text
3492 * @return {Roo.MessageBox} This message box
3494 wait : function(msg, title){
3505 waitTimer = Roo.TaskMgr.start({
3507 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3515 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3516 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3517 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3518 * @param {String} title The title bar text
3519 * @param {String} msg The message box body text
3520 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3521 * @param {Object} scope (optional) The scope of the callback function
3522 * @return {Roo.MessageBox} This message box
3524 confirm : function(title, msg, fn, scope){
3528 buttons: this.YESNO,
3537 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3538 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3539 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3540 * (could also be the top-right close button) and the text that was entered will be passed as the two
3541 * parameters to the callback.
3542 * @param {String} title The title bar text
3543 * @param {String} msg The message box body text
3544 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3545 * @param {Object} scope (optional) The scope of the callback function
3546 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3547 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3548 * @return {Roo.MessageBox} This message box
3550 prompt : function(title, msg, fn, scope, multiline){
3554 buttons: this.OKCANCEL,
3559 multiline: multiline,
3566 * Button config that displays a single OK button
3571 * Button config that displays Yes and No buttons
3574 YESNO : {yes:true, no:true},
3576 * Button config that displays OK and Cancel buttons
3579 OKCANCEL : {ok:true, cancel:true},
3581 * Button config that displays Yes, No and Cancel buttons
3584 YESNOCANCEL : {yes:true, no:true, cancel:true},
3587 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3590 defaultTextHeight : 75,
3592 * The maximum width in pixels of the message box (defaults to 600)
3597 * The minimum width in pixels of the message box (defaults to 100)
3602 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3603 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3606 minProgressWidth : 250,
3608 * An object containing the default button text strings that can be overriden for localized language support.
3609 * Supported properties are: ok, cancel, yes and no.
3610 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3623 * Shorthand for {@link Roo.MessageBox}
3625 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3626 Roo.Msg = Roo.Msg || Roo.MessageBox;
3635 * @class Roo.bootstrap.Navbar
3636 * @extends Roo.bootstrap.Component
3637 * Bootstrap Navbar class
3640 * Create a new Navbar
3641 * @param {Object} config The config object
3645 Roo.bootstrap.Navbar = function(config){
3646 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3650 * @event beforetoggle
3651 * Fire before toggle the menu
3652 * @param {Roo.EventObject} e
3654 "beforetoggle" : true
3658 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3667 getAutoCreate : function(){
3670 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3674 initEvents :function ()
3676 //Roo.log(this.el.select('.navbar-toggle',true));
3677 this.el.select('.navbar-toggle',true).on('click', function() {
3678 if(this.fireEvent('beforetoggle', this) !== false){
3679 this.el.select('.navbar-collapse',true).toggleClass('in');
3689 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3691 var size = this.el.getSize();
3692 this.maskEl.setSize(size.width, size.height);
3693 this.maskEl.enableDisplayMode("block");
3702 getChildContainer : function()
3704 if (this.el.select('.collapse').getCount()) {
3705 return this.el.select('.collapse',true).first();
3738 * @class Roo.bootstrap.NavSimplebar
3739 * @extends Roo.bootstrap.Navbar
3740 * Bootstrap Sidebar class
3742 * @cfg {Boolean} inverse is inverted color
3744 * @cfg {String} type (nav | pills | tabs)
3745 * @cfg {Boolean} arrangement stacked | justified
3746 * @cfg {String} align (left | right) alignment
3748 * @cfg {Boolean} main (true|false) main nav bar? default false
3749 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3751 * @cfg {String} tag (header|footer|nav|div) default is nav
3757 * Create a new Sidebar
3758 * @param {Object} config The config object
3762 Roo.bootstrap.NavSimplebar = function(config){
3763 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3766 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3782 getAutoCreate : function(){
3786 tag : this.tag || 'div',
3799 this.type = this.type || 'nav';
3800 if (['tabs','pills'].indexOf(this.type)!==-1) {
3801 cfg.cn[0].cls += ' nav-' + this.type
3805 if (this.type!=='nav') {
3806 Roo.log('nav type must be nav/tabs/pills')
3808 cfg.cn[0].cls += ' navbar-nav'
3814 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3815 cfg.cn[0].cls += ' nav-' + this.arrangement;
3819 if (this.align === 'right') {
3820 cfg.cn[0].cls += ' navbar-right';
3824 cfg.cls += ' navbar-inverse';
3851 * @class Roo.bootstrap.NavHeaderbar
3852 * @extends Roo.bootstrap.NavSimplebar
3853 * Bootstrap Sidebar class
3855 * @cfg {String} brand what is brand
3856 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3857 * @cfg {String} brand_href href of the brand
3858 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3859 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3860 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3861 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3864 * Create a new Sidebar
3865 * @param {Object} config The config object
3869 Roo.bootstrap.NavHeaderbar = function(config){
3870 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3874 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3881 desktopCenter : false,
3884 getAutoCreate : function(){
3887 tag: this.nav || 'nav',
3894 if (this.desktopCenter) {
3895 cn.push({cls : 'container', cn : []});
3902 cls: 'navbar-header',
3907 cls: 'navbar-toggle',
3908 'data-toggle': 'collapse',
3913 html: 'Toggle navigation'
3935 cls: 'collapse navbar-collapse',
3939 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3941 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3942 cfg.cls += ' navbar-' + this.position;
3944 // tag can override this..
3946 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3949 if (this.brand !== '') {
3952 href: this.brand_href ? this.brand_href : '#',
3953 cls: 'navbar-brand',
3961 cfg.cls += ' main-nav';
3969 getHeaderChildContainer : function()
3971 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3972 return this.el.select('.navbar-header',true).first();
3975 return this.getChildContainer();
3979 initEvents : function()
3981 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3983 if (this.autohide) {
3988 Roo.get(document).on('scroll',function(e) {
3989 var ns = Roo.get(document).getScroll().top;
3990 var os = prevScroll;
3994 ft.removeClass('slideDown');
3995 ft.addClass('slideUp');
3998 ft.removeClass('slideUp');
3999 ft.addClass('slideDown');
4020 * @class Roo.bootstrap.NavSidebar
4021 * @extends Roo.bootstrap.Navbar
4022 * Bootstrap Sidebar class
4025 * Create a new Sidebar
4026 * @param {Object} config The config object
4030 Roo.bootstrap.NavSidebar = function(config){
4031 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4034 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4036 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4038 getAutoCreate : function(){
4043 cls: 'sidebar sidebar-nav'
4065 * @class Roo.bootstrap.NavGroup
4066 * @extends Roo.bootstrap.Component
4067 * Bootstrap NavGroup class
4068 * @cfg {String} align (left|right)
4069 * @cfg {Boolean} inverse
4070 * @cfg {String} type (nav|pills|tab) default nav
4071 * @cfg {String} navId - reference Id for navbar.
4075 * Create a new nav group
4076 * @param {Object} config The config object
4079 Roo.bootstrap.NavGroup = function(config){
4080 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4083 Roo.bootstrap.NavGroup.register(this);
4087 * Fires when the active item changes
4088 * @param {Roo.bootstrap.NavGroup} this
4089 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4090 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4097 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4108 getAutoCreate : function()
4110 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4117 if (['tabs','pills'].indexOf(this.type)!==-1) {
4118 cfg.cls += ' nav-' + this.type
4120 if (this.type!=='nav') {
4121 Roo.log('nav type must be nav/tabs/pills')
4123 cfg.cls += ' navbar-nav'
4126 if (this.parent() && this.parent().sidebar) {
4129 cls: 'dashboard-menu sidebar-menu'
4135 if (this.form === true) {
4141 if (this.align === 'right') {
4142 cfg.cls += ' navbar-right';
4144 cfg.cls += ' navbar-left';
4148 if (this.align === 'right') {
4149 cfg.cls += ' navbar-right';
4153 cfg.cls += ' navbar-inverse';
4161 * sets the active Navigation item
4162 * @param {Roo.bootstrap.NavItem} the new current navitem
4164 setActiveItem : function(item)
4167 Roo.each(this.navItems, function(v){
4172 v.setActive(false, true);
4179 item.setActive(true, true);
4180 this.fireEvent('changed', this, item, prev);
4185 * gets the active Navigation item
4186 * @return {Roo.bootstrap.NavItem} the current navitem
4188 getActive : function()
4192 Roo.each(this.navItems, function(v){
4203 indexOfNav : function()
4207 Roo.each(this.navItems, function(v,i){
4218 * adds a Navigation item
4219 * @param {Roo.bootstrap.NavItem} the navitem to add
4221 addItem : function(cfg)
4223 var cn = new Roo.bootstrap.NavItem(cfg);
4225 cn.parentId = this.id;
4226 cn.onRender(this.el, null);
4230 * register a Navigation item
4231 * @param {Roo.bootstrap.NavItem} the navitem to add
4233 register : function(item)
4235 this.navItems.push( item);
4236 item.navId = this.navId;
4241 * clear all the Navigation item
4244 clearAll : function()
4247 this.el.dom.innerHTML = '';
4250 getNavItem: function(tabId)
4253 Roo.each(this.navItems, function(e) {
4254 if (e.tabId == tabId) {
4264 setActiveNext : function()
4266 var i = this.indexOfNav(this.getActive());
4267 if (i > this.navItems.length) {
4270 this.setActiveItem(this.navItems[i+1]);
4272 setActivePrev : function()
4274 var i = this.indexOfNav(this.getActive());
4278 this.setActiveItem(this.navItems[i-1]);
4280 clearWasActive : function(except) {
4281 Roo.each(this.navItems, function(e) {
4282 if (e.tabId != except.tabId && e.was_active) {
4283 e.was_active = false;
4290 getWasActive : function ()
4293 Roo.each(this.navItems, function(e) {
4308 Roo.apply(Roo.bootstrap.NavGroup, {
4312 * register a Navigation Group
4313 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4315 register : function(navgrp)
4317 this.groups[navgrp.navId] = navgrp;
4321 * fetch a Navigation Group based on the navigation ID
4322 * @param {string} the navgroup to add
4323 * @returns {Roo.bootstrap.NavGroup} the navgroup
4325 get: function(navId) {
4326 if (typeof(this.groups[navId]) == 'undefined') {
4328 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4330 return this.groups[navId] ;
4345 * @class Roo.bootstrap.NavItem
4346 * @extends Roo.bootstrap.Component
4347 * Bootstrap Navbar.NavItem class
4348 * @cfg {String} href link to
4349 * @cfg {String} html content of button
4350 * @cfg {String} badge text inside badge
4351 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4352 * @cfg {String} glyphicon name of glyphicon
4353 * @cfg {String} icon name of font awesome icon
4354 * @cfg {Boolean} active Is item active
4355 * @cfg {Boolean} disabled Is item disabled
4357 * @cfg {Boolean} preventDefault (true | false) default false
4358 * @cfg {String} tabId the tab that this item activates.
4359 * @cfg {String} tagtype (a|span) render as a href or span?
4360 * @cfg {Boolean} animateRef (true|false) link to element default false
4363 * Create a new Navbar Item
4364 * @param {Object} config The config object
4366 Roo.bootstrap.NavItem = function(config){
4367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4372 * The raw click event for the entire grid.
4373 * @param {Roo.EventObject} e
4378 * Fires when the active item active state changes
4379 * @param {Roo.bootstrap.NavItem} this
4380 * @param {boolean} state the new state
4386 * Fires when scroll to element
4387 * @param {Roo.bootstrap.NavItem} this
4388 * @param {Object} options
4389 * @param {Roo.EventObject} e
4397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4405 preventDefault : false,
4412 getAutoCreate : function(){
4421 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4423 if (this.disabled) {
4424 cfg.cls += ' disabled';
4427 if (this.href || this.html || this.glyphicon || this.icon) {
4431 href : this.href || "#",
4432 html: this.html || ''
4437 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4440 if(this.glyphicon) {
4441 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4446 cfg.cn[0].html += " <span class='caret'></span>";
4450 if (this.badge !== '') {
4452 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4460 initEvents: function()
4462 if (typeof (this.menu) != 'undefined') {
4463 this.menu.parentType = this.xtype;
4464 this.menu.triggerEl = this.el;
4465 this.menu = this.addxtype(Roo.apply({}, this.menu));
4468 this.el.select('a',true).on('click', this.onClick, this);
4470 if(this.tagtype == 'span'){
4471 this.el.select('span',true).on('click', this.onClick, this);
4474 // at this point parent should be available..
4475 this.parent().register(this);
4478 onClick : function(e)
4480 if (e.getTarget('.dropdown-menu-item')) {
4481 // did you click on a menu itemm.... - then don't trigger onclick..
4486 this.preventDefault ||
4489 Roo.log("NavItem - prevent Default?");
4493 if (this.disabled) {
4497 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4498 if (tg && tg.transition) {
4499 Roo.log("waiting for the transitionend");
4505 //Roo.log("fire event clicked");
4506 if(this.fireEvent('click', this, e) === false){
4510 if(this.tagtype == 'span'){
4514 //Roo.log(this.href);
4515 var ael = this.el.select('a',true).first();
4518 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4519 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4520 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4521 return; // ignore... - it's a 'hash' to another page.
4523 Roo.log("NavItem - prevent Default?");
4525 this.scrollToElement(e);
4529 var p = this.parent();
4531 if (['tabs','pills'].indexOf(p.type)!==-1) {
4532 if (typeof(p.setActiveItem) !== 'undefined') {
4533 p.setActiveItem(this);
4537 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4538 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4539 // remove the collapsed menu expand...
4540 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4544 isActive: function () {
4547 setActive : function(state, fire, is_was_active)
4549 if (this.active && !state && this.navId) {
4550 this.was_active = true;
4551 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4553 nv.clearWasActive(this);
4557 this.active = state;
4560 this.el.removeClass('active');
4561 } else if (!this.el.hasClass('active')) {
4562 this.el.addClass('active');
4565 this.fireEvent('changed', this, state);
4568 // show a panel if it's registered and related..
4570 if (!this.navId || !this.tabId || !state || is_was_active) {
4574 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4578 var pan = tg.getPanelByName(this.tabId);
4582 // if we can not flip to new panel - go back to old nav highlight..
4583 if (false == tg.showPanel(pan)) {
4584 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4586 var onav = nv.getWasActive();
4588 onav.setActive(true, false, true);
4597 // this should not be here...
4598 setDisabled : function(state)
4600 this.disabled = state;
4602 this.el.removeClass('disabled');
4603 } else if (!this.el.hasClass('disabled')) {
4604 this.el.addClass('disabled');
4610 * Fetch the element to display the tooltip on.
4611 * @return {Roo.Element} defaults to this.el
4613 tooltipEl : function()
4615 return this.el.select('' + this.tagtype + '', true).first();
4618 scrollToElement : function(e)
4620 var c = document.body;
4623 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4625 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4626 c = document.documentElement;
4629 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4635 var o = target.calcOffsetsTo(c);
4642 this.fireEvent('scrollto', this, options, e);
4644 Roo.get(c).scrollTo('top', options.value, true);
4657 * <span> icon </span>
4658 * <span> text </span>
4659 * <span>badge </span>
4663 * @class Roo.bootstrap.NavSidebarItem
4664 * @extends Roo.bootstrap.NavItem
4665 * Bootstrap Navbar.NavSidebarItem class
4666 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4667 * {Boolean} open is the menu open
4668 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4669 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4670 * {String} buttonSize (sm|md|lg)the extra classes for the button
4671 * {Boolean} showArrow show arrow next to the text (default true)
4673 * Create a new Navbar Button
4674 * @param {Object} config The config object
4676 Roo.bootstrap.NavSidebarItem = function(config){
4677 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4682 * The raw click event for the entire grid.
4683 * @param {Roo.EventObject} e
4688 * Fires when the active item active state changes
4689 * @param {Roo.bootstrap.NavSidebarItem} this
4690 * @param {boolean} state the new state
4698 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4700 badgeWeight : 'default',
4706 buttonWeight : 'default',
4712 getAutoCreate : function(){
4717 href : this.href || '#',
4723 if(this.buttonView){
4726 href : this.href || '#',
4727 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4740 cfg.cls += ' active';
4743 if (this.disabled) {
4744 cfg.cls += ' disabled';
4747 cfg.cls += ' open x-open';
4750 if (this.glyphicon || this.icon) {
4751 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4752 a.cn.push({ tag : 'i', cls : c }) ;
4755 if(!this.buttonView){
4758 html : this.html || ''
4765 if (this.badge !== '') {
4766 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4772 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4775 a.cls += ' dropdown-toggle treeview' ;
4781 initEvents : function()
4783 if (typeof (this.menu) != 'undefined') {
4784 this.menu.parentType = this.xtype;
4785 this.menu.triggerEl = this.el;
4786 this.menu = this.addxtype(Roo.apply({}, this.menu));
4789 this.el.on('click', this.onClick, this);
4791 if(this.badge !== ''){
4792 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4797 onClick : function(e)
4804 if(this.preventDefault){
4808 this.fireEvent('click', this);
4811 disable : function()
4813 this.setDisabled(true);
4818 this.setDisabled(false);
4821 setDisabled : function(state)
4823 if(this.disabled == state){
4827 this.disabled = state;
4830 this.el.addClass('disabled');
4834 this.el.removeClass('disabled');
4839 setActive : function(state)
4841 if(this.active == state){
4845 this.active = state;
4848 this.el.addClass('active');
4852 this.el.removeClass('active');
4857 isActive: function ()
4862 setBadge : function(str)
4868 this.badgeEl.dom.innerHTML = str;
4885 * @class Roo.bootstrap.Row
4886 * @extends Roo.bootstrap.Component
4887 * Bootstrap Row class (contains columns...)
4891 * @param {Object} config The config object
4894 Roo.bootstrap.Row = function(config){
4895 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4898 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4900 getAutoCreate : function(){
4919 * @class Roo.bootstrap.Element
4920 * @extends Roo.bootstrap.Component
4921 * Bootstrap Element class
4922 * @cfg {String} html contents of the element
4923 * @cfg {String} tag tag of the element
4924 * @cfg {String} cls class of the element
4925 * @cfg {Boolean} preventDefault (true|false) default false
4926 * @cfg {Boolean} clickable (true|false) default false
4929 * Create a new Element
4930 * @param {Object} config The config object
4933 Roo.bootstrap.Element = function(config){
4934 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4940 * When a element is chick
4941 * @param {Roo.bootstrap.Element} this
4942 * @param {Roo.EventObject} e
4948 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4953 preventDefault: false,
4956 getAutoCreate : function(){
4960 // cls: this.cls, double assign in parent class Component.js :: onRender
4967 initEvents: function()
4969 Roo.bootstrap.Element.superclass.initEvents.call(this);
4972 this.el.on('click', this.onClick, this);
4977 onClick : function(e)
4979 if(this.preventDefault){
4983 this.fireEvent('click', this, e);
4986 getValue : function()
4988 return this.el.dom.innerHTML;
4991 setValue : function(value)
4993 this.el.dom.innerHTML = value;
5008 * @class Roo.bootstrap.Pagination
5009 * @extends Roo.bootstrap.Component
5010 * Bootstrap Pagination class
5011 * @cfg {String} size xs | sm | md | lg
5012 * @cfg {Boolean} inverse false | true
5015 * Create a new Pagination
5016 * @param {Object} config The config object
5019 Roo.bootstrap.Pagination = function(config){
5020 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5023 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5029 getAutoCreate : function(){
5035 cfg.cls += ' inverse';
5041 cfg.cls += " " + this.cls;
5059 * @class Roo.bootstrap.PaginationItem
5060 * @extends Roo.bootstrap.Component
5061 * Bootstrap PaginationItem class
5062 * @cfg {String} html text
5063 * @cfg {String} href the link
5064 * @cfg {Boolean} preventDefault (true | false) default true
5065 * @cfg {Boolean} active (true | false) default false
5066 * @cfg {Boolean} disabled default false
5070 * Create a new PaginationItem
5071 * @param {Object} config The config object
5075 Roo.bootstrap.PaginationItem = function(config){
5076 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5081 * The raw click event for the entire grid.
5082 * @param {Roo.EventObject} e
5088 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5092 preventDefault: true,
5097 getAutoCreate : function(){
5103 href : this.href ? this.href : '#',
5104 html : this.html ? this.html : ''
5114 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5118 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5124 initEvents: function() {
5126 this.el.on('click', this.onClick, this);
5129 onClick : function(e)
5131 Roo.log('PaginationItem on click ');
5132 if(this.preventDefault){
5140 this.fireEvent('click', this, e);
5156 * @class Roo.bootstrap.Slider
5157 * @extends Roo.bootstrap.Component
5158 * Bootstrap Slider class
5161 * Create a new Slider
5162 * @param {Object} config The config object
5165 Roo.bootstrap.Slider = function(config){
5166 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5169 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5171 getAutoCreate : function(){
5175 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5179 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5191 * Ext JS Library 1.1.1
5192 * Copyright(c) 2006-2007, Ext JS, LLC.
5194 * Originally Released Under LGPL - original licence link has changed is not relivant.
5197 * <script type="text/javascript">
5202 * @class Roo.grid.ColumnModel
5203 * @extends Roo.util.Observable
5204 * This is the default implementation of a ColumnModel used by the Grid. It defines
5205 * the columns in the grid.
5208 var colModel = new Roo.grid.ColumnModel([
5209 {header: "Ticker", width: 60, sortable: true, locked: true},
5210 {header: "Company Name", width: 150, sortable: true},
5211 {header: "Market Cap.", width: 100, sortable: true},
5212 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5213 {header: "Employees", width: 100, sortable: true, resizable: false}
5218 * The config options listed for this class are options which may appear in each
5219 * individual column definition.
5220 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5222 * @param {Object} config An Array of column config objects. See this class's
5223 * config objects for details.
5225 Roo.grid.ColumnModel = function(config){
5227 * The config passed into the constructor
5229 this.config = config;
5232 // if no id, create one
5233 // if the column does not have a dataIndex mapping,
5234 // map it to the order it is in the config
5235 for(var i = 0, len = config.length; i < len; i++){
5237 if(typeof c.dataIndex == "undefined"){
5240 if(typeof c.renderer == "string"){
5241 c.renderer = Roo.util.Format[c.renderer];
5243 if(typeof c.id == "undefined"){
5246 if(c.editor && c.editor.xtype){
5247 c.editor = Roo.factory(c.editor, Roo.grid);
5249 if(c.editor && c.editor.isFormField){
5250 c.editor = new Roo.grid.GridEditor(c.editor);
5252 this.lookup[c.id] = c;
5256 * The width of columns which have no width specified (defaults to 100)
5259 this.defaultWidth = 100;
5262 * Default sortable of columns which have no sortable specified (defaults to false)
5265 this.defaultSortable = false;
5269 * @event widthchange
5270 * Fires when the width of a column changes.
5271 * @param {ColumnModel} this
5272 * @param {Number} columnIndex The column index
5273 * @param {Number} newWidth The new width
5275 "widthchange": true,
5277 * @event headerchange
5278 * Fires when the text of a header changes.
5279 * @param {ColumnModel} this
5280 * @param {Number} columnIndex The column index
5281 * @param {Number} newText The new header text
5283 "headerchange": true,
5285 * @event hiddenchange
5286 * Fires when a column is hidden or "unhidden".
5287 * @param {ColumnModel} this
5288 * @param {Number} columnIndex The column index
5289 * @param {Boolean} hidden true if hidden, false otherwise
5291 "hiddenchange": true,
5293 * @event columnmoved
5294 * Fires when a column is moved.
5295 * @param {ColumnModel} this
5296 * @param {Number} oldIndex
5297 * @param {Number} newIndex
5299 "columnmoved" : true,
5301 * @event columlockchange
5302 * Fires when a column's locked state is changed
5303 * @param {ColumnModel} this
5304 * @param {Number} colIndex
5305 * @param {Boolean} locked true if locked
5307 "columnlockchange" : true
5309 Roo.grid.ColumnModel.superclass.constructor.call(this);
5311 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5313 * @cfg {String} header The header text to display in the Grid view.
5316 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5317 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5318 * specified, the column's index is used as an index into the Record's data Array.
5321 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5322 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5325 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5326 * Defaults to the value of the {@link #defaultSortable} property.
5327 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5330 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5333 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5336 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5339 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5342 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5343 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5344 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5345 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5348 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5351 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5354 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5357 * @cfg {String} cursor (Optional)
5360 * @cfg {String} tooltip (Optional)
5363 * @cfg {Number} xs (Optional)
5366 * @cfg {Number} sm (Optional)
5369 * @cfg {Number} md (Optional)
5372 * @cfg {Number} lg (Optional)
5375 * Returns the id of the column at the specified index.
5376 * @param {Number} index The column index
5377 * @return {String} the id
5379 getColumnId : function(index){
5380 return this.config[index].id;
5384 * Returns the column for a specified id.
5385 * @param {String} id The column id
5386 * @return {Object} the column
5388 getColumnById : function(id){
5389 return this.lookup[id];
5394 * Returns the column for a specified dataIndex.
5395 * @param {String} dataIndex The column dataIndex
5396 * @return {Object|Boolean} the column or false if not found
5398 getColumnByDataIndex: function(dataIndex){
5399 var index = this.findColumnIndex(dataIndex);
5400 return index > -1 ? this.config[index] : false;
5404 * Returns the index for a specified column id.
5405 * @param {String} id The column id
5406 * @return {Number} the index, or -1 if not found
5408 getIndexById : function(id){
5409 for(var i = 0, len = this.config.length; i < len; i++){
5410 if(this.config[i].id == id){
5418 * Returns the index for a specified column dataIndex.
5419 * @param {String} dataIndex The column dataIndex
5420 * @return {Number} the index, or -1 if not found
5423 findColumnIndex : function(dataIndex){
5424 for(var i = 0, len = this.config.length; i < len; i++){
5425 if(this.config[i].dataIndex == dataIndex){
5433 moveColumn : function(oldIndex, newIndex){
5434 var c = this.config[oldIndex];
5435 this.config.splice(oldIndex, 1);
5436 this.config.splice(newIndex, 0, c);
5437 this.dataMap = null;
5438 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5441 isLocked : function(colIndex){
5442 return this.config[colIndex].locked === true;
5445 setLocked : function(colIndex, value, suppressEvent){
5446 if(this.isLocked(colIndex) == value){
5449 this.config[colIndex].locked = value;
5451 this.fireEvent("columnlockchange", this, colIndex, value);
5455 getTotalLockedWidth : function(){
5457 for(var i = 0; i < this.config.length; i++){
5458 if(this.isLocked(i) && !this.isHidden(i)){
5459 this.totalWidth += this.getColumnWidth(i);
5465 getLockedCount : function(){
5466 for(var i = 0, len = this.config.length; i < len; i++){
5467 if(!this.isLocked(i)){
5472 return this.config.length;
5476 * Returns the number of columns.
5479 getColumnCount : function(visibleOnly){
5480 if(visibleOnly === true){
5482 for(var i = 0, len = this.config.length; i < len; i++){
5483 if(!this.isHidden(i)){
5489 return this.config.length;
5493 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5494 * @param {Function} fn
5495 * @param {Object} scope (optional)
5496 * @return {Array} result
5498 getColumnsBy : function(fn, scope){
5500 for(var i = 0, len = this.config.length; i < len; i++){
5501 var c = this.config[i];
5502 if(fn.call(scope||this, c, i) === true){
5510 * Returns true if the specified column is sortable.
5511 * @param {Number} col The column index
5514 isSortable : function(col){
5515 if(typeof this.config[col].sortable == "undefined"){
5516 return this.defaultSortable;
5518 return this.config[col].sortable;
5522 * Returns the rendering (formatting) function defined for the column.
5523 * @param {Number} col The column index.
5524 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5526 getRenderer : function(col){
5527 if(!this.config[col].renderer){
5528 return Roo.grid.ColumnModel.defaultRenderer;
5530 return this.config[col].renderer;
5534 * Sets the rendering (formatting) function for a column.
5535 * @param {Number} col The column index
5536 * @param {Function} fn The function to use to process the cell's raw data
5537 * to return HTML markup for the grid view. The render function is called with
5538 * the following parameters:<ul>
5539 * <li>Data value.</li>
5540 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5541 * <li>css A CSS style string to apply to the table cell.</li>
5542 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5543 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5544 * <li>Row index</li>
5545 * <li>Column index</li>
5546 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5548 setRenderer : function(col, fn){
5549 this.config[col].renderer = fn;
5553 * Returns the width for the specified column.
5554 * @param {Number} col The column index
5557 getColumnWidth : function(col){
5558 return this.config[col].width * 1 || this.defaultWidth;
5562 * Sets the width for a column.
5563 * @param {Number} col The column index
5564 * @param {Number} width The new width
5566 setColumnWidth : function(col, width, suppressEvent){
5567 this.config[col].width = width;
5568 this.totalWidth = null;
5570 this.fireEvent("widthchange", this, col, width);
5575 * Returns the total width of all columns.
5576 * @param {Boolean} includeHidden True to include hidden column widths
5579 getTotalWidth : function(includeHidden){
5580 if(!this.totalWidth){
5581 this.totalWidth = 0;
5582 for(var i = 0, len = this.config.length; i < len; i++){
5583 if(includeHidden || !this.isHidden(i)){
5584 this.totalWidth += this.getColumnWidth(i);
5588 return this.totalWidth;
5592 * Returns the header for the specified column.
5593 * @param {Number} col The column index
5596 getColumnHeader : function(col){
5597 return this.config[col].header;
5601 * Sets the header for a column.
5602 * @param {Number} col The column index
5603 * @param {String} header The new header
5605 setColumnHeader : function(col, header){
5606 this.config[col].header = header;
5607 this.fireEvent("headerchange", this, col, header);
5611 * Returns the tooltip for the specified column.
5612 * @param {Number} col The column index
5615 getColumnTooltip : function(col){
5616 return this.config[col].tooltip;
5619 * Sets the tooltip for a column.
5620 * @param {Number} col The column index
5621 * @param {String} tooltip The new tooltip
5623 setColumnTooltip : function(col, tooltip){
5624 this.config[col].tooltip = tooltip;
5628 * Returns the dataIndex for the specified column.
5629 * @param {Number} col The column index
5632 getDataIndex : function(col){
5633 return this.config[col].dataIndex;
5637 * Sets the dataIndex for a column.
5638 * @param {Number} col The column index
5639 * @param {Number} dataIndex The new dataIndex
5641 setDataIndex : function(col, dataIndex){
5642 this.config[col].dataIndex = dataIndex;
5648 * Returns true if the cell is editable.
5649 * @param {Number} colIndex The column index
5650 * @param {Number} rowIndex The row index - this is nto actually used..?
5653 isCellEditable : function(colIndex, rowIndex){
5654 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5658 * Returns the editor defined for the cell/column.
5659 * return false or null to disable editing.
5660 * @param {Number} colIndex The column index
5661 * @param {Number} rowIndex The row index
5664 getCellEditor : function(colIndex, rowIndex){
5665 return this.config[colIndex].editor;
5669 * Sets if a column is editable.
5670 * @param {Number} col The column index
5671 * @param {Boolean} editable True if the column is editable
5673 setEditable : function(col, editable){
5674 this.config[col].editable = editable;
5679 * Returns true if the column is hidden.
5680 * @param {Number} colIndex The column index
5683 isHidden : function(colIndex){
5684 return this.config[colIndex].hidden;
5689 * Returns true if the column width cannot be changed
5691 isFixed : function(colIndex){
5692 return this.config[colIndex].fixed;
5696 * Returns true if the column can be resized
5699 isResizable : function(colIndex){
5700 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5703 * Sets if a column is hidden.
5704 * @param {Number} colIndex The column index
5705 * @param {Boolean} hidden True if the column is hidden
5707 setHidden : function(colIndex, hidden){
5708 this.config[colIndex].hidden = hidden;
5709 this.totalWidth = null;
5710 this.fireEvent("hiddenchange", this, colIndex, hidden);
5714 * Sets the editor for a column.
5715 * @param {Number} col The column index
5716 * @param {Object} editor The editor object
5718 setEditor : function(col, editor){
5719 this.config[col].editor = editor;
5723 Roo.grid.ColumnModel.defaultRenderer = function(value)
5725 if(typeof value == "object") {
5728 if(typeof value == "string" && value.length < 1){
5732 return String.format("{0}", value);
5735 // Alias for backwards compatibility
5736 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5739 * Ext JS Library 1.1.1
5740 * Copyright(c) 2006-2007, Ext JS, LLC.
5742 * Originally Released Under LGPL - original licence link has changed is not relivant.
5745 * <script type="text/javascript">
5749 * @class Roo.LoadMask
5750 * A simple utility class for generically masking elements while loading data. If the element being masked has
5751 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5752 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5753 * element's UpdateManager load indicator and will be destroyed after the initial load.
5755 * Create a new LoadMask
5756 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5757 * @param {Object} config The config object
5759 Roo.LoadMask = function(el, config){
5760 this.el = Roo.get(el);
5761 Roo.apply(this, config);
5763 this.store.on('beforeload', this.onBeforeLoad, this);
5764 this.store.on('load', this.onLoad, this);
5765 this.store.on('loadexception', this.onLoadException, this);
5766 this.removeMask = false;
5768 var um = this.el.getUpdateManager();
5769 um.showLoadIndicator = false; // disable the default indicator
5770 um.on('beforeupdate', this.onBeforeLoad, this);
5771 um.on('update', this.onLoad, this);
5772 um.on('failure', this.onLoad, this);
5773 this.removeMask = true;
5777 Roo.LoadMask.prototype = {
5779 * @cfg {Boolean} removeMask
5780 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5781 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5785 * The text to display in a centered loading message box (defaults to 'Loading...')
5789 * @cfg {String} msgCls
5790 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5792 msgCls : 'x-mask-loading',
5795 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5801 * Disables the mask to prevent it from being displayed
5803 disable : function(){
5804 this.disabled = true;
5808 * Enables the mask so that it can be displayed
5810 enable : function(){
5811 this.disabled = false;
5814 onLoadException : function()
5818 if (typeof(arguments[3]) != 'undefined') {
5819 Roo.MessageBox.alert("Error loading",arguments[3]);
5823 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5824 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5831 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5836 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5840 onBeforeLoad : function(){
5842 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5847 destroy : function(){
5849 this.store.un('beforeload', this.onBeforeLoad, this);
5850 this.store.un('load', this.onLoad, this);
5851 this.store.un('loadexception', this.onLoadException, this);
5853 var um = this.el.getUpdateManager();
5854 um.un('beforeupdate', this.onBeforeLoad, this);
5855 um.un('update', this.onLoad, this);
5856 um.un('failure', this.onLoad, this);
5867 * @class Roo.bootstrap.Table
5868 * @extends Roo.bootstrap.Component
5869 * Bootstrap Table class
5870 * @cfg {String} cls table class
5871 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5872 * @cfg {String} bgcolor Specifies the background color for a table
5873 * @cfg {Number} border Specifies whether the table cells should have borders or not
5874 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5875 * @cfg {Number} cellspacing Specifies the space between cells
5876 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5877 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5878 * @cfg {String} sortable Specifies that the table should be sortable
5879 * @cfg {String} summary Specifies a summary of the content of a table
5880 * @cfg {Number} width Specifies the width of a table
5881 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5883 * @cfg {boolean} striped Should the rows be alternative striped
5884 * @cfg {boolean} bordered Add borders to the table
5885 * @cfg {boolean} hover Add hover highlighting
5886 * @cfg {boolean} condensed Format condensed
5887 * @cfg {boolean} responsive Format condensed
5888 * @cfg {Boolean} loadMask (true|false) default false
5889 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5890 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5891 * @cfg {Boolean} rowSelection (true|false) default false
5892 * @cfg {Boolean} cellSelection (true|false) default false
5893 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5894 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5895 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5896 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5900 * Create a new Table
5901 * @param {Object} config The config object
5904 Roo.bootstrap.Table = function(config){
5905 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5910 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5911 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5912 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5913 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5915 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5917 this.sm.grid = this;
5918 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5919 this.sm = this.selModel;
5920 this.sm.xmodule = this.xmodule || false;
5923 if (this.cm && typeof(this.cm.config) == 'undefined') {
5924 this.colModel = new Roo.grid.ColumnModel(this.cm);
5925 this.cm = this.colModel;
5926 this.cm.xmodule = this.xmodule || false;
5929 this.store= Roo.factory(this.store, Roo.data);
5930 this.ds = this.store;
5931 this.ds.xmodule = this.xmodule || false;
5934 if (this.footer && this.store) {
5935 this.footer.dataSource = this.ds;
5936 this.footer = Roo.factory(this.footer);
5943 * Fires when a cell is clicked
5944 * @param {Roo.bootstrap.Table} this
5945 * @param {Roo.Element} el
5946 * @param {Number} rowIndex
5947 * @param {Number} columnIndex
5948 * @param {Roo.EventObject} e
5952 * @event celldblclick
5953 * Fires when a cell is double clicked
5954 * @param {Roo.bootstrap.Table} this
5955 * @param {Roo.Element} el
5956 * @param {Number} rowIndex
5957 * @param {Number} columnIndex
5958 * @param {Roo.EventObject} e
5960 "celldblclick" : true,
5963 * Fires when a row is clicked
5964 * @param {Roo.bootstrap.Table} this
5965 * @param {Roo.Element} el
5966 * @param {Number} rowIndex
5967 * @param {Roo.EventObject} e
5971 * @event rowdblclick
5972 * Fires when a row is double clicked
5973 * @param {Roo.bootstrap.Table} this
5974 * @param {Roo.Element} el
5975 * @param {Number} rowIndex
5976 * @param {Roo.EventObject} e
5978 "rowdblclick" : true,
5981 * Fires when a mouseover occur
5982 * @param {Roo.bootstrap.Table} this
5983 * @param {Roo.Element} el
5984 * @param {Number} rowIndex
5985 * @param {Number} columnIndex
5986 * @param {Roo.EventObject} e
5991 * Fires when a mouseout occur
5992 * @param {Roo.bootstrap.Table} this
5993 * @param {Roo.Element} el
5994 * @param {Number} rowIndex
5995 * @param {Number} columnIndex
5996 * @param {Roo.EventObject} e
6001 * Fires when a row is rendered, so you can change add a style to it.
6002 * @param {Roo.bootstrap.Table} this
6003 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6007 * @event rowsrendered
6008 * Fires when all the rows have been rendered
6009 * @param {Roo.bootstrap.Table} this
6011 'rowsrendered' : true,
6013 * @event contextmenu
6014 * The raw contextmenu event for the entire grid.
6015 * @param {Roo.EventObject} e
6017 "contextmenu" : true,
6019 * @event rowcontextmenu
6020 * Fires when a row is right clicked
6021 * @param {Roo.bootstrap.Table} this
6022 * @param {Number} rowIndex
6023 * @param {Roo.EventObject} e
6025 "rowcontextmenu" : true,
6027 * @event cellcontextmenu
6028 * Fires when a cell is right clicked
6029 * @param {Roo.bootstrap.Table} this
6030 * @param {Number} rowIndex
6031 * @param {Number} cellIndex
6032 * @param {Roo.EventObject} e
6034 "cellcontextmenu" : true,
6036 * @event headercontextmenu
6037 * Fires when a header is right clicked
6038 * @param {Roo.bootstrap.Table} this
6039 * @param {Number} columnIndex
6040 * @param {Roo.EventObject} e
6042 "headercontextmenu" : true
6046 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6072 rowSelection : false,
6073 cellSelection : false,
6076 // Roo.Element - the tbody
6078 // Roo.Element - thead element
6081 container: false, // used by gridpanel...
6087 auto_hide_footer : false,
6089 getAutoCreate : function()
6091 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6098 if (this.scrollBody) {
6099 cfg.cls += ' table-body-fixed';
6102 cfg.cls += ' table-striped';
6106 cfg.cls += ' table-hover';
6108 if (this.bordered) {
6109 cfg.cls += ' table-bordered';
6111 if (this.condensed) {
6112 cfg.cls += ' table-condensed';
6114 if (this.responsive) {
6115 cfg.cls += ' table-responsive';
6119 cfg.cls+= ' ' +this.cls;
6122 // this lot should be simplifed...
6135 ].forEach(function(k) {
6143 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6146 if(this.store || this.cm){
6147 if(this.headerShow){
6148 cfg.cn.push(this.renderHeader());
6151 cfg.cn.push(this.renderBody());
6153 if(this.footerShow){
6154 cfg.cn.push(this.renderFooter());
6156 // where does this come from?
6157 //cfg.cls+= ' TableGrid';
6160 return { cn : [ cfg ] };
6163 initEvents : function()
6165 if(!this.store || !this.cm){
6168 if (this.selModel) {
6169 this.selModel.initEvents();
6173 //Roo.log('initEvents with ds!!!!');
6175 this.mainBody = this.el.select('tbody', true).first();
6176 this.mainHead = this.el.select('thead', true).first();
6177 this.mainFoot = this.el.select('tfoot', true).first();
6183 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6184 e.on('click', _this.sort, _this);
6187 this.mainBody.on("click", this.onClick, this);
6188 this.mainBody.on("dblclick", this.onDblClick, this);
6190 // why is this done????? = it breaks dialogs??
6191 //this.parent().el.setStyle('position', 'relative');
6195 this.footer.parentId = this.id;
6196 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6199 this.el.select('tfoot tr td').first().addClass('hide');
6204 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6207 this.store.on('load', this.onLoad, this);
6208 this.store.on('beforeload', this.onBeforeLoad, this);
6209 this.store.on('update', this.onUpdate, this);
6210 this.store.on('add', this.onAdd, this);
6211 this.store.on("clear", this.clear, this);
6213 this.el.on("contextmenu", this.onContextMenu, this);
6215 this.mainBody.on('scroll', this.onBodyScroll, this);
6217 this.cm.on("headerchange", this.onHeaderChange, this);
6219 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6223 onContextMenu : function(e, t)
6225 this.processEvent("contextmenu", e);
6228 processEvent : function(name, e)
6230 if (name != 'touchstart' ) {
6231 this.fireEvent(name, e);
6234 var t = e.getTarget();
6236 var cell = Roo.get(t);
6242 if(cell.findParent('tfoot', false, true)){
6246 if(cell.findParent('thead', false, true)){
6248 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6249 cell = Roo.get(t).findParent('th', false, true);
6251 Roo.log("failed to find th in thead?");
6252 Roo.log(e.getTarget());
6257 var cellIndex = cell.dom.cellIndex;
6259 var ename = name == 'touchstart' ? 'click' : name;
6260 this.fireEvent("header" + ename, this, cellIndex, e);
6265 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6266 cell = Roo.get(t).findParent('td', false, true);
6268 Roo.log("failed to find th in tbody?");
6269 Roo.log(e.getTarget());
6274 var row = cell.findParent('tr', false, true);
6275 var cellIndex = cell.dom.cellIndex;
6276 var rowIndex = row.dom.rowIndex - 1;
6280 this.fireEvent("row" + name, this, rowIndex, e);
6284 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6290 onMouseover : function(e, el)
6292 var cell = Roo.get(el);
6298 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6299 cell = cell.findParent('td', false, true);
6302 var row = cell.findParent('tr', false, true);
6303 var cellIndex = cell.dom.cellIndex;
6304 var rowIndex = row.dom.rowIndex - 1; // start from 0
6306 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6310 onMouseout : function(e, el)
6312 var cell = Roo.get(el);
6318 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6319 cell = cell.findParent('td', false, true);
6322 var row = cell.findParent('tr', false, true);
6323 var cellIndex = cell.dom.cellIndex;
6324 var rowIndex = row.dom.rowIndex - 1; // start from 0
6326 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6330 onClick : function(e, el)
6332 var cell = Roo.get(el);
6334 if(!cell || (!this.cellSelection && !this.rowSelection)){
6338 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6339 cell = cell.findParent('td', false, true);
6342 if(!cell || typeof(cell) == 'undefined'){
6346 var row = cell.findParent('tr', false, true);
6348 if(!row || typeof(row) == 'undefined'){
6352 var cellIndex = cell.dom.cellIndex;
6353 var rowIndex = this.getRowIndex(row);
6355 // why??? - should these not be based on SelectionModel?
6356 if(this.cellSelection){
6357 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6360 if(this.rowSelection){
6361 this.fireEvent('rowclick', this, row, rowIndex, e);
6367 onDblClick : function(e,el)
6369 var cell = Roo.get(el);
6371 if(!cell || (!this.cellSelection && !this.rowSelection)){
6375 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6376 cell = cell.findParent('td', false, true);
6379 if(!cell || typeof(cell) == 'undefined'){
6383 var row = cell.findParent('tr', false, true);
6385 if(!row || typeof(row) == 'undefined'){
6389 var cellIndex = cell.dom.cellIndex;
6390 var rowIndex = this.getRowIndex(row);
6392 if(this.cellSelection){
6393 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6396 if(this.rowSelection){
6397 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6401 sort : function(e,el)
6403 var col = Roo.get(el);
6405 if(!col.hasClass('sortable')){
6409 var sort = col.attr('sort');
6412 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6416 this.store.sortInfo = {field : sort, direction : dir};
6419 Roo.log("calling footer first");
6420 this.footer.onClick('first');
6423 this.store.load({ params : { start : 0 } });
6427 renderHeader : function()
6435 this.totalWidth = 0;
6437 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6439 var config = cm.config[i];
6443 cls : 'x-hcol-' + i,
6445 html: cm.getColumnHeader(i)
6450 if(typeof(config.sortable) != 'undefined' && config.sortable){
6452 c.html = '<i class="glyphicon"></i>' + c.html;
6455 if(typeof(config.lgHeader) != 'undefined'){
6456 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6459 if(typeof(config.mdHeader) != 'undefined'){
6460 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6463 if(typeof(config.smHeader) != 'undefined'){
6464 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6467 if(typeof(config.xsHeader) != 'undefined'){
6468 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6475 if(typeof(config.tooltip) != 'undefined'){
6476 c.tooltip = config.tooltip;
6479 if(typeof(config.colspan) != 'undefined'){
6480 c.colspan = config.colspan;
6483 if(typeof(config.hidden) != 'undefined' && config.hidden){
6484 c.style += ' display:none;';
6487 if(typeof(config.dataIndex) != 'undefined'){
6488 c.sort = config.dataIndex;
6493 if(typeof(config.align) != 'undefined' && config.align.length){
6494 c.style += ' text-align:' + config.align + ';';
6497 if(typeof(config.width) != 'undefined'){
6498 c.style += ' width:' + config.width + 'px;';
6499 this.totalWidth += config.width;
6501 this.totalWidth += 100; // assume minimum of 100 per column?
6504 if(typeof(config.cls) != 'undefined'){
6505 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6508 ['xs','sm','md','lg'].map(function(size){
6510 if(typeof(config[size]) == 'undefined'){
6514 if (!config[size]) { // 0 = hidden
6515 c.cls += ' hidden-' + size;
6519 c.cls += ' col-' + size + '-' + config[size];
6529 renderBody : function()
6539 colspan : this.cm.getColumnCount()
6549 renderFooter : function()
6559 colspan : this.cm.getColumnCount()
6573 // Roo.log('ds onload');
6578 var ds = this.store;
6580 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6581 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6582 if (_this.store.sortInfo) {
6584 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6585 e.select('i', true).addClass(['glyphicon-arrow-up']);
6588 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6589 e.select('i', true).addClass(['glyphicon-arrow-down']);
6594 var tbody = this.mainBody;
6596 if(ds.getCount() > 0){
6597 ds.data.each(function(d,rowIndex){
6598 var row = this.renderRow(cm, ds, rowIndex);
6600 tbody.createChild(row);
6604 if(row.cellObjects.length){
6605 Roo.each(row.cellObjects, function(r){
6606 _this.renderCellObject(r);
6613 var tfoot = this.el.select('tfoot', true).first();
6615 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6617 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6619 var total = this.ds.getTotalCount();
6621 if(this.footer.pageSize < total){
6622 this.mainFoot.show();
6626 Roo.each(this.el.select('tbody td', true).elements, function(e){
6627 e.on('mouseover', _this.onMouseover, _this);
6630 Roo.each(this.el.select('tbody td', true).elements, function(e){
6631 e.on('mouseout', _this.onMouseout, _this);
6633 this.fireEvent('rowsrendered', this);
6639 onUpdate : function(ds,record)
6641 this.refreshRow(record);
6645 onRemove : function(ds, record, index, isUpdate){
6646 if(isUpdate !== true){
6647 this.fireEvent("beforerowremoved", this, index, record);
6649 var bt = this.mainBody.dom;
6651 var rows = this.el.select('tbody > tr', true).elements;
6653 if(typeof(rows[index]) != 'undefined'){
6654 bt.removeChild(rows[index].dom);
6657 // if(bt.rows[index]){
6658 // bt.removeChild(bt.rows[index]);
6661 if(isUpdate !== true){
6662 //this.stripeRows(index);
6663 //this.syncRowHeights(index, index);
6665 this.fireEvent("rowremoved", this, index, record);
6669 onAdd : function(ds, records, rowIndex)
6671 //Roo.log('on Add called');
6672 // - note this does not handle multiple adding very well..
6673 var bt = this.mainBody.dom;
6674 for (var i =0 ; i < records.length;i++) {
6675 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6676 //Roo.log(records[i]);
6677 //Roo.log(this.store.getAt(rowIndex+i));
6678 this.insertRow(this.store, rowIndex + i, false);
6685 refreshRow : function(record){
6686 var ds = this.store, index;
6687 if(typeof record == 'number'){
6689 record = ds.getAt(index);
6691 index = ds.indexOf(record);
6693 this.insertRow(ds, index, true);
6695 this.onRemove(ds, record, index+1, true);
6697 //this.syncRowHeights(index, index);
6699 this.fireEvent("rowupdated", this, index, record);
6702 insertRow : function(dm, rowIndex, isUpdate){
6705 this.fireEvent("beforerowsinserted", this, rowIndex);
6707 //var s = this.getScrollState();
6708 var row = this.renderRow(this.cm, this.store, rowIndex);
6709 // insert before rowIndex..
6710 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6714 if(row.cellObjects.length){
6715 Roo.each(row.cellObjects, function(r){
6716 _this.renderCellObject(r);
6721 this.fireEvent("rowsinserted", this, rowIndex);
6722 //this.syncRowHeights(firstRow, lastRow);
6723 //this.stripeRows(firstRow);
6730 getRowDom : function(rowIndex)
6732 var rows = this.el.select('tbody > tr', true).elements;
6734 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6737 // returns the object tree for a tr..
6740 renderRow : function(cm, ds, rowIndex)
6742 var d = ds.getAt(rowIndex);
6746 cls : 'x-row-' + rowIndex,
6750 var cellObjects = [];
6752 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6753 var config = cm.config[i];
6755 var renderer = cm.getRenderer(i);
6759 if(typeof(renderer) !== 'undefined'){
6760 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6762 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6763 // and are rendered into the cells after the row is rendered - using the id for the element.
6765 if(typeof(value) === 'object'){
6775 rowIndex : rowIndex,
6780 this.fireEvent('rowclass', this, rowcfg);
6784 cls : rowcfg.rowClass + ' x-col-' + i,
6786 html: (typeof(value) === 'object') ? '' : value
6793 if(typeof(config.colspan) != 'undefined'){
6794 td.colspan = config.colspan;
6797 if(typeof(config.hidden) != 'undefined' && config.hidden){
6798 td.style += ' display:none;';
6801 if(typeof(config.align) != 'undefined' && config.align.length){
6802 td.style += ' text-align:' + config.align + ';';
6804 if(typeof(config.valign) != 'undefined' && config.valign.length){
6805 td.style += ' vertical-align:' + config.valign + ';';
6808 if(typeof(config.width) != 'undefined'){
6809 td.style += ' width:' + config.width + 'px;';
6812 if(typeof(config.cursor) != 'undefined'){
6813 td.style += ' cursor:' + config.cursor + ';';
6816 if(typeof(config.cls) != 'undefined'){
6817 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6820 ['xs','sm','md','lg'].map(function(size){
6822 if(typeof(config[size]) == 'undefined'){
6826 if (!config[size]) { // 0 = hidden
6827 td.cls += ' hidden-' + size;
6831 td.cls += ' col-' + size + '-' + config[size];
6839 row.cellObjects = cellObjects;
6847 onBeforeLoad : function()
6856 this.el.select('tbody', true).first().dom.innerHTML = '';
6859 * Show or hide a row.
6860 * @param {Number} rowIndex to show or hide
6861 * @param {Boolean} state hide
6863 setRowVisibility : function(rowIndex, state)
6865 var bt = this.mainBody.dom;
6867 var rows = this.el.select('tbody > tr', true).elements;
6869 if(typeof(rows[rowIndex]) == 'undefined'){
6872 rows[rowIndex].dom.style.display = state ? '' : 'none';
6876 getSelectionModel : function(){
6878 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6880 return this.selModel;
6883 * Render the Roo.bootstrap object from renderder
6885 renderCellObject : function(r)
6889 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6891 var t = r.cfg.render(r.container);
6894 Roo.each(r.cfg.cn, function(c){
6896 container: t.getChildContainer(),
6899 _this.renderCellObject(child);
6904 getRowIndex : function(row)
6908 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6919 * Returns the grid's underlying element = used by panel.Grid
6920 * @return {Element} The element
6922 getGridEl : function(){
6926 * Forces a resize - used by panel.Grid
6927 * @return {Element} The element
6929 autoSize : function()
6931 //var ctr = Roo.get(this.container.dom.parentElement);
6932 var ctr = Roo.get(this.el.dom);
6934 var thd = this.getGridEl().select('thead',true).first();
6935 var tbd = this.getGridEl().select('tbody', true).first();
6936 var tfd = this.getGridEl().select('tfoot', true).first();
6938 var cw = ctr.getWidth();
6942 tbd.setSize(ctr.getWidth(),
6943 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6945 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6948 cw = Math.max(cw, this.totalWidth);
6949 this.getGridEl().select('tr',true).setWidth(cw);
6950 // resize 'expandable coloumn?
6952 return; // we doe not have a view in this design..
6955 onBodyScroll: function()
6957 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6959 this.mainHead.setStyle({
6960 'position' : 'relative',
6961 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6967 var scrollHeight = this.mainBody.dom.scrollHeight;
6969 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6971 var height = this.mainBody.getHeight();
6973 if(scrollHeight - height == scrollTop) {
6975 var total = this.ds.getTotalCount();
6977 if(this.footer.cursor + this.footer.pageSize < total){
6979 this.footer.ds.load({
6981 start : this.footer.cursor + this.footer.pageSize,
6982 limit : this.footer.pageSize
6992 onHeaderChange : function()
6994 var header = this.renderHeader();
6995 var table = this.el.select('table', true).first();
6997 this.mainHead.remove();
6998 this.mainHead = table.createChild(header, this.mainBody, false);
7001 onHiddenChange : function(colModel, colIndex, hidden)
7003 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7004 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7006 this.CSS.updateRule(thSelector, "display", "");
7007 this.CSS.updateRule(tdSelector, "display", "");
7010 this.CSS.updateRule(thSelector, "display", "none");
7011 this.CSS.updateRule(tdSelector, "display", "none");
7014 this.onHeaderChange();
7031 * @class Roo.bootstrap.TableCell
7032 * @extends Roo.bootstrap.Component
7033 * Bootstrap TableCell class
7034 * @cfg {String} html cell contain text
7035 * @cfg {String} cls cell class
7036 * @cfg {String} tag cell tag (td|th) default td
7037 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7038 * @cfg {String} align Aligns the content in a cell
7039 * @cfg {String} axis Categorizes cells
7040 * @cfg {String} bgcolor Specifies the background color of a cell
7041 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7042 * @cfg {Number} colspan Specifies the number of columns a cell should span
7043 * @cfg {String} headers Specifies one or more header cells a cell is related to
7044 * @cfg {Number} height Sets the height of a cell
7045 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7046 * @cfg {Number} rowspan Sets the number of rows a cell should span
7047 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7048 * @cfg {String} valign Vertical aligns the content in a cell
7049 * @cfg {Number} width Specifies the width of a cell
7052 * Create a new TableCell
7053 * @param {Object} config The config object
7056 Roo.bootstrap.TableCell = function(config){
7057 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7060 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7080 getAutoCreate : function(){
7081 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7101 cfg.align=this.align
7107 cfg.bgcolor=this.bgcolor
7110 cfg.charoff=this.charoff
7113 cfg.colspan=this.colspan
7116 cfg.headers=this.headers
7119 cfg.height=this.height
7122 cfg.nowrap=this.nowrap
7125 cfg.rowspan=this.rowspan
7128 cfg.scope=this.scope
7131 cfg.valign=this.valign
7134 cfg.width=this.width
7153 * @class Roo.bootstrap.TableRow
7154 * @extends Roo.bootstrap.Component
7155 * Bootstrap TableRow class
7156 * @cfg {String} cls row class
7157 * @cfg {String} align Aligns the content in a table row
7158 * @cfg {String} bgcolor Specifies a background color for a table row
7159 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7160 * @cfg {String} valign Vertical aligns the content in a table row
7163 * Create a new TableRow
7164 * @param {Object} config The config object
7167 Roo.bootstrap.TableRow = function(config){
7168 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7171 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7179 getAutoCreate : function(){
7180 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7190 cfg.align = this.align;
7193 cfg.bgcolor = this.bgcolor;
7196 cfg.charoff = this.charoff;
7199 cfg.valign = this.valign;
7217 * @class Roo.bootstrap.TableBody
7218 * @extends Roo.bootstrap.Component
7219 * Bootstrap TableBody class
7220 * @cfg {String} cls element class
7221 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7222 * @cfg {String} align Aligns the content inside the element
7223 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7224 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7227 * Create a new TableBody
7228 * @param {Object} config The config object
7231 Roo.bootstrap.TableBody = function(config){
7232 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7235 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7243 getAutoCreate : function(){
7244 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7258 cfg.align = this.align;
7261 cfg.charoff = this.charoff;
7264 cfg.valign = this.valign;
7271 // initEvents : function()
7278 // this.store = Roo.factory(this.store, Roo.data);
7279 // this.store.on('load', this.onLoad, this);
7281 // this.store.load();
7285 // onLoad: function ()
7287 // this.fireEvent('load', this);
7297 * Ext JS Library 1.1.1
7298 * Copyright(c) 2006-2007, Ext JS, LLC.
7300 * Originally Released Under LGPL - original licence link has changed is not relivant.
7303 * <script type="text/javascript">
7306 // as we use this in bootstrap.
7307 Roo.namespace('Roo.form');
7309 * @class Roo.form.Action
7310 * Internal Class used to handle form actions
7312 * @param {Roo.form.BasicForm} el The form element or its id
7313 * @param {Object} config Configuration options
7318 // define the action interface
7319 Roo.form.Action = function(form, options){
7321 this.options = options || {};
7324 * Client Validation Failed
7327 Roo.form.Action.CLIENT_INVALID = 'client';
7329 * Server Validation Failed
7332 Roo.form.Action.SERVER_INVALID = 'server';
7334 * Connect to Server Failed
7337 Roo.form.Action.CONNECT_FAILURE = 'connect';
7339 * Reading Data from Server Failed
7342 Roo.form.Action.LOAD_FAILURE = 'load';
7344 Roo.form.Action.prototype = {
7346 failureType : undefined,
7347 response : undefined,
7351 run : function(options){
7356 success : function(response){
7361 handleResponse : function(response){
7365 // default connection failure
7366 failure : function(response){
7368 this.response = response;
7369 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7370 this.form.afterAction(this, false);
7373 processResponse : function(response){
7374 this.response = response;
7375 if(!response.responseText){
7378 this.result = this.handleResponse(response);
7382 // utility functions used internally
7383 getUrl : function(appendParams){
7384 var url = this.options.url || this.form.url || this.form.el.dom.action;
7386 var p = this.getParams();
7388 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7394 getMethod : function(){
7395 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7398 getParams : function(){
7399 var bp = this.form.baseParams;
7400 var p = this.options.params;
7402 if(typeof p == "object"){
7403 p = Roo.urlEncode(Roo.applyIf(p, bp));
7404 }else if(typeof p == 'string' && bp){
7405 p += '&' + Roo.urlEncode(bp);
7408 p = Roo.urlEncode(bp);
7413 createCallback : function(){
7415 success: this.success,
7416 failure: this.failure,
7418 timeout: (this.form.timeout*1000),
7419 upload: this.form.fileUpload ? this.success : undefined
7424 Roo.form.Action.Submit = function(form, options){
7425 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7428 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7431 haveProgress : false,
7432 uploadComplete : false,
7434 // uploadProgress indicator.
7435 uploadProgress : function()
7437 if (!this.form.progressUrl) {
7441 if (!this.haveProgress) {
7442 Roo.MessageBox.progress("Uploading", "Uploading");
7444 if (this.uploadComplete) {
7445 Roo.MessageBox.hide();
7449 this.haveProgress = true;
7451 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7453 var c = new Roo.data.Connection();
7455 url : this.form.progressUrl,
7460 success : function(req){
7461 //console.log(data);
7465 rdata = Roo.decode(req.responseText)
7467 Roo.log("Invalid data from server..");
7471 if (!rdata || !rdata.success) {
7473 Roo.MessageBox.alert(Roo.encode(rdata));
7476 var data = rdata.data;
7478 if (this.uploadComplete) {
7479 Roo.MessageBox.hide();
7484 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7485 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7488 this.uploadProgress.defer(2000,this);
7491 failure: function(data) {
7492 Roo.log('progress url failed ');
7503 // run get Values on the form, so it syncs any secondary forms.
7504 this.form.getValues();
7506 var o = this.options;
7507 var method = this.getMethod();
7508 var isPost = method == 'POST';
7509 if(o.clientValidation === false || this.form.isValid()){
7511 if (this.form.progressUrl) {
7512 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7513 (new Date() * 1) + '' + Math.random());
7518 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7519 form:this.form.el.dom,
7520 url:this.getUrl(!isPost),
7522 params:isPost ? this.getParams() : null,
7523 isUpload: this.form.fileUpload
7526 this.uploadProgress();
7528 }else if (o.clientValidation !== false){ // client validation failed
7529 this.failureType = Roo.form.Action.CLIENT_INVALID;
7530 this.form.afterAction(this, false);
7534 success : function(response)
7536 this.uploadComplete= true;
7537 if (this.haveProgress) {
7538 Roo.MessageBox.hide();
7542 var result = this.processResponse(response);
7543 if(result === true || result.success){
7544 this.form.afterAction(this, true);
7548 this.form.markInvalid(result.errors);
7549 this.failureType = Roo.form.Action.SERVER_INVALID;
7551 this.form.afterAction(this, false);
7553 failure : function(response)
7555 this.uploadComplete= true;
7556 if (this.haveProgress) {
7557 Roo.MessageBox.hide();
7560 this.response = response;
7561 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7562 this.form.afterAction(this, false);
7565 handleResponse : function(response){
7566 if(this.form.errorReader){
7567 var rs = this.form.errorReader.read(response);
7570 for(var i = 0, len = rs.records.length; i < len; i++) {
7571 var r = rs.records[i];
7575 if(errors.length < 1){
7579 success : rs.success,
7585 ret = Roo.decode(response.responseText);
7589 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7599 Roo.form.Action.Load = function(form, options){
7600 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7601 this.reader = this.form.reader;
7604 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7609 Roo.Ajax.request(Roo.apply(
7610 this.createCallback(), {
7611 method:this.getMethod(),
7612 url:this.getUrl(false),
7613 params:this.getParams()
7617 success : function(response){
7619 var result = this.processResponse(response);
7620 if(result === true || !result.success || !result.data){
7621 this.failureType = Roo.form.Action.LOAD_FAILURE;
7622 this.form.afterAction(this, false);
7625 this.form.clearInvalid();
7626 this.form.setValues(result.data);
7627 this.form.afterAction(this, true);
7630 handleResponse : function(response){
7631 if(this.form.reader){
7632 var rs = this.form.reader.read(response);
7633 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7635 success : rs.success,
7639 return Roo.decode(response.responseText);
7643 Roo.form.Action.ACTION_TYPES = {
7644 'load' : Roo.form.Action.Load,
7645 'submit' : Roo.form.Action.Submit
7654 * @class Roo.bootstrap.Form
7655 * @extends Roo.bootstrap.Component
7656 * Bootstrap Form class
7657 * @cfg {String} method GET | POST (default POST)
7658 * @cfg {String} labelAlign top | left (default top)
7659 * @cfg {String} align left | right - for navbars
7660 * @cfg {Boolean} loadMask load mask when submit (default true)
7665 * @param {Object} config The config object
7669 Roo.bootstrap.Form = function(config){
7671 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7673 Roo.bootstrap.Form.popover.apply();
7677 * @event clientvalidation
7678 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7679 * @param {Form} this
7680 * @param {Boolean} valid true if the form has passed client-side validation
7682 clientvalidation: true,
7684 * @event beforeaction
7685 * Fires before any action is performed. Return false to cancel the action.
7686 * @param {Form} this
7687 * @param {Action} action The action to be performed
7691 * @event actionfailed
7692 * Fires when an action fails.
7693 * @param {Form} this
7694 * @param {Action} action The action that failed
7696 actionfailed : true,
7698 * @event actioncomplete
7699 * Fires when an action is completed.
7700 * @param {Form} this
7701 * @param {Action} action The action that completed
7703 actioncomplete : true
7707 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7710 * @cfg {String} method
7711 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7716 * The URL to use for form actions if one isn't supplied in the action options.
7719 * @cfg {Boolean} fileUpload
7720 * Set to true if this form is a file upload.
7724 * @cfg {Object} baseParams
7725 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7729 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7733 * @cfg {Sting} align (left|right) for navbar forms
7738 activeAction : null,
7741 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7742 * element by passing it or its id or mask the form itself by passing in true.
7745 waitMsgTarget : false,
7750 * @cfg {Boolean} errorMask (true|false) default false
7755 * @cfg {Number} maskOffset Default 100
7760 * @cfg {Boolean} maskBody
7764 getAutoCreate : function(){
7768 method : this.method || 'POST',
7769 id : this.id || Roo.id(),
7772 if (this.parent().xtype.match(/^Nav/)) {
7773 cfg.cls = 'navbar-form navbar-' + this.align;
7777 if (this.labelAlign == 'left' ) {
7778 cfg.cls += ' form-horizontal';
7784 initEvents : function()
7786 this.el.on('submit', this.onSubmit, this);
7787 // this was added as random key presses on the form where triggering form submit.
7788 this.el.on('keypress', function(e) {
7789 if (e.getCharCode() != 13) {
7792 // we might need to allow it for textareas.. and some other items.
7793 // check e.getTarget().
7795 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7799 Roo.log("keypress blocked");
7807 onSubmit : function(e){
7812 * Returns true if client-side validation on the form is successful.
7815 isValid : function(){
7816 var items = this.getItems();
7820 items.each(function(f){
7828 if(!target && f.el.isVisible(true)){
7834 if(this.errorMask && !valid){
7835 Roo.bootstrap.Form.popover.mask(this, target);
7842 * Returns true if any fields in this form have changed since their original load.
7845 isDirty : function(){
7847 var items = this.getItems();
7848 items.each(function(f){
7858 * Performs a predefined action (submit or load) or custom actions you define on this form.
7859 * @param {String} actionName The name of the action type
7860 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7861 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7862 * accept other config options):
7864 Property Type Description
7865 ---------------- --------------- ----------------------------------------------------------------------------------
7866 url String The url for the action (defaults to the form's url)
7867 method String The form method to use (defaults to the form's method, or POST if not defined)
7868 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7869 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7870 validate the form on the client (defaults to false)
7872 * @return {BasicForm} this
7874 doAction : function(action, options){
7875 if(typeof action == 'string'){
7876 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7878 if(this.fireEvent('beforeaction', this, action) !== false){
7879 this.beforeAction(action);
7880 action.run.defer(100, action);
7886 beforeAction : function(action){
7887 var o = action.options;
7892 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7894 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7897 // not really supported yet.. ??
7899 //if(this.waitMsgTarget === true){
7900 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7901 //}else if(this.waitMsgTarget){
7902 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7903 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7905 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7911 afterAction : function(action, success){
7912 this.activeAction = null;
7913 var o = action.options;
7918 Roo.get(document.body).unmask();
7924 //if(this.waitMsgTarget === true){
7925 // this.el.unmask();
7926 //}else if(this.waitMsgTarget){
7927 // this.waitMsgTarget.unmask();
7929 // Roo.MessageBox.updateProgress(1);
7930 // Roo.MessageBox.hide();
7937 Roo.callback(o.success, o.scope, [this, action]);
7938 this.fireEvent('actioncomplete', this, action);
7942 // failure condition..
7943 // we have a scenario where updates need confirming.
7944 // eg. if a locking scenario exists..
7945 // we look for { errors : { needs_confirm : true }} in the response.
7947 (typeof(action.result) != 'undefined') &&
7948 (typeof(action.result.errors) != 'undefined') &&
7949 (typeof(action.result.errors.needs_confirm) != 'undefined')
7952 Roo.log("not supported yet");
7955 Roo.MessageBox.confirm(
7956 "Change requires confirmation",
7957 action.result.errorMsg,
7962 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7972 Roo.callback(o.failure, o.scope, [this, action]);
7973 // show an error message if no failed handler is set..
7974 if (!this.hasListener('actionfailed')) {
7975 Roo.log("need to add dialog support");
7977 Roo.MessageBox.alert("Error",
7978 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7979 action.result.errorMsg :
7980 "Saving Failed, please check your entries or try again"
7985 this.fireEvent('actionfailed', this, action);
7990 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7991 * @param {String} id The value to search for
7994 findField : function(id){
7995 var items = this.getItems();
7996 var field = items.get(id);
7998 items.each(function(f){
7999 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8006 return field || null;
8009 * Mark fields in this form invalid in bulk.
8010 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8011 * @return {BasicForm} this
8013 markInvalid : function(errors){
8014 if(errors instanceof Array){
8015 for(var i = 0, len = errors.length; i < len; i++){
8016 var fieldError = errors[i];
8017 var f = this.findField(fieldError.id);
8019 f.markInvalid(fieldError.msg);
8025 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8026 field.markInvalid(errors[id]);
8030 //Roo.each(this.childForms || [], function (f) {
8031 // f.markInvalid(errors);
8038 * Set values for fields in this form in bulk.
8039 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8040 * @return {BasicForm} this
8042 setValues : function(values){
8043 if(values instanceof Array){ // array of objects
8044 for(var i = 0, len = values.length; i < len; i++){
8046 var f = this.findField(v.id);
8048 f.setValue(v.value);
8049 if(this.trackResetOnLoad){
8050 f.originalValue = f.getValue();
8054 }else{ // object hash
8057 if(typeof values[id] != 'function' && (field = this.findField(id))){
8059 if (field.setFromData &&
8061 field.displayField &&
8062 // combos' with local stores can
8063 // be queried via setValue()
8064 // to set their value..
8065 (field.store && !field.store.isLocal)
8069 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8070 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8071 field.setFromData(sd);
8073 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8075 field.setFromData(values);
8078 field.setValue(values[id]);
8082 if(this.trackResetOnLoad){
8083 field.originalValue = field.getValue();
8089 //Roo.each(this.childForms || [], function (f) {
8090 // f.setValues(values);
8097 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8098 * they are returned as an array.
8099 * @param {Boolean} asString
8102 getValues : function(asString){
8103 //if (this.childForms) {
8104 // copy values from the child forms
8105 // Roo.each(this.childForms, function (f) {
8106 // this.setValues(f.getValues());
8112 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8113 if(asString === true){
8116 return Roo.urlDecode(fs);
8120 * Returns the fields in this form as an object with key/value pairs.
8121 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8124 getFieldValues : function(with_hidden)
8126 var items = this.getItems();
8128 items.each(function(f){
8134 var v = f.getValue();
8136 if (f.inputType =='radio') {
8137 if (typeof(ret[f.getName()]) == 'undefined') {
8138 ret[f.getName()] = ''; // empty..
8141 if (!f.el.dom.checked) {
8149 if(f.xtype == 'MoneyField'){
8150 ret[f.currencyName] = f.getCurrency();
8153 // not sure if this supported any more..
8154 if ((typeof(v) == 'object') && f.getRawValue) {
8155 v = f.getRawValue() ; // dates..
8157 // combo boxes where name != hiddenName...
8158 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8159 ret[f.name] = f.getRawValue();
8161 ret[f.getName()] = v;
8168 * Clears all invalid messages in this form.
8169 * @return {BasicForm} this
8171 clearInvalid : function(){
8172 var items = this.getItems();
8174 items.each(function(f){
8183 * @return {BasicForm} this
8186 var items = this.getItems();
8187 items.each(function(f){
8191 Roo.each(this.childForms || [], function (f) {
8199 getItems : function()
8201 var r=new Roo.util.MixedCollection(false, function(o){
8202 return o.id || (o.id = Roo.id());
8204 var iter = function(el) {
8211 Roo.each(el.items,function(e) {
8220 hideFields : function(items)
8222 Roo.each(items, function(i){
8224 var f = this.findField(i);
8230 if(f.xtype == 'DateField'){
8231 f.setVisible(false);
8240 showFields : function(items)
8242 Roo.each(items, function(i){
8244 var f = this.findField(i);
8250 if(f.xtype == 'DateField'){
8262 Roo.apply(Roo.bootstrap.Form, {
8289 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8290 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8291 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8292 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8295 this.maskEl.top.enableDisplayMode("block");
8296 this.maskEl.left.enableDisplayMode("block");
8297 this.maskEl.bottom.enableDisplayMode("block");
8298 this.maskEl.right.enableDisplayMode("block");
8300 this.toolTip = new Roo.bootstrap.Tooltip({
8301 cls : 'roo-form-error-popover',
8303 'left' : ['r-l', [-2,0], 'right'],
8304 'right' : ['l-r', [2,0], 'left'],
8305 'bottom' : ['tl-bl', [0,2], 'top'],
8306 'top' : [ 'bl-tl', [0,-2], 'bottom']
8310 this.toolTip.render(Roo.get(document.body));
8312 this.toolTip.el.enableDisplayMode("block");
8314 Roo.get(document.body).on('click', function(){
8318 Roo.get(document.body).on('touchstart', function(){
8322 this.isApplied = true
8325 mask : function(form, target)
8329 this.target = target;
8331 if(!this.form.errorMask || !target.el){
8335 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8337 Roo.log(scrollable);
8339 var ot = this.target.el.calcOffsetsTo(scrollable);
8341 var scrollTo = ot[1] - this.form.maskOffset;
8343 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8345 scrollable.scrollTo('top', scrollTo);
8347 var box = this.target.el.getBox();
8349 var zIndex = Roo.bootstrap.Modal.zIndex++;
8352 this.maskEl.top.setStyle('position', 'absolute');
8353 this.maskEl.top.setStyle('z-index', zIndex);
8354 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8355 this.maskEl.top.setLeft(0);
8356 this.maskEl.top.setTop(0);
8357 this.maskEl.top.show();
8359 this.maskEl.left.setStyle('position', 'absolute');
8360 this.maskEl.left.setStyle('z-index', zIndex);
8361 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8362 this.maskEl.left.setLeft(0);
8363 this.maskEl.left.setTop(box.y - this.padding);
8364 this.maskEl.left.show();
8366 this.maskEl.bottom.setStyle('position', 'absolute');
8367 this.maskEl.bottom.setStyle('z-index', zIndex);
8368 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8369 this.maskEl.bottom.setLeft(0);
8370 this.maskEl.bottom.setTop(box.bottom + this.padding);
8371 this.maskEl.bottom.show();
8373 this.maskEl.right.setStyle('position', 'absolute');
8374 this.maskEl.right.setStyle('z-index', zIndex);
8375 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8376 this.maskEl.right.setLeft(box.right + this.padding);
8377 this.maskEl.right.setTop(box.y - this.padding);
8378 this.maskEl.right.show();
8380 this.toolTip.bindEl = this.target.el;
8382 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8384 var tip = this.target.blankText;
8386 if(this.target.getValue() !== '' ) {
8388 if (this.target.invalidText.length) {
8389 tip = this.target.invalidText;
8390 } else if (this.target.regexText.length){
8391 tip = this.target.regexText;
8395 this.toolTip.show(tip);
8397 this.intervalID = window.setInterval(function() {
8398 Roo.bootstrap.Form.popover.unmask();
8401 window.onwheel = function(){ return false;};
8403 (function(){ this.isMasked = true; }).defer(500, this);
8409 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8413 this.maskEl.top.setStyle('position', 'absolute');
8414 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8415 this.maskEl.top.hide();
8417 this.maskEl.left.setStyle('position', 'absolute');
8418 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8419 this.maskEl.left.hide();
8421 this.maskEl.bottom.setStyle('position', 'absolute');
8422 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8423 this.maskEl.bottom.hide();
8425 this.maskEl.right.setStyle('position', 'absolute');
8426 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8427 this.maskEl.right.hide();
8429 this.toolTip.hide();
8431 this.toolTip.el.hide();
8433 window.onwheel = function(){ return true;};
8435 if(this.intervalID){
8436 window.clearInterval(this.intervalID);
8437 this.intervalID = false;
8440 this.isMasked = false;
8450 * Ext JS Library 1.1.1
8451 * Copyright(c) 2006-2007, Ext JS, LLC.
8453 * Originally Released Under LGPL - original licence link has changed is not relivant.
8456 * <script type="text/javascript">
8459 * @class Roo.form.VTypes
8460 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8463 Roo.form.VTypes = function(){
8464 // closure these in so they are only created once.
8465 var alpha = /^[a-zA-Z_]+$/;
8466 var alphanum = /^[a-zA-Z0-9_]+$/;
8467 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8468 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8470 // All these messages and functions are configurable
8473 * The function used to validate email addresses
8474 * @param {String} value The email address
8476 'email' : function(v){
8477 return email.test(v);
8480 * The error text to display when the email validation function returns false
8483 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8485 * The keystroke filter mask to be applied on email input
8488 'emailMask' : /[a-z0-9_\.\-@]/i,
8491 * The function used to validate URLs
8492 * @param {String} value The URL
8494 'url' : function(v){
8498 * The error text to display when the url validation function returns false
8501 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8504 * The function used to validate alpha values
8505 * @param {String} value The value
8507 'alpha' : function(v){
8508 return alpha.test(v);
8511 * The error text to display when the alpha validation function returns false
8514 'alphaText' : 'This field should only contain letters and _',
8516 * The keystroke filter mask to be applied on alpha input
8519 'alphaMask' : /[a-z_]/i,
8522 * The function used to validate alphanumeric values
8523 * @param {String} value The value
8525 'alphanum' : function(v){
8526 return alphanum.test(v);
8529 * The error text to display when the alphanumeric validation function returns false
8532 'alphanumText' : 'This field should only contain letters, numbers and _',
8534 * The keystroke filter mask to be applied on alphanumeric input
8537 'alphanumMask' : /[a-z0-9_]/i
8547 * @class Roo.bootstrap.Input
8548 * @extends Roo.bootstrap.Component
8549 * Bootstrap Input class
8550 * @cfg {Boolean} disabled is it disabled
8551 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8552 * @cfg {String} name name of the input
8553 * @cfg {string} fieldLabel - the label associated
8554 * @cfg {string} placeholder - placeholder to put in text.
8555 * @cfg {string} before - input group add on before
8556 * @cfg {string} after - input group add on after
8557 * @cfg {string} size - (lg|sm) or leave empty..
8558 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8559 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8560 * @cfg {Number} md colspan out of 12 for computer-sized screens
8561 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8562 * @cfg {string} value default value of the input
8563 * @cfg {Number} labelWidth set the width of label
8564 * @cfg {Number} labellg set the width of label (1-12)
8565 * @cfg {Number} labelmd set the width of label (1-12)
8566 * @cfg {Number} labelsm set the width of label (1-12)
8567 * @cfg {Number} labelxs set the width of label (1-12)
8568 * @cfg {String} labelAlign (top|left)
8569 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8570 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8571 * @cfg {String} indicatorpos (left|right) default left
8572 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8573 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8575 * @cfg {String} align (left|center|right) Default left
8576 * @cfg {Boolean} forceFeedback (true|false) Default false
8579 * Create a new Input
8580 * @param {Object} config The config object
8583 Roo.bootstrap.Input = function(config){
8585 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8590 * Fires when this field receives input focus.
8591 * @param {Roo.form.Field} this
8596 * Fires when this field loses input focus.
8597 * @param {Roo.form.Field} this
8602 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8603 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8604 * @param {Roo.form.Field} this
8605 * @param {Roo.EventObject} e The event object
8610 * Fires just before the field blurs if the field value has changed.
8611 * @param {Roo.form.Field} this
8612 * @param {Mixed} newValue The new value
8613 * @param {Mixed} oldValue The original value
8618 * Fires after the field has been marked as invalid.
8619 * @param {Roo.form.Field} this
8620 * @param {String} msg The validation message
8625 * Fires after the field has been validated with no errors.
8626 * @param {Roo.form.Field} this
8631 * Fires after the key up
8632 * @param {Roo.form.Field} this
8633 * @param {Roo.EventObject} e The event Object
8639 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8641 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8642 automatic validation (defaults to "keyup").
8644 validationEvent : "keyup",
8646 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8648 validateOnBlur : true,
8650 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8652 validationDelay : 250,
8654 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8656 focusClass : "x-form-focus", // not needed???
8660 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8662 invalidClass : "has-warning",
8665 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8667 validClass : "has-success",
8670 * @cfg {Boolean} hasFeedback (true|false) default true
8675 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8677 invalidFeedbackClass : "glyphicon-warning-sign",
8680 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8682 validFeedbackClass : "glyphicon-ok",
8685 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8687 selectOnFocus : false,
8690 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8694 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8699 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8701 disableKeyFilter : false,
8704 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8708 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8712 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8714 blankText : "Please complete this mandatory field",
8717 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8721 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8723 maxLength : Number.MAX_VALUE,
8725 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8727 minLengthText : "The minimum length for this field is {0}",
8729 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8731 maxLengthText : "The maximum length for this field is {0}",
8735 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8736 * If available, this function will be called only after the basic validators all return true, and will be passed the
8737 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8741 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8742 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8743 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8747 * @cfg {String} regexText -- Depricated - use Invalid Text
8752 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8758 autocomplete: false,
8777 formatedValue : false,
8778 forceFeedback : false,
8780 indicatorpos : 'left',
8790 parentLabelAlign : function()
8793 while (parent.parent()) {
8794 parent = parent.parent();
8795 if (typeof(parent.labelAlign) !='undefined') {
8796 return parent.labelAlign;
8803 getAutoCreate : function()
8805 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8811 if(this.inputType != 'hidden'){
8812 cfg.cls = 'form-group' //input-group
8818 type : this.inputType,
8820 cls : 'form-control',
8821 placeholder : this.placeholder || '',
8822 autocomplete : this.autocomplete || 'new-password'
8825 if(this.capture.length){
8826 input.capture = this.capture;
8829 if(this.accept.length){
8830 input.accept = this.accept + "/*";
8834 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8837 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8838 input.maxLength = this.maxLength;
8841 if (this.disabled) {
8842 input.disabled=true;
8845 if (this.readOnly) {
8846 input.readonly=true;
8850 input.name = this.name;
8854 input.cls += ' input-' + this.size;
8858 ['xs','sm','md','lg'].map(function(size){
8859 if (settings[size]) {
8860 cfg.cls += ' col-' + size + '-' + settings[size];
8864 var inputblock = input;
8868 cls: 'glyphicon form-control-feedback'
8871 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8874 cls : 'has-feedback',
8882 if (this.before || this.after) {
8885 cls : 'input-group',
8889 if (this.before && typeof(this.before) == 'string') {
8891 inputblock.cn.push({
8893 cls : 'roo-input-before input-group-addon',
8897 if (this.before && typeof(this.before) == 'object') {
8898 this.before = Roo.factory(this.before);
8900 inputblock.cn.push({
8902 cls : 'roo-input-before input-group-' +
8903 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8907 inputblock.cn.push(input);
8909 if (this.after && typeof(this.after) == 'string') {
8910 inputblock.cn.push({
8912 cls : 'roo-input-after input-group-addon',
8916 if (this.after && typeof(this.after) == 'object') {
8917 this.after = Roo.factory(this.after);
8919 inputblock.cn.push({
8921 cls : 'roo-input-after input-group-' +
8922 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8926 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8927 inputblock.cls += ' has-feedback';
8928 inputblock.cn.push(feedback);
8932 if (align ==='left' && this.fieldLabel.length) {
8934 cfg.cls += ' roo-form-group-label-left';
8939 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8940 tooltip : 'This field is required'
8945 cls : 'control-label',
8946 html : this.fieldLabel
8957 var labelCfg = cfg.cn[1];
8958 var contentCfg = cfg.cn[2];
8960 if(this.indicatorpos == 'right'){
8965 cls : 'control-label',
8969 html : this.fieldLabel
8973 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8974 tooltip : 'This field is required'
8987 labelCfg = cfg.cn[0];
8988 contentCfg = cfg.cn[1];
8992 if(this.labelWidth > 12){
8993 labelCfg.style = "width: " + this.labelWidth + 'px';
8996 if(this.labelWidth < 13 && this.labelmd == 0){
8997 this.labelmd = this.labelWidth;
9000 if(this.labellg > 0){
9001 labelCfg.cls += ' col-lg-' + this.labellg;
9002 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9005 if(this.labelmd > 0){
9006 labelCfg.cls += ' col-md-' + this.labelmd;
9007 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9010 if(this.labelsm > 0){
9011 labelCfg.cls += ' col-sm-' + this.labelsm;
9012 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9015 if(this.labelxs > 0){
9016 labelCfg.cls += ' col-xs-' + this.labelxs;
9017 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9021 } else if ( this.fieldLabel.length) {
9026 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9027 tooltip : 'This field is required'
9031 //cls : 'input-group-addon',
9032 html : this.fieldLabel
9040 if(this.indicatorpos == 'right'){
9045 //cls : 'input-group-addon',
9046 html : this.fieldLabel
9051 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9052 tooltip : 'This field is required'
9072 if (this.parentType === 'Navbar' && this.parent().bar) {
9073 cfg.cls += ' navbar-form';
9076 if (this.parentType === 'NavGroup') {
9077 cfg.cls += ' navbar-form';
9085 * return the real input element.
9087 inputEl: function ()
9089 return this.el.select('input.form-control',true).first();
9092 tooltipEl : function()
9094 return this.inputEl();
9097 indicatorEl : function()
9099 var indicator = this.el.select('i.roo-required-indicator',true).first();
9109 setDisabled : function(v)
9111 var i = this.inputEl().dom;
9113 i.removeAttribute('disabled');
9117 i.setAttribute('disabled','true');
9119 initEvents : function()
9122 this.inputEl().on("keydown" , this.fireKey, this);
9123 this.inputEl().on("focus", this.onFocus, this);
9124 this.inputEl().on("blur", this.onBlur, this);
9126 this.inputEl().relayEvent('keyup', this);
9128 this.indicator = this.indicatorEl();
9131 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9134 // reference to original value for reset
9135 this.originalValue = this.getValue();
9136 //Roo.form.TextField.superclass.initEvents.call(this);
9137 if(this.validationEvent == 'keyup'){
9138 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9139 this.inputEl().on('keyup', this.filterValidation, this);
9141 else if(this.validationEvent !== false){
9142 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9145 if(this.selectOnFocus){
9146 this.on("focus", this.preFocus, this);
9149 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9150 this.inputEl().on("keypress", this.filterKeys, this);
9152 this.inputEl().relayEvent('keypress', this);
9155 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9156 this.el.on("click", this.autoSize, this);
9159 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9160 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9163 if (typeof(this.before) == 'object') {
9164 this.before.render(this.el.select('.roo-input-before',true).first());
9166 if (typeof(this.after) == 'object') {
9167 this.after.render(this.el.select('.roo-input-after',true).first());
9170 this.inputEl().on('change', this.onChange, this);
9173 filterValidation : function(e){
9174 if(!e.isNavKeyPress()){
9175 this.validationTask.delay(this.validationDelay);
9179 * Validates the field value
9180 * @return {Boolean} True if the value is valid, else false
9182 validate : function(){
9183 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9184 if(this.disabled || this.validateValue(this.getRawValue())){
9195 * Validates a value according to the field's validation rules and marks the field as invalid
9196 * if the validation fails
9197 * @param {Mixed} value The value to validate
9198 * @return {Boolean} True if the value is valid, else false
9200 validateValue : function(value)
9202 if(this.getVisibilityEl().hasClass('hidden')){
9206 if(value.length < 1) { // if it's blank
9207 if(this.allowBlank){
9213 if(value.length < this.minLength){
9216 if(value.length > this.maxLength){
9220 var vt = Roo.form.VTypes;
9221 if(!vt[this.vtype](value, this)){
9225 if(typeof this.validator == "function"){
9226 var msg = this.validator(value);
9230 if (typeof(msg) == 'string') {
9231 this.invalidText = msg;
9235 if(this.regex && !this.regex.test(value)){
9243 fireKey : function(e){
9244 //Roo.log('field ' + e.getKey());
9245 if(e.isNavKeyPress()){
9246 this.fireEvent("specialkey", this, e);
9249 focus : function (selectText){
9251 this.inputEl().focus();
9252 if(selectText === true){
9253 this.inputEl().dom.select();
9259 onFocus : function(){
9260 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9261 // this.el.addClass(this.focusClass);
9264 this.hasFocus = true;
9265 this.startValue = this.getValue();
9266 this.fireEvent("focus", this);
9270 beforeBlur : Roo.emptyFn,
9274 onBlur : function(){
9276 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9277 //this.el.removeClass(this.focusClass);
9279 this.hasFocus = false;
9280 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9283 var v = this.getValue();
9284 if(String(v) !== String(this.startValue)){
9285 this.fireEvent('change', this, v, this.startValue);
9287 this.fireEvent("blur", this);
9290 onChange : function(e)
9292 var v = this.getValue();
9293 if(String(v) !== String(this.startValue)){
9294 this.fireEvent('change', this, v, this.startValue);
9300 * Resets the current field value to the originally loaded value and clears any validation messages
9303 this.setValue(this.originalValue);
9307 * Returns the name of the field
9308 * @return {Mixed} name The name field
9310 getName: function(){
9314 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9315 * @return {Mixed} value The field value
9317 getValue : function(){
9319 var v = this.inputEl().getValue();
9324 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9325 * @return {Mixed} value The field value
9327 getRawValue : function(){
9328 var v = this.inputEl().getValue();
9334 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9335 * @param {Mixed} value The value to set
9337 setRawValue : function(v){
9338 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9341 selectText : function(start, end){
9342 var v = this.getRawValue();
9344 start = start === undefined ? 0 : start;
9345 end = end === undefined ? v.length : end;
9346 var d = this.inputEl().dom;
9347 if(d.setSelectionRange){
9348 d.setSelectionRange(start, end);
9349 }else if(d.createTextRange){
9350 var range = d.createTextRange();
9351 range.moveStart("character", start);
9352 range.moveEnd("character", v.length-end);
9359 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9360 * @param {Mixed} value The value to set
9362 setValue : function(v){
9365 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9371 processValue : function(value){
9372 if(this.stripCharsRe){
9373 var newValue = value.replace(this.stripCharsRe, '');
9374 if(newValue !== value){
9375 this.setRawValue(newValue);
9382 preFocus : function(){
9384 if(this.selectOnFocus){
9385 this.inputEl().dom.select();
9388 filterKeys : function(e){
9390 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9393 var c = e.getCharCode(), cc = String.fromCharCode(c);
9394 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9397 if(!this.maskRe.test(cc)){
9402 * Clear any invalid styles/messages for this field
9404 clearInvalid : function(){
9406 if(!this.el || this.preventMark){ // not rendered
9411 this.el.removeClass(this.invalidClass);
9413 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9415 var feedback = this.el.select('.form-control-feedback', true).first();
9418 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9424 this.indicator.removeClass('visible');
9425 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9428 this.fireEvent('valid', this);
9432 * Mark this field as valid
9434 markValid : function()
9436 if(!this.el || this.preventMark){ // not rendered...
9440 this.el.removeClass([this.invalidClass, this.validClass]);
9442 var feedback = this.el.select('.form-control-feedback', true).first();
9445 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9449 this.indicator.removeClass('visible');
9450 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9457 if(this.allowBlank && !this.getRawValue().length){
9461 this.el.addClass(this.validClass);
9463 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9465 var feedback = this.el.select('.form-control-feedback', true).first();
9468 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9469 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9474 this.fireEvent('valid', this);
9478 * Mark this field as invalid
9479 * @param {String} msg The validation message
9481 markInvalid : function(msg)
9483 if(!this.el || this.preventMark){ // not rendered
9487 this.el.removeClass([this.invalidClass, this.validClass]);
9489 var feedback = this.el.select('.form-control-feedback', true).first();
9492 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9499 if(this.allowBlank && !this.getRawValue().length){
9504 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9505 this.indicator.addClass('visible');
9508 this.el.addClass(this.invalidClass);
9510 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9512 var feedback = this.el.select('.form-control-feedback', true).first();
9515 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9517 if(this.getValue().length || this.forceFeedback){
9518 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9525 this.fireEvent('invalid', this, msg);
9528 SafariOnKeyDown : function(event)
9530 // this is a workaround for a password hang bug on chrome/ webkit.
9531 if (this.inputEl().dom.type != 'password') {
9535 var isSelectAll = false;
9537 if(this.inputEl().dom.selectionEnd > 0){
9538 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9540 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9541 event.preventDefault();
9546 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9548 event.preventDefault();
9549 // this is very hacky as keydown always get's upper case.
9551 var cc = String.fromCharCode(event.getCharCode());
9552 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9556 adjustWidth : function(tag, w){
9557 tag = tag.toLowerCase();
9558 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9559 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9563 if(tag == 'textarea'){
9566 }else if(Roo.isOpera){
9570 if(tag == 'textarea'){
9578 setFieldLabel : function(v)
9585 var ar = this.el.select('label > span',true);
9587 if (ar.elements.length) {
9588 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9589 this.fieldLabel = v;
9593 var br = this.el.select('label',true);
9595 if(br.elements.length) {
9596 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9597 this.fieldLabel = v;
9601 Roo.log('Cannot Found any of label > span || label in input');
9605 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9606 this.fieldLabel = v;
9621 * @class Roo.bootstrap.TextArea
9622 * @extends Roo.bootstrap.Input
9623 * Bootstrap TextArea class
9624 * @cfg {Number} cols Specifies the visible width of a text area
9625 * @cfg {Number} rows Specifies the visible number of lines in a text area
9626 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9627 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9628 * @cfg {string} html text
9631 * Create a new TextArea
9632 * @param {Object} config The config object
9635 Roo.bootstrap.TextArea = function(config){
9636 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9640 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9650 getAutoCreate : function(){
9652 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9658 if(this.inputType != 'hidden'){
9659 cfg.cls = 'form-group' //input-group
9667 value : this.value || '',
9668 html: this.html || '',
9669 cls : 'form-control',
9670 placeholder : this.placeholder || ''
9674 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9675 input.maxLength = this.maxLength;
9679 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9683 input.cols = this.cols;
9686 if (this.readOnly) {
9687 input.readonly = true;
9691 input.name = this.name;
9695 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9699 ['xs','sm','md','lg'].map(function(size){
9700 if (settings[size]) {
9701 cfg.cls += ' col-' + size + '-' + settings[size];
9705 var inputblock = input;
9707 if(this.hasFeedback && !this.allowBlank){
9711 cls: 'glyphicon form-control-feedback'
9715 cls : 'has-feedback',
9724 if (this.before || this.after) {
9727 cls : 'input-group',
9731 inputblock.cn.push({
9733 cls : 'input-group-addon',
9738 inputblock.cn.push(input);
9740 if(this.hasFeedback && !this.allowBlank){
9741 inputblock.cls += ' has-feedback';
9742 inputblock.cn.push(feedback);
9746 inputblock.cn.push({
9748 cls : 'input-group-addon',
9755 if (align ==='left' && this.fieldLabel.length) {
9760 cls : 'control-label',
9761 html : this.fieldLabel
9772 if(this.labelWidth > 12){
9773 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9776 if(this.labelWidth < 13 && this.labelmd == 0){
9777 this.labelmd = this.labelWidth;
9780 if(this.labellg > 0){
9781 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9782 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9785 if(this.labelmd > 0){
9786 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9787 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9790 if(this.labelsm > 0){
9791 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9792 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9795 if(this.labelxs > 0){
9796 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9797 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9800 } else if ( this.fieldLabel.length) {
9805 //cls : 'input-group-addon',
9806 html : this.fieldLabel
9824 if (this.disabled) {
9825 input.disabled=true;
9832 * return the real textarea element.
9834 inputEl: function ()
9836 return this.el.select('textarea.form-control',true).first();
9840 * Clear any invalid styles/messages for this field
9842 clearInvalid : function()
9845 if(!this.el || this.preventMark){ // not rendered
9849 var label = this.el.select('label', true).first();
9850 var icon = this.el.select('i.fa-star', true).first();
9856 this.el.removeClass(this.invalidClass);
9858 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9860 var feedback = this.el.select('.form-control-feedback', true).first();
9863 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9868 this.fireEvent('valid', this);
9872 * Mark this field as valid
9874 markValid : function()
9876 if(!this.el || this.preventMark){ // not rendered
9880 this.el.removeClass([this.invalidClass, this.validClass]);
9882 var feedback = this.el.select('.form-control-feedback', true).first();
9885 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9888 if(this.disabled || this.allowBlank){
9892 var label = this.el.select('label', true).first();
9893 var icon = this.el.select('i.fa-star', true).first();
9899 this.el.addClass(this.validClass);
9901 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9903 var feedback = this.el.select('.form-control-feedback', true).first();
9906 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9907 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9912 this.fireEvent('valid', this);
9916 * Mark this field as invalid
9917 * @param {String} msg The validation message
9919 markInvalid : function(msg)
9921 if(!this.el || this.preventMark){ // not rendered
9925 this.el.removeClass([this.invalidClass, this.validClass]);
9927 var feedback = this.el.select('.form-control-feedback', true).first();
9930 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9933 if(this.disabled || this.allowBlank){
9937 var label = this.el.select('label', true).first();
9938 var icon = this.el.select('i.fa-star', true).first();
9940 if(!this.getValue().length && label && !icon){
9941 this.el.createChild({
9943 cls : 'text-danger fa fa-lg fa-star',
9944 tooltip : 'This field is required',
9945 style : 'margin-right:5px;'
9949 this.el.addClass(this.invalidClass);
9951 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9953 var feedback = this.el.select('.form-control-feedback', true).first();
9956 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9958 if(this.getValue().length || this.forceFeedback){
9959 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9966 this.fireEvent('invalid', this, msg);
9974 * trigger field - base class for combo..
9979 * @class Roo.bootstrap.TriggerField
9980 * @extends Roo.bootstrap.Input
9981 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9982 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9983 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9984 * for which you can provide a custom implementation. For example:
9986 var trigger = new Roo.bootstrap.TriggerField();
9987 trigger.onTriggerClick = myTriggerFn;
9988 trigger.applyTo('my-field');
9991 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9992 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9993 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9994 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9995 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9998 * Create a new TriggerField.
9999 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10000 * to the base TextField)
10002 Roo.bootstrap.TriggerField = function(config){
10003 this.mimicing = false;
10004 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10007 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10009 * @cfg {String} triggerClass A CSS class to apply to the trigger
10012 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10017 * @cfg {Boolean} removable (true|false) special filter default false
10021 /** @cfg {Boolean} grow @hide */
10022 /** @cfg {Number} growMin @hide */
10023 /** @cfg {Number} growMax @hide */
10029 autoSize: Roo.emptyFn,
10033 deferHeight : true,
10036 actionMode : 'wrap',
10041 getAutoCreate : function(){
10043 var align = this.labelAlign || this.parentLabelAlign();
10048 cls: 'form-group' //input-group
10055 type : this.inputType,
10056 cls : 'form-control',
10057 autocomplete: 'new-password',
10058 placeholder : this.placeholder || ''
10062 input.name = this.name;
10065 input.cls += ' input-' + this.size;
10068 if (this.disabled) {
10069 input.disabled=true;
10072 var inputblock = input;
10074 if(this.hasFeedback && !this.allowBlank){
10078 cls: 'glyphicon form-control-feedback'
10081 if(this.removable && !this.editable && !this.tickable){
10083 cls : 'has-feedback',
10089 cls : 'roo-combo-removable-btn close'
10096 cls : 'has-feedback',
10105 if(this.removable && !this.editable && !this.tickable){
10107 cls : 'roo-removable',
10113 cls : 'roo-combo-removable-btn close'
10120 if (this.before || this.after) {
10123 cls : 'input-group',
10127 inputblock.cn.push({
10129 cls : 'input-group-addon',
10134 inputblock.cn.push(input);
10136 if(this.hasFeedback && !this.allowBlank){
10137 inputblock.cls += ' has-feedback';
10138 inputblock.cn.push(feedback);
10142 inputblock.cn.push({
10144 cls : 'input-group-addon',
10157 cls: 'form-hidden-field'
10171 cls: 'form-hidden-field'
10175 cls: 'roo-select2-choices',
10179 cls: 'roo-select2-search-field',
10192 cls: 'roo-select2-container input-group',
10197 // cls: 'typeahead typeahead-long dropdown-menu',
10198 // style: 'display:none'
10203 if(!this.multiple && this.showToggleBtn){
10209 if (this.caret != false) {
10212 cls: 'fa fa-' + this.caret
10219 cls : 'input-group-addon btn dropdown-toggle',
10224 cls: 'combobox-clear',
10238 combobox.cls += ' roo-select2-container-multi';
10241 if (align ==='left' && this.fieldLabel.length) {
10243 cfg.cls += ' roo-form-group-label-left';
10248 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10249 tooltip : 'This field is required'
10254 cls : 'control-label',
10255 html : this.fieldLabel
10267 var labelCfg = cfg.cn[1];
10268 var contentCfg = cfg.cn[2];
10270 if(this.indicatorpos == 'right'){
10275 cls : 'control-label',
10279 html : this.fieldLabel
10283 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10284 tooltip : 'This field is required'
10297 labelCfg = cfg.cn[0];
10298 contentCfg = cfg.cn[1];
10301 if(this.labelWidth > 12){
10302 labelCfg.style = "width: " + this.labelWidth + 'px';
10305 if(this.labelWidth < 13 && this.labelmd == 0){
10306 this.labelmd = this.labelWidth;
10309 if(this.labellg > 0){
10310 labelCfg.cls += ' col-lg-' + this.labellg;
10311 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10314 if(this.labelmd > 0){
10315 labelCfg.cls += ' col-md-' + this.labelmd;
10316 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10319 if(this.labelsm > 0){
10320 labelCfg.cls += ' col-sm-' + this.labelsm;
10321 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10324 if(this.labelxs > 0){
10325 labelCfg.cls += ' col-xs-' + this.labelxs;
10326 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10329 } else if ( this.fieldLabel.length) {
10330 // Roo.log(" label");
10334 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10335 tooltip : 'This field is required'
10339 //cls : 'input-group-addon',
10340 html : this.fieldLabel
10348 if(this.indicatorpos == 'right'){
10356 html : this.fieldLabel
10360 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10361 tooltip : 'This field is required'
10374 // Roo.log(" no label && no align");
10381 ['xs','sm','md','lg'].map(function(size){
10382 if (settings[size]) {
10383 cfg.cls += ' col-' + size + '-' + settings[size];
10394 onResize : function(w, h){
10395 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10396 // if(typeof w == 'number'){
10397 // var x = w - this.trigger.getWidth();
10398 // this.inputEl().setWidth(this.adjustWidth('input', x));
10399 // this.trigger.setStyle('left', x+'px');
10404 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10407 getResizeEl : function(){
10408 return this.inputEl();
10412 getPositionEl : function(){
10413 return this.inputEl();
10417 alignErrorIcon : function(){
10418 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10422 initEvents : function(){
10426 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10427 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10428 if(!this.multiple && this.showToggleBtn){
10429 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10430 if(this.hideTrigger){
10431 this.trigger.setDisplayed(false);
10433 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10437 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10440 if(this.removable && !this.editable && !this.tickable){
10441 var close = this.closeTriggerEl();
10444 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10445 close.on('click', this.removeBtnClick, this, close);
10449 //this.trigger.addClassOnOver('x-form-trigger-over');
10450 //this.trigger.addClassOnClick('x-form-trigger-click');
10453 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10457 closeTriggerEl : function()
10459 var close = this.el.select('.roo-combo-removable-btn', true).first();
10460 return close ? close : false;
10463 removeBtnClick : function(e, h, el)
10465 e.preventDefault();
10467 if(this.fireEvent("remove", this) !== false){
10469 this.fireEvent("afterremove", this)
10473 createList : function()
10475 this.list = Roo.get(document.body).createChild({
10477 cls: 'typeahead typeahead-long dropdown-menu',
10478 style: 'display:none'
10481 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10486 initTrigger : function(){
10491 onDestroy : function(){
10493 this.trigger.removeAllListeners();
10494 // this.trigger.remove();
10497 // this.wrap.remove();
10499 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10503 onFocus : function(){
10504 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10506 if(!this.mimicing){
10507 this.wrap.addClass('x-trigger-wrap-focus');
10508 this.mimicing = true;
10509 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10510 if(this.monitorTab){
10511 this.el.on("keydown", this.checkTab, this);
10518 checkTab : function(e){
10519 if(e.getKey() == e.TAB){
10520 this.triggerBlur();
10525 onBlur : function(){
10530 mimicBlur : function(e, t){
10532 if(!this.wrap.contains(t) && this.validateBlur()){
10533 this.triggerBlur();
10539 triggerBlur : function(){
10540 this.mimicing = false;
10541 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10542 if(this.monitorTab){
10543 this.el.un("keydown", this.checkTab, this);
10545 //this.wrap.removeClass('x-trigger-wrap-focus');
10546 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10550 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10551 validateBlur : function(e, t){
10556 onDisable : function(){
10557 this.inputEl().dom.disabled = true;
10558 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10560 // this.wrap.addClass('x-item-disabled');
10565 onEnable : function(){
10566 this.inputEl().dom.disabled = false;
10567 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10569 // this.el.removeClass('x-item-disabled');
10574 onShow : function(){
10575 var ae = this.getActionEl();
10578 ae.dom.style.display = '';
10579 ae.dom.style.visibility = 'visible';
10585 onHide : function(){
10586 var ae = this.getActionEl();
10587 ae.dom.style.display = 'none';
10591 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10592 * by an implementing function.
10594 * @param {EventObject} e
10596 onTriggerClick : Roo.emptyFn
10600 * Ext JS Library 1.1.1
10601 * Copyright(c) 2006-2007, Ext JS, LLC.
10603 * Originally Released Under LGPL - original licence link has changed is not relivant.
10606 * <script type="text/javascript">
10611 * @class Roo.data.SortTypes
10613 * Defines the default sorting (casting?) comparison functions used when sorting data.
10615 Roo.data.SortTypes = {
10617 * Default sort that does nothing
10618 * @param {Mixed} s The value being converted
10619 * @return {Mixed} The comparison value
10621 none : function(s){
10626 * The regular expression used to strip tags
10630 stripTagsRE : /<\/?[^>]+>/gi,
10633 * Strips all HTML tags to sort on text only
10634 * @param {Mixed} s The value being converted
10635 * @return {String} The comparison value
10637 asText : function(s){
10638 return String(s).replace(this.stripTagsRE, "");
10642 * Strips all HTML tags to sort on text only - Case insensitive
10643 * @param {Mixed} s The value being converted
10644 * @return {String} The comparison value
10646 asUCText : function(s){
10647 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10651 * Case insensitive string
10652 * @param {Mixed} s The value being converted
10653 * @return {String} The comparison value
10655 asUCString : function(s) {
10656 return String(s).toUpperCase();
10661 * @param {Mixed} s The value being converted
10662 * @return {Number} The comparison value
10664 asDate : function(s) {
10668 if(s instanceof Date){
10669 return s.getTime();
10671 return Date.parse(String(s));
10676 * @param {Mixed} s The value being converted
10677 * @return {Float} The comparison value
10679 asFloat : function(s) {
10680 var val = parseFloat(String(s).replace(/,/g, ""));
10689 * @param {Mixed} s The value being converted
10690 * @return {Number} The comparison value
10692 asInt : function(s) {
10693 var val = parseInt(String(s).replace(/,/g, ""));
10701 * Ext JS Library 1.1.1
10702 * Copyright(c) 2006-2007, Ext JS, LLC.
10704 * Originally Released Under LGPL - original licence link has changed is not relivant.
10707 * <script type="text/javascript">
10711 * @class Roo.data.Record
10712 * Instances of this class encapsulate both record <em>definition</em> information, and record
10713 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10714 * to access Records cached in an {@link Roo.data.Store} object.<br>
10716 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10717 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10720 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10722 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10723 * {@link #create}. The parameters are the same.
10724 * @param {Array} data An associative Array of data values keyed by the field name.
10725 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10726 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10727 * not specified an integer id is generated.
10729 Roo.data.Record = function(data, id){
10730 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10735 * Generate a constructor for a specific record layout.
10736 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10737 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10738 * Each field definition object may contain the following properties: <ul>
10739 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10740 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10741 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10742 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10743 * is being used, then this is a string containing the javascript expression to reference the data relative to
10744 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10745 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10746 * this may be omitted.</p></li>
10747 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10748 * <ul><li>auto (Default, implies no conversion)</li>
10753 * <li>date</li></ul></p></li>
10754 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10755 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10756 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10757 * by the Reader into an object that will be stored in the Record. It is passed the
10758 * following parameters:<ul>
10759 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10761 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10763 * <br>usage:<br><pre><code>
10764 var TopicRecord = Roo.data.Record.create(
10765 {name: 'title', mapping: 'topic_title'},
10766 {name: 'author', mapping: 'username'},
10767 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10768 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10769 {name: 'lastPoster', mapping: 'user2'},
10770 {name: 'excerpt', mapping: 'post_text'}
10773 var myNewRecord = new TopicRecord({
10774 title: 'Do my job please',
10777 lastPost: new Date(),
10778 lastPoster: 'Animal',
10779 excerpt: 'No way dude!'
10781 myStore.add(myNewRecord);
10786 Roo.data.Record.create = function(o){
10787 var f = function(){
10788 f.superclass.constructor.apply(this, arguments);
10790 Roo.extend(f, Roo.data.Record);
10791 var p = f.prototype;
10792 p.fields = new Roo.util.MixedCollection(false, function(field){
10795 for(var i = 0, len = o.length; i < len; i++){
10796 p.fields.add(new Roo.data.Field(o[i]));
10798 f.getField = function(name){
10799 return p.fields.get(name);
10804 Roo.data.Record.AUTO_ID = 1000;
10805 Roo.data.Record.EDIT = 'edit';
10806 Roo.data.Record.REJECT = 'reject';
10807 Roo.data.Record.COMMIT = 'commit';
10809 Roo.data.Record.prototype = {
10811 * Readonly flag - true if this record has been modified.
10820 join : function(store){
10821 this.store = store;
10825 * Set the named field to the specified value.
10826 * @param {String} name The name of the field to set.
10827 * @param {Object} value The value to set the field to.
10829 set : function(name, value){
10830 if(this.data[name] == value){
10834 if(!this.modified){
10835 this.modified = {};
10837 if(typeof this.modified[name] == 'undefined'){
10838 this.modified[name] = this.data[name];
10840 this.data[name] = value;
10841 if(!this.editing && this.store){
10842 this.store.afterEdit(this);
10847 * Get the value of the named field.
10848 * @param {String} name The name of the field to get the value of.
10849 * @return {Object} The value of the field.
10851 get : function(name){
10852 return this.data[name];
10856 beginEdit : function(){
10857 this.editing = true;
10858 this.modified = {};
10862 cancelEdit : function(){
10863 this.editing = false;
10864 delete this.modified;
10868 endEdit : function(){
10869 this.editing = false;
10870 if(this.dirty && this.store){
10871 this.store.afterEdit(this);
10876 * Usually called by the {@link Roo.data.Store} which owns the Record.
10877 * Rejects all changes made to the Record since either creation, or the last commit operation.
10878 * Modified fields are reverted to their original values.
10880 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10881 * of reject operations.
10883 reject : function(){
10884 var m = this.modified;
10886 if(typeof m[n] != "function"){
10887 this.data[n] = m[n];
10890 this.dirty = false;
10891 delete this.modified;
10892 this.editing = false;
10894 this.store.afterReject(this);
10899 * Usually called by the {@link Roo.data.Store} which owns the Record.
10900 * Commits all changes made to the Record since either creation, or the last commit operation.
10902 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10903 * of commit operations.
10905 commit : function(){
10906 this.dirty = false;
10907 delete this.modified;
10908 this.editing = false;
10910 this.store.afterCommit(this);
10915 hasError : function(){
10916 return this.error != null;
10920 clearError : function(){
10925 * Creates a copy of this record.
10926 * @param {String} id (optional) A new record id if you don't want to use this record's id
10929 copy : function(newId) {
10930 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10934 * Ext JS Library 1.1.1
10935 * Copyright(c) 2006-2007, Ext JS, LLC.
10937 * Originally Released Under LGPL - original licence link has changed is not relivant.
10940 * <script type="text/javascript">
10946 * @class Roo.data.Store
10947 * @extends Roo.util.Observable
10948 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10949 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10951 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10952 * has no knowledge of the format of the data returned by the Proxy.<br>
10954 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10955 * instances from the data object. These records are cached and made available through accessor functions.
10957 * Creates a new Store.
10958 * @param {Object} config A config object containing the objects needed for the Store to access data,
10959 * and read the data into Records.
10961 Roo.data.Store = function(config){
10962 this.data = new Roo.util.MixedCollection(false);
10963 this.data.getKey = function(o){
10966 this.baseParams = {};
10968 this.paramNames = {
10973 "multisort" : "_multisort"
10976 if(config && config.data){
10977 this.inlineData = config.data;
10978 delete config.data;
10981 Roo.apply(this, config);
10983 if(this.reader){ // reader passed
10984 this.reader = Roo.factory(this.reader, Roo.data);
10985 this.reader.xmodule = this.xmodule || false;
10986 if(!this.recordType){
10987 this.recordType = this.reader.recordType;
10989 if(this.reader.onMetaChange){
10990 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10994 if(this.recordType){
10995 this.fields = this.recordType.prototype.fields;
10997 this.modified = [];
11001 * @event datachanged
11002 * Fires when the data cache has changed, and a widget which is using this Store
11003 * as a Record cache should refresh its view.
11004 * @param {Store} this
11006 datachanged : true,
11008 * @event metachange
11009 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11010 * @param {Store} this
11011 * @param {Object} meta The JSON metadata
11016 * Fires when Records have been added to the Store
11017 * @param {Store} this
11018 * @param {Roo.data.Record[]} records The array of Records added
11019 * @param {Number} index The index at which the record(s) were added
11024 * Fires when a Record has been removed from the Store
11025 * @param {Store} this
11026 * @param {Roo.data.Record} record The Record that was removed
11027 * @param {Number} index The index at which the record was removed
11032 * Fires when a Record has been updated
11033 * @param {Store} this
11034 * @param {Roo.data.Record} record The Record that was updated
11035 * @param {String} operation The update operation being performed. Value may be one of:
11037 Roo.data.Record.EDIT
11038 Roo.data.Record.REJECT
11039 Roo.data.Record.COMMIT
11045 * Fires when the data cache has been cleared.
11046 * @param {Store} this
11050 * @event beforeload
11051 * Fires before a request is made for a new data object. If the beforeload handler returns false
11052 * the load action will be canceled.
11053 * @param {Store} this
11054 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11058 * @event beforeloadadd
11059 * Fires after a new set of Records has been loaded.
11060 * @param {Store} this
11061 * @param {Roo.data.Record[]} records The Records that were loaded
11062 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11064 beforeloadadd : true,
11067 * Fires after a new set of Records has been loaded, before they are added to the store.
11068 * @param {Store} this
11069 * @param {Roo.data.Record[]} records The Records that were loaded
11070 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11071 * @params {Object} return from reader
11075 * @event loadexception
11076 * Fires if an exception occurs in the Proxy during loading.
11077 * Called with the signature of the Proxy's "loadexception" event.
11078 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11081 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11082 * @param {Object} load options
11083 * @param {Object} jsonData from your request (normally this contains the Exception)
11085 loadexception : true
11089 this.proxy = Roo.factory(this.proxy, Roo.data);
11090 this.proxy.xmodule = this.xmodule || false;
11091 this.relayEvents(this.proxy, ["loadexception"]);
11093 this.sortToggle = {};
11094 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11096 Roo.data.Store.superclass.constructor.call(this);
11098 if(this.inlineData){
11099 this.loadData(this.inlineData);
11100 delete this.inlineData;
11104 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11106 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11107 * without a remote query - used by combo/forms at present.
11111 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11114 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11117 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11118 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11121 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11122 * on any HTTP request
11125 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11128 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11132 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11133 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11135 remoteSort : false,
11138 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11139 * loaded or when a record is removed. (defaults to false).
11141 pruneModifiedRecords : false,
11144 lastOptions : null,
11147 * Add Records to the Store and fires the add event.
11148 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11150 add : function(records){
11151 records = [].concat(records);
11152 for(var i = 0, len = records.length; i < len; i++){
11153 records[i].join(this);
11155 var index = this.data.length;
11156 this.data.addAll(records);
11157 this.fireEvent("add", this, records, index);
11161 * Remove a Record from the Store and fires the remove event.
11162 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11164 remove : function(record){
11165 var index = this.data.indexOf(record);
11166 this.data.removeAt(index);
11168 if(this.pruneModifiedRecords){
11169 this.modified.remove(record);
11171 this.fireEvent("remove", this, record, index);
11175 * Remove all Records from the Store and fires the clear event.
11177 removeAll : function(){
11179 if(this.pruneModifiedRecords){
11180 this.modified = [];
11182 this.fireEvent("clear", this);
11186 * Inserts Records to the Store at the given index and fires the add event.
11187 * @param {Number} index The start index at which to insert the passed Records.
11188 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11190 insert : function(index, records){
11191 records = [].concat(records);
11192 for(var i = 0, len = records.length; i < len; i++){
11193 this.data.insert(index, records[i]);
11194 records[i].join(this);
11196 this.fireEvent("add", this, records, index);
11200 * Get the index within the cache of the passed Record.
11201 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11202 * @return {Number} The index of the passed Record. Returns -1 if not found.
11204 indexOf : function(record){
11205 return this.data.indexOf(record);
11209 * Get the index within the cache of the Record with the passed id.
11210 * @param {String} id The id of the Record to find.
11211 * @return {Number} The index of the Record. Returns -1 if not found.
11213 indexOfId : function(id){
11214 return this.data.indexOfKey(id);
11218 * Get the Record with the specified id.
11219 * @param {String} id The id of the Record to find.
11220 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11222 getById : function(id){
11223 return this.data.key(id);
11227 * Get the Record at the specified index.
11228 * @param {Number} index The index of the Record to find.
11229 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11231 getAt : function(index){
11232 return this.data.itemAt(index);
11236 * Returns a range of Records between specified indices.
11237 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11238 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11239 * @return {Roo.data.Record[]} An array of Records
11241 getRange : function(start, end){
11242 return this.data.getRange(start, end);
11246 storeOptions : function(o){
11247 o = Roo.apply({}, o);
11250 this.lastOptions = o;
11254 * Loads the Record cache from the configured Proxy using the configured Reader.
11256 * If using remote paging, then the first load call must specify the <em>start</em>
11257 * and <em>limit</em> properties in the options.params property to establish the initial
11258 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11260 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11261 * and this call will return before the new data has been loaded. Perform any post-processing
11262 * in a callback function, or in a "load" event handler.</strong>
11264 * @param {Object} options An object containing properties which control loading options:<ul>
11265 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11266 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11267 * passed the following arguments:<ul>
11268 * <li>r : Roo.data.Record[]</li>
11269 * <li>options: Options object from the load call</li>
11270 * <li>success: Boolean success indicator</li></ul></li>
11271 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11272 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11275 load : function(options){
11276 options = options || {};
11277 if(this.fireEvent("beforeload", this, options) !== false){
11278 this.storeOptions(options);
11279 var p = Roo.apply(options.params || {}, this.baseParams);
11280 // if meta was not loaded from remote source.. try requesting it.
11281 if (!this.reader.metaFromRemote) {
11282 p._requestMeta = 1;
11284 if(this.sortInfo && this.remoteSort){
11285 var pn = this.paramNames;
11286 p[pn["sort"]] = this.sortInfo.field;
11287 p[pn["dir"]] = this.sortInfo.direction;
11289 if (this.multiSort) {
11290 var pn = this.paramNames;
11291 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11294 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11299 * Reloads the Record cache from the configured Proxy using the configured Reader and
11300 * the options from the last load operation performed.
11301 * @param {Object} options (optional) An object containing properties which may override the options
11302 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11303 * the most recently used options are reused).
11305 reload : function(options){
11306 this.load(Roo.applyIf(options||{}, this.lastOptions));
11310 // Called as a callback by the Reader during a load operation.
11311 loadRecords : function(o, options, success){
11312 if(!o || success === false){
11313 if(success !== false){
11314 this.fireEvent("load", this, [], options, o);
11316 if(options.callback){
11317 options.callback.call(options.scope || this, [], options, false);
11321 // if data returned failure - throw an exception.
11322 if (o.success === false) {
11323 // show a message if no listener is registered.
11324 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11325 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11327 // loadmask wil be hooked into this..
11328 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11331 var r = o.records, t = o.totalRecords || r.length;
11333 this.fireEvent("beforeloadadd", this, r, options, o);
11335 if(!options || options.add !== true){
11336 if(this.pruneModifiedRecords){
11337 this.modified = [];
11339 for(var i = 0, len = r.length; i < len; i++){
11343 this.data = this.snapshot;
11344 delete this.snapshot;
11347 this.data.addAll(r);
11348 this.totalLength = t;
11350 this.fireEvent("datachanged", this);
11352 this.totalLength = Math.max(t, this.data.length+r.length);
11356 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11358 var e = new Roo.data.Record({});
11360 e.set(this.parent.displayField, this.parent.emptyTitle);
11361 e.set(this.parent.valueField, '');
11366 this.fireEvent("load", this, r, options, o);
11367 if(options.callback){
11368 options.callback.call(options.scope || this, r, options, true);
11374 * Loads data from a passed data block. A Reader which understands the format of the data
11375 * must have been configured in the constructor.
11376 * @param {Object} data The data block from which to read the Records. The format of the data expected
11377 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11378 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11380 loadData : function(o, append){
11381 var r = this.reader.readRecords(o);
11382 this.loadRecords(r, {add: append}, true);
11386 * Gets the number of cached records.
11388 * <em>If using paging, this may not be the total size of the dataset. If the data object
11389 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11390 * the data set size</em>
11392 getCount : function(){
11393 return this.data.length || 0;
11397 * Gets the total number of records in the dataset as returned by the server.
11399 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11400 * the dataset size</em>
11402 getTotalCount : function(){
11403 return this.totalLength || 0;
11407 * Returns the sort state of the Store as an object with two properties:
11409 field {String} The name of the field by which the Records are sorted
11410 direction {String} The sort order, "ASC" or "DESC"
11413 getSortState : function(){
11414 return this.sortInfo;
11418 applySort : function(){
11419 if(this.sortInfo && !this.remoteSort){
11420 var s = this.sortInfo, f = s.field;
11421 var st = this.fields.get(f).sortType;
11422 var fn = function(r1, r2){
11423 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11424 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11426 this.data.sort(s.direction, fn);
11427 if(this.snapshot && this.snapshot != this.data){
11428 this.snapshot.sort(s.direction, fn);
11434 * Sets the default sort column and order to be used by the next load operation.
11435 * @param {String} fieldName The name of the field to sort by.
11436 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11438 setDefaultSort : function(field, dir){
11439 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11443 * Sort the Records.
11444 * If remote sorting is used, the sort is performed on the server, and the cache is
11445 * reloaded. If local sorting is used, the cache is sorted internally.
11446 * @param {String} fieldName The name of the field to sort by.
11447 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11449 sort : function(fieldName, dir){
11450 var f = this.fields.get(fieldName);
11452 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11454 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11455 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11460 this.sortToggle[f.name] = dir;
11461 this.sortInfo = {field: f.name, direction: dir};
11462 if(!this.remoteSort){
11464 this.fireEvent("datachanged", this);
11466 this.load(this.lastOptions);
11471 * Calls the specified function for each of the Records in the cache.
11472 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11473 * Returning <em>false</em> aborts and exits the iteration.
11474 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11476 each : function(fn, scope){
11477 this.data.each(fn, scope);
11481 * Gets all records modified since the last commit. Modified records are persisted across load operations
11482 * (e.g., during paging).
11483 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11485 getModifiedRecords : function(){
11486 return this.modified;
11490 createFilterFn : function(property, value, anyMatch){
11491 if(!value.exec){ // not a regex
11492 value = String(value);
11493 if(value.length == 0){
11496 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11498 return function(r){
11499 return value.test(r.data[property]);
11504 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11505 * @param {String} property A field on your records
11506 * @param {Number} start The record index to start at (defaults to 0)
11507 * @param {Number} end The last record index to include (defaults to length - 1)
11508 * @return {Number} The sum
11510 sum : function(property, start, end){
11511 var rs = this.data.items, v = 0;
11512 start = start || 0;
11513 end = (end || end === 0) ? end : rs.length-1;
11515 for(var i = start; i <= end; i++){
11516 v += (rs[i].data[property] || 0);
11522 * Filter the records by a specified property.
11523 * @param {String} field A field on your records
11524 * @param {String/RegExp} value Either a string that the field
11525 * should start with or a RegExp to test against the field
11526 * @param {Boolean} anyMatch True to match any part not just the beginning
11528 filter : function(property, value, anyMatch){
11529 var fn = this.createFilterFn(property, value, anyMatch);
11530 return fn ? this.filterBy(fn) : this.clearFilter();
11534 * Filter by a function. The specified function will be called with each
11535 * record in this data source. If the function returns true the record is included,
11536 * otherwise it is filtered.
11537 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11538 * @param {Object} scope (optional) The scope of the function (defaults to this)
11540 filterBy : function(fn, scope){
11541 this.snapshot = this.snapshot || this.data;
11542 this.data = this.queryBy(fn, scope||this);
11543 this.fireEvent("datachanged", this);
11547 * Query the records by a specified property.
11548 * @param {String} field A field on your records
11549 * @param {String/RegExp} value Either a string that the field
11550 * should start with or a RegExp to test against the field
11551 * @param {Boolean} anyMatch True to match any part not just the beginning
11552 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11554 query : function(property, value, anyMatch){
11555 var fn = this.createFilterFn(property, value, anyMatch);
11556 return fn ? this.queryBy(fn) : this.data.clone();
11560 * Query by a function. The specified function will be called with each
11561 * record in this data source. If the function returns true the record is included
11563 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11564 * @param {Object} scope (optional) The scope of the function (defaults to this)
11565 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11567 queryBy : function(fn, scope){
11568 var data = this.snapshot || this.data;
11569 return data.filterBy(fn, scope||this);
11573 * Collects unique values for a particular dataIndex from this store.
11574 * @param {String} dataIndex The property to collect
11575 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11576 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11577 * @return {Array} An array of the unique values
11579 collect : function(dataIndex, allowNull, bypassFilter){
11580 var d = (bypassFilter === true && this.snapshot) ?
11581 this.snapshot.items : this.data.items;
11582 var v, sv, r = [], l = {};
11583 for(var i = 0, len = d.length; i < len; i++){
11584 v = d[i].data[dataIndex];
11586 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11595 * Revert to a view of the Record cache with no filtering applied.
11596 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11598 clearFilter : function(suppressEvent){
11599 if(this.snapshot && this.snapshot != this.data){
11600 this.data = this.snapshot;
11601 delete this.snapshot;
11602 if(suppressEvent !== true){
11603 this.fireEvent("datachanged", this);
11609 afterEdit : function(record){
11610 if(this.modified.indexOf(record) == -1){
11611 this.modified.push(record);
11613 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11617 afterReject : function(record){
11618 this.modified.remove(record);
11619 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11623 afterCommit : function(record){
11624 this.modified.remove(record);
11625 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11629 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11630 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11632 commitChanges : function(){
11633 var m = this.modified.slice(0);
11634 this.modified = [];
11635 for(var i = 0, len = m.length; i < len; i++){
11641 * Cancel outstanding changes on all changed records.
11643 rejectChanges : function(){
11644 var m = this.modified.slice(0);
11645 this.modified = [];
11646 for(var i = 0, len = m.length; i < len; i++){
11651 onMetaChange : function(meta, rtype, o){
11652 this.recordType = rtype;
11653 this.fields = rtype.prototype.fields;
11654 delete this.snapshot;
11655 this.sortInfo = meta.sortInfo || this.sortInfo;
11656 this.modified = [];
11657 this.fireEvent('metachange', this, this.reader.meta);
11660 moveIndex : function(data, type)
11662 var index = this.indexOf(data);
11664 var newIndex = index + type;
11668 this.insert(newIndex, data);
11673 * Ext JS Library 1.1.1
11674 * Copyright(c) 2006-2007, Ext JS, LLC.
11676 * Originally Released Under LGPL - original licence link has changed is not relivant.
11679 * <script type="text/javascript">
11683 * @class Roo.data.SimpleStore
11684 * @extends Roo.data.Store
11685 * Small helper class to make creating Stores from Array data easier.
11686 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11687 * @cfg {Array} fields An array of field definition objects, or field name strings.
11688 * @cfg {Array} data The multi-dimensional array of data
11690 * @param {Object} config
11692 Roo.data.SimpleStore = function(config){
11693 Roo.data.SimpleStore.superclass.constructor.call(this, {
11695 reader: new Roo.data.ArrayReader({
11698 Roo.data.Record.create(config.fields)
11700 proxy : new Roo.data.MemoryProxy(config.data)
11704 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11706 * Ext JS Library 1.1.1
11707 * Copyright(c) 2006-2007, Ext JS, LLC.
11709 * Originally Released Under LGPL - original licence link has changed is not relivant.
11712 * <script type="text/javascript">
11717 * @extends Roo.data.Store
11718 * @class Roo.data.JsonStore
11719 * Small helper class to make creating Stores for JSON data easier. <br/>
11721 var store = new Roo.data.JsonStore({
11722 url: 'get-images.php',
11724 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11727 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11728 * JsonReader and HttpProxy (unless inline data is provided).</b>
11729 * @cfg {Array} fields An array of field definition objects, or field name strings.
11731 * @param {Object} config
11733 Roo.data.JsonStore = function(c){
11734 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11735 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11736 reader: new Roo.data.JsonReader(c, c.fields)
11739 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11741 * Ext JS Library 1.1.1
11742 * Copyright(c) 2006-2007, Ext JS, LLC.
11744 * Originally Released Under LGPL - original licence link has changed is not relivant.
11747 * <script type="text/javascript">
11751 Roo.data.Field = function(config){
11752 if(typeof config == "string"){
11753 config = {name: config};
11755 Roo.apply(this, config);
11758 this.type = "auto";
11761 var st = Roo.data.SortTypes;
11762 // named sortTypes are supported, here we look them up
11763 if(typeof this.sortType == "string"){
11764 this.sortType = st[this.sortType];
11767 // set default sortType for strings and dates
11768 if(!this.sortType){
11771 this.sortType = st.asUCString;
11774 this.sortType = st.asDate;
11777 this.sortType = st.none;
11782 var stripRe = /[\$,%]/g;
11784 // prebuilt conversion function for this field, instead of
11785 // switching every time we're reading a value
11787 var cv, dateFormat = this.dateFormat;
11792 cv = function(v){ return v; };
11795 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11799 return v !== undefined && v !== null && v !== '' ?
11800 parseInt(String(v).replace(stripRe, ""), 10) : '';
11805 return v !== undefined && v !== null && v !== '' ?
11806 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11811 cv = function(v){ return v === true || v === "true" || v == 1; };
11818 if(v instanceof Date){
11822 if(dateFormat == "timestamp"){
11823 return new Date(v*1000);
11825 return Date.parseDate(v, dateFormat);
11827 var parsed = Date.parse(v);
11828 return parsed ? new Date(parsed) : null;
11837 Roo.data.Field.prototype = {
11845 * Ext JS Library 1.1.1
11846 * Copyright(c) 2006-2007, Ext JS, LLC.
11848 * Originally Released Under LGPL - original licence link has changed is not relivant.
11851 * <script type="text/javascript">
11854 // Base class for reading structured data from a data source. This class is intended to be
11855 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11858 * @class Roo.data.DataReader
11859 * Base class for reading structured data from a data source. This class is intended to be
11860 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11863 Roo.data.DataReader = function(meta, recordType){
11867 this.recordType = recordType instanceof Array ?
11868 Roo.data.Record.create(recordType) : recordType;
11871 Roo.data.DataReader.prototype = {
11873 * Create an empty record
11874 * @param {Object} data (optional) - overlay some values
11875 * @return {Roo.data.Record} record created.
11877 newRow : function(d) {
11879 this.recordType.prototype.fields.each(function(c) {
11881 case 'int' : da[c.name] = 0; break;
11882 case 'date' : da[c.name] = new Date(); break;
11883 case 'float' : da[c.name] = 0.0; break;
11884 case 'boolean' : da[c.name] = false; break;
11885 default : da[c.name] = ""; break;
11889 return new this.recordType(Roo.apply(da, d));
11894 * Ext JS Library 1.1.1
11895 * Copyright(c) 2006-2007, Ext JS, LLC.
11897 * Originally Released Under LGPL - original licence link has changed is not relivant.
11900 * <script type="text/javascript">
11904 * @class Roo.data.DataProxy
11905 * @extends Roo.data.Observable
11906 * This class is an abstract base class for implementations which provide retrieval of
11907 * unformatted data objects.<br>
11909 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11910 * (of the appropriate type which knows how to parse the data object) to provide a block of
11911 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11913 * Custom implementations must implement the load method as described in
11914 * {@link Roo.data.HttpProxy#load}.
11916 Roo.data.DataProxy = function(){
11919 * @event beforeload
11920 * Fires before a network request is made to retrieve a data object.
11921 * @param {Object} This DataProxy object.
11922 * @param {Object} params The params parameter to the load function.
11927 * Fires before the load method's callback is called.
11928 * @param {Object} This DataProxy object.
11929 * @param {Object} o The data object.
11930 * @param {Object} arg The callback argument object passed to the load function.
11934 * @event loadexception
11935 * Fires if an Exception occurs during data retrieval.
11936 * @param {Object} This DataProxy object.
11937 * @param {Object} o The data object.
11938 * @param {Object} arg The callback argument object passed to the load function.
11939 * @param {Object} e The Exception.
11941 loadexception : true
11943 Roo.data.DataProxy.superclass.constructor.call(this);
11946 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11949 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11953 * Ext JS Library 1.1.1
11954 * Copyright(c) 2006-2007, Ext JS, LLC.
11956 * Originally Released Under LGPL - original licence link has changed is not relivant.
11959 * <script type="text/javascript">
11962 * @class Roo.data.MemoryProxy
11963 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11964 * to the Reader when its load method is called.
11966 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11968 Roo.data.MemoryProxy = function(data){
11972 Roo.data.MemoryProxy.superclass.constructor.call(this);
11976 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11979 * Load data from the requested source (in this case an in-memory
11980 * data object passed to the constructor), read the data object into
11981 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11982 * process that block using the passed callback.
11983 * @param {Object} params This parameter is not used by the MemoryProxy class.
11984 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11985 * object into a block of Roo.data.Records.
11986 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11987 * The function must be passed <ul>
11988 * <li>The Record block object</li>
11989 * <li>The "arg" argument from the load function</li>
11990 * <li>A boolean success indicator</li>
11992 * @param {Object} scope The scope in which to call the callback
11993 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11995 load : function(params, reader, callback, scope, arg){
11996 params = params || {};
11999 result = reader.readRecords(this.data);
12001 this.fireEvent("loadexception", this, arg, null, e);
12002 callback.call(scope, null, arg, false);
12005 callback.call(scope, result, arg, true);
12009 update : function(params, records){
12014 * Ext JS Library 1.1.1
12015 * Copyright(c) 2006-2007, Ext JS, LLC.
12017 * Originally Released Under LGPL - original licence link has changed is not relivant.
12020 * <script type="text/javascript">
12023 * @class Roo.data.HttpProxy
12024 * @extends Roo.data.DataProxy
12025 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12026 * configured to reference a certain URL.<br><br>
12028 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12029 * from which the running page was served.<br><br>
12031 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12033 * Be aware that to enable the browser to parse an XML document, the server must set
12034 * the Content-Type header in the HTTP response to "text/xml".
12036 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12037 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12038 * will be used to make the request.
12040 Roo.data.HttpProxy = function(conn){
12041 Roo.data.HttpProxy.superclass.constructor.call(this);
12042 // is conn a conn config or a real conn?
12044 this.useAjax = !conn || !conn.events;
12048 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12049 // thse are take from connection...
12052 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12055 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12056 * extra parameters to each request made by this object. (defaults to undefined)
12059 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12060 * to each request made by this object. (defaults to undefined)
12063 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12066 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12069 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12075 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12079 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12080 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12081 * a finer-grained basis than the DataProxy events.
12083 getConnection : function(){
12084 return this.useAjax ? Roo.Ajax : this.conn;
12088 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12089 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12090 * process that block using the passed callback.
12091 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12092 * for the request to the remote server.
12093 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12094 * object into a block of Roo.data.Records.
12095 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12096 * The function must be passed <ul>
12097 * <li>The Record block object</li>
12098 * <li>The "arg" argument from the load function</li>
12099 * <li>A boolean success indicator</li>
12101 * @param {Object} scope The scope in which to call the callback
12102 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12104 load : function(params, reader, callback, scope, arg){
12105 if(this.fireEvent("beforeload", this, params) !== false){
12107 params : params || {},
12109 callback : callback,
12114 callback : this.loadResponse,
12118 Roo.applyIf(o, this.conn);
12119 if(this.activeRequest){
12120 Roo.Ajax.abort(this.activeRequest);
12122 this.activeRequest = Roo.Ajax.request(o);
12124 this.conn.request(o);
12127 callback.call(scope||this, null, arg, false);
12132 loadResponse : function(o, success, response){
12133 delete this.activeRequest;
12135 this.fireEvent("loadexception", this, o, response);
12136 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12141 result = o.reader.read(response);
12143 this.fireEvent("loadexception", this, o, response, e);
12144 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12148 this.fireEvent("load", this, o, o.request.arg);
12149 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12153 update : function(dataSet){
12158 updateResponse : function(dataSet){
12163 * Ext JS Library 1.1.1
12164 * Copyright(c) 2006-2007, Ext JS, LLC.
12166 * Originally Released Under LGPL - original licence link has changed is not relivant.
12169 * <script type="text/javascript">
12173 * @class Roo.data.ScriptTagProxy
12174 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12175 * other than the originating domain of the running page.<br><br>
12177 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12178 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12180 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12181 * source code that is used as the source inside a <script> tag.<br><br>
12183 * In order for the browser to process the returned data, the server must wrap the data object
12184 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12185 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12186 * depending on whether the callback name was passed:
12189 boolean scriptTag = false;
12190 String cb = request.getParameter("callback");
12193 response.setContentType("text/javascript");
12195 response.setContentType("application/x-json");
12197 Writer out = response.getWriter();
12199 out.write(cb + "(");
12201 out.print(dataBlock.toJsonString());
12208 * @param {Object} config A configuration object.
12210 Roo.data.ScriptTagProxy = function(config){
12211 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12212 Roo.apply(this, config);
12213 this.head = document.getElementsByTagName("head")[0];
12216 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12218 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12220 * @cfg {String} url The URL from which to request the data object.
12223 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12227 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12228 * the server the name of the callback function set up by the load call to process the returned data object.
12229 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12230 * javascript output which calls this named function passing the data object as its only parameter.
12232 callbackParam : "callback",
12234 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12235 * name to the request.
12240 * Load data from the configured URL, read the data object into
12241 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12242 * process that block using the passed callback.
12243 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12244 * for the request to the remote server.
12245 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12246 * object into a block of Roo.data.Records.
12247 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12248 * The function must be passed <ul>
12249 * <li>The Record block object</li>
12250 * <li>The "arg" argument from the load function</li>
12251 * <li>A boolean success indicator</li>
12253 * @param {Object} scope The scope in which to call the callback
12254 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12256 load : function(params, reader, callback, scope, arg){
12257 if(this.fireEvent("beforeload", this, params) !== false){
12259 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12261 var url = this.url;
12262 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12264 url += "&_dc=" + (new Date().getTime());
12266 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12269 cb : "stcCallback"+transId,
12270 scriptId : "stcScript"+transId,
12274 callback : callback,
12280 window[trans.cb] = function(o){
12281 conn.handleResponse(o, trans);
12284 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12286 if(this.autoAbort !== false){
12290 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12292 var script = document.createElement("script");
12293 script.setAttribute("src", url);
12294 script.setAttribute("type", "text/javascript");
12295 script.setAttribute("id", trans.scriptId);
12296 this.head.appendChild(script);
12298 this.trans = trans;
12300 callback.call(scope||this, null, arg, false);
12305 isLoading : function(){
12306 return this.trans ? true : false;
12310 * Abort the current server request.
12312 abort : function(){
12313 if(this.isLoading()){
12314 this.destroyTrans(this.trans);
12319 destroyTrans : function(trans, isLoaded){
12320 this.head.removeChild(document.getElementById(trans.scriptId));
12321 clearTimeout(trans.timeoutId);
12323 window[trans.cb] = undefined;
12325 delete window[trans.cb];
12328 // if hasn't been loaded, wait for load to remove it to prevent script error
12329 window[trans.cb] = function(){
12330 window[trans.cb] = undefined;
12332 delete window[trans.cb];
12339 handleResponse : function(o, trans){
12340 this.trans = false;
12341 this.destroyTrans(trans, true);
12344 result = trans.reader.readRecords(o);
12346 this.fireEvent("loadexception", this, o, trans.arg, e);
12347 trans.callback.call(trans.scope||window, null, trans.arg, false);
12350 this.fireEvent("load", this, o, trans.arg);
12351 trans.callback.call(trans.scope||window, result, trans.arg, true);
12355 handleFailure : function(trans){
12356 this.trans = false;
12357 this.destroyTrans(trans, false);
12358 this.fireEvent("loadexception", this, null, trans.arg);
12359 trans.callback.call(trans.scope||window, null, trans.arg, false);
12363 * Ext JS Library 1.1.1
12364 * Copyright(c) 2006-2007, Ext JS, LLC.
12366 * Originally Released Under LGPL - original licence link has changed is not relivant.
12369 * <script type="text/javascript">
12373 * @class Roo.data.JsonReader
12374 * @extends Roo.data.DataReader
12375 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12376 * based on mappings in a provided Roo.data.Record constructor.
12378 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12379 * in the reply previously.
12384 var RecordDef = Roo.data.Record.create([
12385 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12386 {name: 'occupation'} // This field will use "occupation" as the mapping.
12388 var myReader = new Roo.data.JsonReader({
12389 totalProperty: "results", // The property which contains the total dataset size (optional)
12390 root: "rows", // The property which contains an Array of row objects
12391 id: "id" // The property within each row object that provides an ID for the record (optional)
12395 * This would consume a JSON file like this:
12397 { 'results': 2, 'rows': [
12398 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12399 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12402 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12403 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12404 * paged from the remote server.
12405 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12406 * @cfg {String} root name of the property which contains the Array of row objects.
12407 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12408 * @cfg {Array} fields Array of field definition objects
12410 * Create a new JsonReader
12411 * @param {Object} meta Metadata configuration options
12412 * @param {Object} recordType Either an Array of field definition objects,
12413 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12415 Roo.data.JsonReader = function(meta, recordType){
12418 // set some defaults:
12419 Roo.applyIf(meta, {
12420 totalProperty: 'total',
12421 successProperty : 'success',
12426 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12428 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12431 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12432 * Used by Store query builder to append _requestMeta to params.
12435 metaFromRemote : false,
12437 * This method is only used by a DataProxy which has retrieved data from a remote server.
12438 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12439 * @return {Object} data A data block which is used by an Roo.data.Store object as
12440 * a cache of Roo.data.Records.
12442 read : function(response){
12443 var json = response.responseText;
12445 var o = /* eval:var:o */ eval("("+json+")");
12447 throw {message: "JsonReader.read: Json object not found"};
12453 this.metaFromRemote = true;
12454 this.meta = o.metaData;
12455 this.recordType = Roo.data.Record.create(o.metaData.fields);
12456 this.onMetaChange(this.meta, this.recordType, o);
12458 return this.readRecords(o);
12461 // private function a store will implement
12462 onMetaChange : function(meta, recordType, o){
12469 simpleAccess: function(obj, subsc) {
12476 getJsonAccessor: function(){
12478 return function(expr) {
12480 return(re.test(expr))
12481 ? new Function("obj", "return obj." + expr)
12486 return Roo.emptyFn;
12491 * Create a data block containing Roo.data.Records from an XML document.
12492 * @param {Object} o An object which contains an Array of row objects in the property specified
12493 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12494 * which contains the total size of the dataset.
12495 * @return {Object} data A data block which is used by an Roo.data.Store object as
12496 * a cache of Roo.data.Records.
12498 readRecords : function(o){
12500 * After any data loads, the raw JSON data is available for further custom processing.
12504 var s = this.meta, Record = this.recordType,
12505 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12507 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12509 if(s.totalProperty) {
12510 this.getTotal = this.getJsonAccessor(s.totalProperty);
12512 if(s.successProperty) {
12513 this.getSuccess = this.getJsonAccessor(s.successProperty);
12515 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12517 var g = this.getJsonAccessor(s.id);
12518 this.getId = function(rec) {
12520 return (r === undefined || r === "") ? null : r;
12523 this.getId = function(){return null;};
12526 for(var jj = 0; jj < fl; jj++){
12528 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12529 this.ef[jj] = this.getJsonAccessor(map);
12533 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12534 if(s.totalProperty){
12535 var vt = parseInt(this.getTotal(o), 10);
12540 if(s.successProperty){
12541 var vs = this.getSuccess(o);
12542 if(vs === false || vs === 'false'){
12547 for(var i = 0; i < c; i++){
12550 var id = this.getId(n);
12551 for(var j = 0; j < fl; j++){
12553 var v = this.ef[j](n);
12555 Roo.log('missing convert for ' + f.name);
12559 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12561 var record = new Record(values, id);
12563 records[i] = record;
12569 totalRecords : totalRecords
12574 * Ext JS Library 1.1.1
12575 * Copyright(c) 2006-2007, Ext JS, LLC.
12577 * Originally Released Under LGPL - original licence link has changed is not relivant.
12580 * <script type="text/javascript">
12584 * @class Roo.data.ArrayReader
12585 * @extends Roo.data.DataReader
12586 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12587 * Each element of that Array represents a row of data fields. The
12588 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12589 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12593 var RecordDef = Roo.data.Record.create([
12594 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12595 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12597 var myReader = new Roo.data.ArrayReader({
12598 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12602 * This would consume an Array like this:
12604 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12606 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12608 * Create a new JsonReader
12609 * @param {Object} meta Metadata configuration options.
12610 * @param {Object} recordType Either an Array of field definition objects
12611 * as specified to {@link Roo.data.Record#create},
12612 * or an {@link Roo.data.Record} object
12613 * created using {@link Roo.data.Record#create}.
12615 Roo.data.ArrayReader = function(meta, recordType){
12616 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12619 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12621 * Create a data block containing Roo.data.Records from an XML document.
12622 * @param {Object} o An Array of row objects which represents the dataset.
12623 * @return {Object} data A data block which is used by an Roo.data.Store object as
12624 * a cache of Roo.data.Records.
12626 readRecords : function(o){
12627 var sid = this.meta ? this.meta.id : null;
12628 var recordType = this.recordType, fields = recordType.prototype.fields;
12631 for(var i = 0; i < root.length; i++){
12634 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12635 for(var j = 0, jlen = fields.length; j < jlen; j++){
12636 var f = fields.items[j];
12637 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12638 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12640 values[f.name] = v;
12642 var record = new recordType(values, id);
12644 records[records.length] = record;
12648 totalRecords : records.length
12657 * @class Roo.bootstrap.ComboBox
12658 * @extends Roo.bootstrap.TriggerField
12659 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12660 * @cfg {Boolean} append (true|false) default false
12661 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12662 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12663 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12664 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12665 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12666 * @cfg {Boolean} animate default true
12667 * @cfg {Boolean} emptyResultText only for touch device
12668 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12669 * @cfg {String} emptyTitle default ''
12671 * Create a new ComboBox.
12672 * @param {Object} config Configuration options
12674 Roo.bootstrap.ComboBox = function(config){
12675 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12679 * Fires when the dropdown list is expanded
12680 * @param {Roo.bootstrap.ComboBox} combo This combo box
12685 * Fires when the dropdown list is collapsed
12686 * @param {Roo.bootstrap.ComboBox} combo This combo box
12690 * @event beforeselect
12691 * Fires before a list item is selected. Return false to cancel the selection.
12692 * @param {Roo.bootstrap.ComboBox} combo This combo box
12693 * @param {Roo.data.Record} record The data record returned from the underlying store
12694 * @param {Number} index The index of the selected item in the dropdown list
12696 'beforeselect' : true,
12699 * Fires when a list item is selected
12700 * @param {Roo.bootstrap.ComboBox} combo This combo box
12701 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12702 * @param {Number} index The index of the selected item in the dropdown list
12706 * @event beforequery
12707 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12708 * The event object passed has these properties:
12709 * @param {Roo.bootstrap.ComboBox} combo This combo box
12710 * @param {String} query The query
12711 * @param {Boolean} forceAll true to force "all" query
12712 * @param {Boolean} cancel true to cancel the query
12713 * @param {Object} e The query event object
12715 'beforequery': true,
12718 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12719 * @param {Roo.bootstrap.ComboBox} combo This combo box
12724 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12725 * @param {Roo.bootstrap.ComboBox} combo This combo box
12726 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12731 * Fires when the remove value from the combobox array
12732 * @param {Roo.bootstrap.ComboBox} combo This combo box
12736 * @event afterremove
12737 * Fires when the remove value from the combobox array
12738 * @param {Roo.bootstrap.ComboBox} combo This combo box
12740 'afterremove' : true,
12742 * @event specialfilter
12743 * Fires when specialfilter
12744 * @param {Roo.bootstrap.ComboBox} combo This combo box
12746 'specialfilter' : true,
12749 * Fires when tick the element
12750 * @param {Roo.bootstrap.ComboBox} combo This combo box
12754 * @event touchviewdisplay
12755 * Fires when touch view require special display (default is using displayField)
12756 * @param {Roo.bootstrap.ComboBox} combo This combo box
12757 * @param {Object} cfg set html .
12759 'touchviewdisplay' : true
12764 this.tickItems = [];
12766 this.selectedIndex = -1;
12767 if(this.mode == 'local'){
12768 if(config.queryDelay === undefined){
12769 this.queryDelay = 10;
12771 if(config.minChars === undefined){
12777 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12780 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12781 * rendering into an Roo.Editor, defaults to false)
12784 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12785 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12788 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12791 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12792 * the dropdown list (defaults to undefined, with no header element)
12796 * @cfg {String/Roo.Template} tpl The template to use to render the output
12800 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12802 listWidth: undefined,
12804 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12805 * mode = 'remote' or 'text' if mode = 'local')
12807 displayField: undefined,
12810 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12811 * mode = 'remote' or 'value' if mode = 'local').
12812 * Note: use of a valueField requires the user make a selection
12813 * in order for a value to be mapped.
12815 valueField: undefined,
12817 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12822 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12823 * field's data value (defaults to the underlying DOM element's name)
12825 hiddenName: undefined,
12827 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12831 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12833 selectedClass: 'active',
12836 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12840 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12841 * anchor positions (defaults to 'tl-bl')
12843 listAlign: 'tl-bl?',
12845 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12849 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12850 * query specified by the allQuery config option (defaults to 'query')
12852 triggerAction: 'query',
12854 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12855 * (defaults to 4, does not apply if editable = false)
12859 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12860 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12864 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12865 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12869 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12870 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12874 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12875 * when editable = true (defaults to false)
12877 selectOnFocus:false,
12879 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12881 queryParam: 'query',
12883 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12884 * when mode = 'remote' (defaults to 'Loading...')
12886 loadingText: 'Loading...',
12888 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12892 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12896 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12897 * traditional select (defaults to true)
12901 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12905 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12909 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12910 * listWidth has a higher value)
12914 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12915 * allow the user to set arbitrary text into the field (defaults to false)
12917 forceSelection:false,
12919 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12920 * if typeAhead = true (defaults to 250)
12922 typeAheadDelay : 250,
12924 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12925 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12927 valueNotFoundText : undefined,
12929 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12931 blockFocus : false,
12934 * @cfg {Boolean} disableClear Disable showing of clear button.
12936 disableClear : false,
12938 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12940 alwaysQuery : false,
12943 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12948 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12950 invalidClass : "has-warning",
12953 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12955 validClass : "has-success",
12958 * @cfg {Boolean} specialFilter (true|false) special filter default false
12960 specialFilter : false,
12963 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12965 mobileTouchView : true,
12968 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12970 useNativeIOS : false,
12972 ios_options : false,
12984 btnPosition : 'right',
12985 triggerList : true,
12986 showToggleBtn : true,
12988 emptyResultText: 'Empty',
12989 triggerText : 'Select',
12992 // element that contains real text value.. (when hidden is used..)
12994 getAutoCreate : function()
12999 * Render classic select for iso
13002 if(Roo.isIOS && this.useNativeIOS){
13003 cfg = this.getAutoCreateNativeIOS();
13011 if(Roo.isTouch && this.mobileTouchView){
13012 cfg = this.getAutoCreateTouchView();
13019 if(!this.tickable){
13020 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13025 * ComboBox with tickable selections
13028 var align = this.labelAlign || this.parentLabelAlign();
13031 cls : 'form-group roo-combobox-tickable' //input-group
13034 var btn_text_select = '';
13035 var btn_text_done = '';
13036 var btn_text_cancel = '';
13038 if (this.btn_text_show) {
13039 btn_text_select = 'Select';
13040 btn_text_done = 'Done';
13041 btn_text_cancel = 'Cancel';
13046 cls : 'tickable-buttons',
13051 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13052 //html : this.triggerText
13053 html: btn_text_select
13059 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13061 html: btn_text_done
13067 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13069 html: btn_text_cancel
13075 buttons.cn.unshift({
13077 cls: 'roo-select2-search-field-input'
13083 Roo.each(buttons.cn, function(c){
13085 c.cls += ' btn-' + _this.size;
13088 if (_this.disabled) {
13099 cls: 'form-hidden-field'
13103 cls: 'roo-select2-choices',
13107 cls: 'roo-select2-search-field',
13118 cls: 'roo-select2-container input-group roo-select2-container-multi',
13123 // cls: 'typeahead typeahead-long dropdown-menu',
13124 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13129 if(this.hasFeedback && !this.allowBlank){
13133 cls: 'glyphicon form-control-feedback'
13136 combobox.cn.push(feedback);
13140 if (align ==='left' && this.fieldLabel.length) {
13142 cfg.cls += ' roo-form-group-label-left';
13147 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13148 tooltip : 'This field is required'
13153 cls : 'control-label',
13154 html : this.fieldLabel
13166 var labelCfg = cfg.cn[1];
13167 var contentCfg = cfg.cn[2];
13170 if(this.indicatorpos == 'right'){
13176 cls : 'control-label',
13180 html : this.fieldLabel
13184 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13185 tooltip : 'This field is required'
13200 labelCfg = cfg.cn[0];
13201 contentCfg = cfg.cn[1];
13205 if(this.labelWidth > 12){
13206 labelCfg.style = "width: " + this.labelWidth + 'px';
13209 if(this.labelWidth < 13 && this.labelmd == 0){
13210 this.labelmd = this.labelWidth;
13213 if(this.labellg > 0){
13214 labelCfg.cls += ' col-lg-' + this.labellg;
13215 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13218 if(this.labelmd > 0){
13219 labelCfg.cls += ' col-md-' + this.labelmd;
13220 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13223 if(this.labelsm > 0){
13224 labelCfg.cls += ' col-sm-' + this.labelsm;
13225 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13228 if(this.labelxs > 0){
13229 labelCfg.cls += ' col-xs-' + this.labelxs;
13230 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13234 } else if ( this.fieldLabel.length) {
13235 // Roo.log(" label");
13239 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13240 tooltip : 'This field is required'
13244 //cls : 'input-group-addon',
13245 html : this.fieldLabel
13250 if(this.indicatorpos == 'right'){
13254 //cls : 'input-group-addon',
13255 html : this.fieldLabel
13259 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13260 tooltip : 'This field is required'
13269 // Roo.log(" no label && no align");
13276 ['xs','sm','md','lg'].map(function(size){
13277 if (settings[size]) {
13278 cfg.cls += ' col-' + size + '-' + settings[size];
13286 _initEventsCalled : false,
13289 initEvents: function()
13291 if (this._initEventsCalled) { // as we call render... prevent looping...
13294 this._initEventsCalled = true;
13297 throw "can not find store for combo";
13300 this.indicator = this.indicatorEl();
13302 this.store = Roo.factory(this.store, Roo.data);
13303 this.store.parent = this;
13305 // if we are building from html. then this element is so complex, that we can not really
13306 // use the rendered HTML.
13307 // so we have to trash and replace the previous code.
13308 if (Roo.XComponent.build_from_html) {
13309 // remove this element....
13310 var e = this.el.dom, k=0;
13311 while (e ) { e = e.previousSibling; ++k;}
13316 this.rendered = false;
13318 this.render(this.parent().getChildContainer(true), k);
13321 if(Roo.isIOS && this.useNativeIOS){
13322 this.initIOSView();
13330 if(Roo.isTouch && this.mobileTouchView){
13331 this.initTouchView();
13336 this.initTickableEvents();
13340 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13342 if(this.hiddenName){
13344 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13346 this.hiddenField.dom.value =
13347 this.hiddenValue !== undefined ? this.hiddenValue :
13348 this.value !== undefined ? this.value : '';
13350 // prevent input submission
13351 this.el.dom.removeAttribute('name');
13352 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13357 // this.el.dom.setAttribute('autocomplete', 'off');
13360 var cls = 'x-combo-list';
13362 //this.list = new Roo.Layer({
13363 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13369 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13370 _this.list.setWidth(lw);
13373 this.list.on('mouseover', this.onViewOver, this);
13374 this.list.on('mousemove', this.onViewMove, this);
13375 this.list.on('scroll', this.onViewScroll, this);
13378 this.list.swallowEvent('mousewheel');
13379 this.assetHeight = 0;
13382 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13383 this.assetHeight += this.header.getHeight();
13386 this.innerList = this.list.createChild({cls:cls+'-inner'});
13387 this.innerList.on('mouseover', this.onViewOver, this);
13388 this.innerList.on('mousemove', this.onViewMove, this);
13389 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13391 if(this.allowBlank && !this.pageSize && !this.disableClear){
13392 this.footer = this.list.createChild({cls:cls+'-ft'});
13393 this.pageTb = new Roo.Toolbar(this.footer);
13397 this.footer = this.list.createChild({cls:cls+'-ft'});
13398 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13399 {pageSize: this.pageSize});
13403 if (this.pageTb && this.allowBlank && !this.disableClear) {
13405 this.pageTb.add(new Roo.Toolbar.Fill(), {
13406 cls: 'x-btn-icon x-btn-clear',
13408 handler: function()
13411 _this.clearValue();
13412 _this.onSelect(false, -1);
13417 this.assetHeight += this.footer.getHeight();
13422 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13425 this.view = new Roo.View(this.list, this.tpl, {
13426 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13428 //this.view.wrapEl.setDisplayed(false);
13429 this.view.on('click', this.onViewClick, this);
13432 this.store.on('beforeload', this.onBeforeLoad, this);
13433 this.store.on('load', this.onLoad, this);
13434 this.store.on('loadexception', this.onLoadException, this);
13436 if(this.resizable){
13437 this.resizer = new Roo.Resizable(this.list, {
13438 pinned:true, handles:'se'
13440 this.resizer.on('resize', function(r, w, h){
13441 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13442 this.listWidth = w;
13443 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13444 this.restrictHeight();
13446 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13449 if(!this.editable){
13450 this.editable = true;
13451 this.setEditable(false);
13456 if (typeof(this.events.add.listeners) != 'undefined') {
13458 this.addicon = this.wrap.createChild(
13459 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13461 this.addicon.on('click', function(e) {
13462 this.fireEvent('add', this);
13465 if (typeof(this.events.edit.listeners) != 'undefined') {
13467 this.editicon = this.wrap.createChild(
13468 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13469 if (this.addicon) {
13470 this.editicon.setStyle('margin-left', '40px');
13472 this.editicon.on('click', function(e) {
13474 // we fire even if inothing is selected..
13475 this.fireEvent('edit', this, this.lastData );
13481 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13482 "up" : function(e){
13483 this.inKeyMode = true;
13487 "down" : function(e){
13488 if(!this.isExpanded()){
13489 this.onTriggerClick();
13491 this.inKeyMode = true;
13496 "enter" : function(e){
13497 // this.onViewClick();
13501 if(this.fireEvent("specialkey", this, e)){
13502 this.onViewClick(false);
13508 "esc" : function(e){
13512 "tab" : function(e){
13515 if(this.fireEvent("specialkey", this, e)){
13516 this.onViewClick(false);
13524 doRelay : function(foo, bar, hname){
13525 if(hname == 'down' || this.scope.isExpanded()){
13526 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13535 this.queryDelay = Math.max(this.queryDelay || 10,
13536 this.mode == 'local' ? 10 : 250);
13539 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13541 if(this.typeAhead){
13542 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13544 if(this.editable !== false){
13545 this.inputEl().on("keyup", this.onKeyUp, this);
13547 if(this.forceSelection){
13548 this.inputEl().on('blur', this.doForce, this);
13552 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13553 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13557 initTickableEvents: function()
13561 if(this.hiddenName){
13563 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13565 this.hiddenField.dom.value =
13566 this.hiddenValue !== undefined ? this.hiddenValue :
13567 this.value !== undefined ? this.value : '';
13569 // prevent input submission
13570 this.el.dom.removeAttribute('name');
13571 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13576 // this.list = this.el.select('ul.dropdown-menu',true).first();
13578 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13579 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13580 if(this.triggerList){
13581 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13584 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13585 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13587 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13588 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13590 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13591 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13593 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13594 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13595 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13598 this.cancelBtn.hide();
13603 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13604 _this.list.setWidth(lw);
13607 this.list.on('mouseover', this.onViewOver, this);
13608 this.list.on('mousemove', this.onViewMove, this);
13610 this.list.on('scroll', this.onViewScroll, this);
13613 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13616 this.view = new Roo.View(this.list, this.tpl, {
13617 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13620 //this.view.wrapEl.setDisplayed(false);
13621 this.view.on('click', this.onViewClick, this);
13625 this.store.on('beforeload', this.onBeforeLoad, this);
13626 this.store.on('load', this.onLoad, this);
13627 this.store.on('loadexception', this.onLoadException, this);
13630 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13631 "up" : function(e){
13632 this.inKeyMode = true;
13636 "down" : function(e){
13637 this.inKeyMode = true;
13641 "enter" : function(e){
13642 if(this.fireEvent("specialkey", this, e)){
13643 this.onViewClick(false);
13649 "esc" : function(e){
13650 this.onTickableFooterButtonClick(e, false, false);
13653 "tab" : function(e){
13654 this.fireEvent("specialkey", this, e);
13656 this.onTickableFooterButtonClick(e, false, false);
13663 doRelay : function(e, fn, key){
13664 if(this.scope.isExpanded()){
13665 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13674 this.queryDelay = Math.max(this.queryDelay || 10,
13675 this.mode == 'local' ? 10 : 250);
13678 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13680 if(this.typeAhead){
13681 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13684 if(this.editable !== false){
13685 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13688 this.indicator = this.indicatorEl();
13690 if(this.indicator){
13691 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13692 this.indicator.hide();
13697 onDestroy : function(){
13699 this.view.setStore(null);
13700 this.view.el.removeAllListeners();
13701 this.view.el.remove();
13702 this.view.purgeListeners();
13705 this.list.dom.innerHTML = '';
13709 this.store.un('beforeload', this.onBeforeLoad, this);
13710 this.store.un('load', this.onLoad, this);
13711 this.store.un('loadexception', this.onLoadException, this);
13713 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13717 fireKey : function(e){
13718 if(e.isNavKeyPress() && !this.list.isVisible()){
13719 this.fireEvent("specialkey", this, e);
13724 onResize: function(w, h){
13725 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13727 // if(typeof w != 'number'){
13728 // // we do not handle it!?!?
13731 // var tw = this.trigger.getWidth();
13732 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13733 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13735 // this.inputEl().setWidth( this.adjustWidth('input', x));
13737 // //this.trigger.setStyle('left', x+'px');
13739 // if(this.list && this.listWidth === undefined){
13740 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13741 // this.list.setWidth(lw);
13742 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13750 * Allow or prevent the user from directly editing the field text. If false is passed,
13751 * the user will only be able to select from the items defined in the dropdown list. This method
13752 * is the runtime equivalent of setting the 'editable' config option at config time.
13753 * @param {Boolean} value True to allow the user to directly edit the field text
13755 setEditable : function(value){
13756 if(value == this.editable){
13759 this.editable = value;
13761 this.inputEl().dom.setAttribute('readOnly', true);
13762 this.inputEl().on('mousedown', this.onTriggerClick, this);
13763 this.inputEl().addClass('x-combo-noedit');
13765 this.inputEl().dom.setAttribute('readOnly', false);
13766 this.inputEl().un('mousedown', this.onTriggerClick, this);
13767 this.inputEl().removeClass('x-combo-noedit');
13773 onBeforeLoad : function(combo,opts){
13774 if(!this.hasFocus){
13778 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13780 this.restrictHeight();
13781 this.selectedIndex = -1;
13785 onLoad : function(){
13787 this.hasQuery = false;
13789 if(!this.hasFocus){
13793 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13794 this.loading.hide();
13797 if(this.store.getCount() > 0){
13800 this.restrictHeight();
13801 if(this.lastQuery == this.allQuery){
13802 if(this.editable && !this.tickable){
13803 this.inputEl().dom.select();
13807 !this.selectByValue(this.value, true) &&
13810 !this.store.lastOptions ||
13811 typeof(this.store.lastOptions.add) == 'undefined' ||
13812 this.store.lastOptions.add != true
13815 this.select(0, true);
13818 if(this.autoFocus){
13821 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13822 this.taTask.delay(this.typeAheadDelay);
13826 this.onEmptyResults();
13832 onLoadException : function()
13834 this.hasQuery = false;
13836 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13837 this.loading.hide();
13840 if(this.tickable && this.editable){
13845 // only causes errors at present
13846 //Roo.log(this.store.reader.jsonData);
13847 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13849 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13855 onTypeAhead : function(){
13856 if(this.store.getCount() > 0){
13857 var r = this.store.getAt(0);
13858 var newValue = r.data[this.displayField];
13859 var len = newValue.length;
13860 var selStart = this.getRawValue().length;
13862 if(selStart != len){
13863 this.setRawValue(newValue);
13864 this.selectText(selStart, newValue.length);
13870 onSelect : function(record, index){
13872 if(this.fireEvent('beforeselect', this, record, index) !== false){
13874 this.setFromData(index > -1 ? record.data : false);
13877 this.fireEvent('select', this, record, index);
13882 * Returns the currently selected field value or empty string if no value is set.
13883 * @return {String} value The selected value
13885 getValue : function()
13887 if(Roo.isIOS && this.useNativeIOS){
13888 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13892 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13895 if(this.valueField){
13896 return typeof this.value != 'undefined' ? this.value : '';
13898 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13902 getRawValue : function()
13904 if(Roo.isIOS && this.useNativeIOS){
13905 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13908 var v = this.inputEl().getValue();
13914 * Clears any text/value currently set in the field
13916 clearValue : function(){
13918 if(this.hiddenField){
13919 this.hiddenField.dom.value = '';
13922 this.setRawValue('');
13923 this.lastSelectionText = '';
13924 this.lastData = false;
13926 var close = this.closeTriggerEl();
13937 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13938 * will be displayed in the field. If the value does not match the data value of an existing item,
13939 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13940 * Otherwise the field will be blank (although the value will still be set).
13941 * @param {String} value The value to match
13943 setValue : function(v)
13945 if(Roo.isIOS && this.useNativeIOS){
13946 this.setIOSValue(v);
13956 if(this.valueField){
13957 var r = this.findRecord(this.valueField, v);
13959 text = r.data[this.displayField];
13960 }else if(this.valueNotFoundText !== undefined){
13961 text = this.valueNotFoundText;
13964 this.lastSelectionText = text;
13965 if(this.hiddenField){
13966 this.hiddenField.dom.value = v;
13968 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13971 var close = this.closeTriggerEl();
13974 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13980 * @property {Object} the last set data for the element
13985 * Sets the value of the field based on a object which is related to the record format for the store.
13986 * @param {Object} value the value to set as. or false on reset?
13988 setFromData : function(o){
13995 var dv = ''; // display value
13996 var vv = ''; // value value..
13998 if (this.displayField) {
13999 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14001 // this is an error condition!!!
14002 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14005 if(this.valueField){
14006 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14009 var close = this.closeTriggerEl();
14012 if(dv.length || vv * 1 > 0){
14014 this.blockFocus=true;
14020 if(this.hiddenField){
14021 this.hiddenField.dom.value = vv;
14023 this.lastSelectionText = dv;
14024 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14028 // no hidden field.. - we store the value in 'value', but still display
14029 // display field!!!!
14030 this.lastSelectionText = dv;
14031 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14038 reset : function(){
14039 // overridden so that last data is reset..
14046 this.setValue(this.originalValue);
14047 //this.clearInvalid();
14048 this.lastData = false;
14050 this.view.clearSelections();
14056 findRecord : function(prop, value){
14058 if(this.store.getCount() > 0){
14059 this.store.each(function(r){
14060 if(r.data[prop] == value){
14070 getName: function()
14072 // returns hidden if it's set..
14073 if (!this.rendered) {return ''};
14074 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14078 onViewMove : function(e, t){
14079 this.inKeyMode = false;
14083 onViewOver : function(e, t){
14084 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14087 var item = this.view.findItemFromChild(t);
14090 var index = this.view.indexOf(item);
14091 this.select(index, false);
14096 onViewClick : function(view, doFocus, el, e)
14098 var index = this.view.getSelectedIndexes()[0];
14100 var r = this.store.getAt(index);
14104 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14111 Roo.each(this.tickItems, function(v,k){
14113 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14115 _this.tickItems.splice(k, 1);
14117 if(typeof(e) == 'undefined' && view == false){
14118 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14130 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14131 this.tickItems.push(r.data);
14134 if(typeof(e) == 'undefined' && view == false){
14135 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14142 this.onSelect(r, index);
14144 if(doFocus !== false && !this.blockFocus){
14145 this.inputEl().focus();
14150 restrictHeight : function(){
14151 //this.innerList.dom.style.height = '';
14152 //var inner = this.innerList.dom;
14153 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14154 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14155 //this.list.beginUpdate();
14156 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14157 this.list.alignTo(this.inputEl(), this.listAlign);
14158 this.list.alignTo(this.inputEl(), this.listAlign);
14159 //this.list.endUpdate();
14163 onEmptyResults : function(){
14165 if(this.tickable && this.editable){
14166 this.hasFocus = false;
14167 this.restrictHeight();
14175 * Returns true if the dropdown list is expanded, else false.
14177 isExpanded : function(){
14178 return this.list.isVisible();
14182 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14183 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14184 * @param {String} value The data value of the item to select
14185 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14186 * selected item if it is not currently in view (defaults to true)
14187 * @return {Boolean} True if the value matched an item in the list, else false
14189 selectByValue : function(v, scrollIntoView){
14190 if(v !== undefined && v !== null){
14191 var r = this.findRecord(this.valueField || this.displayField, v);
14193 this.select(this.store.indexOf(r), scrollIntoView);
14201 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14202 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14203 * @param {Number} index The zero-based index of the list item to select
14204 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14205 * selected item if it is not currently in view (defaults to true)
14207 select : function(index, scrollIntoView){
14208 this.selectedIndex = index;
14209 this.view.select(index);
14210 if(scrollIntoView !== false){
14211 var el = this.view.getNode(index);
14213 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14216 this.list.scrollChildIntoView(el, false);
14222 selectNext : function(){
14223 var ct = this.store.getCount();
14225 if(this.selectedIndex == -1){
14227 }else if(this.selectedIndex < ct-1){
14228 this.select(this.selectedIndex+1);
14234 selectPrev : function(){
14235 var ct = this.store.getCount();
14237 if(this.selectedIndex == -1){
14239 }else if(this.selectedIndex != 0){
14240 this.select(this.selectedIndex-1);
14246 onKeyUp : function(e){
14247 if(this.editable !== false && !e.isSpecialKey()){
14248 this.lastKey = e.getKey();
14249 this.dqTask.delay(this.queryDelay);
14254 validateBlur : function(){
14255 return !this.list || !this.list.isVisible();
14259 initQuery : function(){
14261 var v = this.getRawValue();
14263 if(this.tickable && this.editable){
14264 v = this.tickableInputEl().getValue();
14271 doForce : function(){
14272 if(this.inputEl().dom.value.length > 0){
14273 this.inputEl().dom.value =
14274 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14280 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14281 * query allowing the query action to be canceled if needed.
14282 * @param {String} query The SQL query to execute
14283 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14284 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14285 * saved in the current store (defaults to false)
14287 doQuery : function(q, forceAll){
14289 if(q === undefined || q === null){
14294 forceAll: forceAll,
14298 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14303 forceAll = qe.forceAll;
14304 if(forceAll === true || (q.length >= this.minChars)){
14306 this.hasQuery = true;
14308 if(this.lastQuery != q || this.alwaysQuery){
14309 this.lastQuery = q;
14310 if(this.mode == 'local'){
14311 this.selectedIndex = -1;
14313 this.store.clearFilter();
14316 if(this.specialFilter){
14317 this.fireEvent('specialfilter', this);
14322 this.store.filter(this.displayField, q);
14325 this.store.fireEvent("datachanged", this.store);
14332 this.store.baseParams[this.queryParam] = q;
14334 var options = {params : this.getParams(q)};
14337 options.add = true;
14338 options.params.start = this.page * this.pageSize;
14341 this.store.load(options);
14344 * this code will make the page width larger, at the beginning, the list not align correctly,
14345 * we should expand the list on onLoad
14346 * so command out it
14351 this.selectedIndex = -1;
14356 this.loadNext = false;
14360 getParams : function(q){
14362 //p[this.queryParam] = q;
14366 p.limit = this.pageSize;
14372 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14374 collapse : function(){
14375 if(!this.isExpanded()){
14381 this.hasFocus = false;
14385 this.cancelBtn.hide();
14386 this.trigger.show();
14389 this.tickableInputEl().dom.value = '';
14390 this.tickableInputEl().blur();
14395 Roo.get(document).un('mousedown', this.collapseIf, this);
14396 Roo.get(document).un('mousewheel', this.collapseIf, this);
14397 if (!this.editable) {
14398 Roo.get(document).un('keydown', this.listKeyPress, this);
14400 this.fireEvent('collapse', this);
14406 collapseIf : function(e){
14407 var in_combo = e.within(this.el);
14408 var in_list = e.within(this.list);
14409 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14411 if (in_combo || in_list || is_list) {
14412 //e.stopPropagation();
14417 this.onTickableFooterButtonClick(e, false, false);
14425 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14427 expand : function(){
14429 if(this.isExpanded() || !this.hasFocus){
14433 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14434 this.list.setWidth(lw);
14440 this.restrictHeight();
14444 this.tickItems = Roo.apply([], this.item);
14447 this.cancelBtn.show();
14448 this.trigger.hide();
14451 this.tickableInputEl().focus();
14456 Roo.get(document).on('mousedown', this.collapseIf, this);
14457 Roo.get(document).on('mousewheel', this.collapseIf, this);
14458 if (!this.editable) {
14459 Roo.get(document).on('keydown', this.listKeyPress, this);
14462 this.fireEvent('expand', this);
14466 // Implements the default empty TriggerField.onTriggerClick function
14467 onTriggerClick : function(e)
14469 Roo.log('trigger click');
14471 if(this.disabled || !this.triggerList){
14476 this.loadNext = false;
14478 if(this.isExpanded()){
14480 if (!this.blockFocus) {
14481 this.inputEl().focus();
14485 this.hasFocus = true;
14486 if(this.triggerAction == 'all') {
14487 this.doQuery(this.allQuery, true);
14489 this.doQuery(this.getRawValue());
14491 if (!this.blockFocus) {
14492 this.inputEl().focus();
14497 onTickableTriggerClick : function(e)
14504 this.loadNext = false;
14505 this.hasFocus = true;
14507 if(this.triggerAction == 'all') {
14508 this.doQuery(this.allQuery, true);
14510 this.doQuery(this.getRawValue());
14514 onSearchFieldClick : function(e)
14516 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14517 this.onTickableFooterButtonClick(e, false, false);
14521 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14526 this.loadNext = false;
14527 this.hasFocus = true;
14529 if(this.triggerAction == 'all') {
14530 this.doQuery(this.allQuery, true);
14532 this.doQuery(this.getRawValue());
14536 listKeyPress : function(e)
14538 //Roo.log('listkeypress');
14539 // scroll to first matching element based on key pres..
14540 if (e.isSpecialKey()) {
14543 var k = String.fromCharCode(e.getKey()).toUpperCase();
14546 var csel = this.view.getSelectedNodes();
14547 var cselitem = false;
14549 var ix = this.view.indexOf(csel[0]);
14550 cselitem = this.store.getAt(ix);
14551 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14557 this.store.each(function(v) {
14559 // start at existing selection.
14560 if (cselitem.id == v.id) {
14566 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14567 match = this.store.indexOf(v);
14573 if (match === false) {
14574 return true; // no more action?
14577 this.view.select(match);
14578 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14579 sn.scrollIntoView(sn.dom.parentNode, false);
14582 onViewScroll : function(e, t){
14584 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){
14588 this.hasQuery = true;
14590 this.loading = this.list.select('.loading', true).first();
14592 if(this.loading === null){
14593 this.list.createChild({
14595 cls: 'loading roo-select2-more-results roo-select2-active',
14596 html: 'Loading more results...'
14599 this.loading = this.list.select('.loading', true).first();
14601 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14603 this.loading.hide();
14606 this.loading.show();
14611 this.loadNext = true;
14613 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14618 addItem : function(o)
14620 var dv = ''; // display value
14622 if (this.displayField) {
14623 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14625 // this is an error condition!!!
14626 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14633 var choice = this.choices.createChild({
14635 cls: 'roo-select2-search-choice',
14644 cls: 'roo-select2-search-choice-close fa fa-times',
14649 }, this.searchField);
14651 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14653 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14661 this.inputEl().dom.value = '';
14666 onRemoveItem : function(e, _self, o)
14668 e.preventDefault();
14670 this.lastItem = Roo.apply([], this.item);
14672 var index = this.item.indexOf(o.data) * 1;
14675 Roo.log('not this item?!');
14679 this.item.splice(index, 1);
14684 this.fireEvent('remove', this, e);
14690 syncValue : function()
14692 if(!this.item.length){
14699 Roo.each(this.item, function(i){
14700 if(_this.valueField){
14701 value.push(i[_this.valueField]);
14708 this.value = value.join(',');
14710 if(this.hiddenField){
14711 this.hiddenField.dom.value = this.value;
14714 this.store.fireEvent("datachanged", this.store);
14719 clearItem : function()
14721 if(!this.multiple){
14727 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14735 if(this.tickable && !Roo.isTouch){
14736 this.view.refresh();
14740 inputEl: function ()
14742 if(Roo.isIOS && this.useNativeIOS){
14743 return this.el.select('select.roo-ios-select', true).first();
14746 if(Roo.isTouch && this.mobileTouchView){
14747 return this.el.select('input.form-control',true).first();
14751 return this.searchField;
14754 return this.el.select('input.form-control',true).first();
14757 onTickableFooterButtonClick : function(e, btn, el)
14759 e.preventDefault();
14761 this.lastItem = Roo.apply([], this.item);
14763 if(btn && btn.name == 'cancel'){
14764 this.tickItems = Roo.apply([], this.item);
14773 Roo.each(this.tickItems, function(o){
14781 validate : function()
14783 if(this.getVisibilityEl().hasClass('hidden')){
14787 var v = this.getRawValue();
14790 v = this.getValue();
14793 if(this.disabled || this.allowBlank || v.length){
14798 this.markInvalid();
14802 tickableInputEl : function()
14804 if(!this.tickable || !this.editable){
14805 return this.inputEl();
14808 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14812 getAutoCreateTouchView : function()
14817 cls: 'form-group' //input-group
14823 type : this.inputType,
14824 cls : 'form-control x-combo-noedit',
14825 autocomplete: 'new-password',
14826 placeholder : this.placeholder || '',
14831 input.name = this.name;
14835 input.cls += ' input-' + this.size;
14838 if (this.disabled) {
14839 input.disabled = true;
14850 inputblock.cls += ' input-group';
14852 inputblock.cn.unshift({
14854 cls : 'input-group-addon',
14859 if(this.removable && !this.multiple){
14860 inputblock.cls += ' roo-removable';
14862 inputblock.cn.push({
14865 cls : 'roo-combo-removable-btn close'
14869 if(this.hasFeedback && !this.allowBlank){
14871 inputblock.cls += ' has-feedback';
14873 inputblock.cn.push({
14875 cls: 'glyphicon form-control-feedback'
14882 inputblock.cls += (this.before) ? '' : ' input-group';
14884 inputblock.cn.push({
14886 cls : 'input-group-addon',
14897 cls: 'form-hidden-field'
14911 cls: 'form-hidden-field'
14915 cls: 'roo-select2-choices',
14919 cls: 'roo-select2-search-field',
14932 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14938 if(!this.multiple && this.showToggleBtn){
14945 if (this.caret != false) {
14948 cls: 'fa fa-' + this.caret
14955 cls : 'input-group-addon btn dropdown-toggle',
14960 cls: 'combobox-clear',
14974 combobox.cls += ' roo-select2-container-multi';
14977 var align = this.labelAlign || this.parentLabelAlign();
14979 if (align ==='left' && this.fieldLabel.length) {
14984 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14985 tooltip : 'This field is required'
14989 cls : 'control-label',
14990 html : this.fieldLabel
15001 var labelCfg = cfg.cn[1];
15002 var contentCfg = cfg.cn[2];
15005 if(this.indicatorpos == 'right'){
15010 cls : 'control-label',
15014 html : this.fieldLabel
15018 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15019 tooltip : 'This field is required'
15032 labelCfg = cfg.cn[0];
15033 contentCfg = cfg.cn[1];
15038 if(this.labelWidth > 12){
15039 labelCfg.style = "width: " + this.labelWidth + 'px';
15042 if(this.labelWidth < 13 && this.labelmd == 0){
15043 this.labelmd = this.labelWidth;
15046 if(this.labellg > 0){
15047 labelCfg.cls += ' col-lg-' + this.labellg;
15048 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15051 if(this.labelmd > 0){
15052 labelCfg.cls += ' col-md-' + this.labelmd;
15053 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15056 if(this.labelsm > 0){
15057 labelCfg.cls += ' col-sm-' + this.labelsm;
15058 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15061 if(this.labelxs > 0){
15062 labelCfg.cls += ' col-xs-' + this.labelxs;
15063 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15067 } else if ( this.fieldLabel.length) {
15071 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15072 tooltip : 'This field is required'
15076 cls : 'control-label',
15077 html : this.fieldLabel
15088 if(this.indicatorpos == 'right'){
15092 cls : 'control-label',
15093 html : this.fieldLabel,
15097 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15098 tooltip : 'This field is required'
15115 var settings = this;
15117 ['xs','sm','md','lg'].map(function(size){
15118 if (settings[size]) {
15119 cfg.cls += ' col-' + size + '-' + settings[size];
15126 initTouchView : function()
15128 this.renderTouchView();
15130 this.touchViewEl.on('scroll', function(){
15131 this.el.dom.scrollTop = 0;
15134 this.originalValue = this.getValue();
15136 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15138 this.inputEl().on("click", this.showTouchView, this);
15139 if (this.triggerEl) {
15140 this.triggerEl.on("click", this.showTouchView, this);
15144 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15145 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15147 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15149 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15150 this.store.on('load', this.onTouchViewLoad, this);
15151 this.store.on('loadexception', this.onTouchViewLoadException, this);
15153 if(this.hiddenName){
15155 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15157 this.hiddenField.dom.value =
15158 this.hiddenValue !== undefined ? this.hiddenValue :
15159 this.value !== undefined ? this.value : '';
15161 this.el.dom.removeAttribute('name');
15162 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15166 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15167 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15170 if(this.removable && !this.multiple){
15171 var close = this.closeTriggerEl();
15173 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15174 close.on('click', this.removeBtnClick, this, close);
15178 * fix the bug in Safari iOS8
15180 this.inputEl().on("focus", function(e){
15181 document.activeElement.blur();
15189 renderTouchView : function()
15191 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15192 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15194 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15195 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15197 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15198 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15199 this.touchViewBodyEl.setStyle('overflow', 'auto');
15201 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15202 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15204 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15205 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15209 showTouchView : function()
15215 this.touchViewHeaderEl.hide();
15217 if(this.modalTitle.length){
15218 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15219 this.touchViewHeaderEl.show();
15222 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15223 this.touchViewEl.show();
15225 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15227 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15228 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15230 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15232 if(this.modalTitle.length){
15233 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15236 this.touchViewBodyEl.setHeight(bodyHeight);
15240 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15242 this.touchViewEl.addClass('in');
15245 this.doTouchViewQuery();
15249 hideTouchView : function()
15251 this.touchViewEl.removeClass('in');
15255 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15257 this.touchViewEl.setStyle('display', 'none');
15262 setTouchViewValue : function()
15269 Roo.each(this.tickItems, function(o){
15274 this.hideTouchView();
15277 doTouchViewQuery : function()
15286 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15290 if(!this.alwaysQuery || this.mode == 'local'){
15291 this.onTouchViewLoad();
15298 onTouchViewBeforeLoad : function(combo,opts)
15304 onTouchViewLoad : function()
15306 if(this.store.getCount() < 1){
15307 this.onTouchViewEmptyResults();
15311 this.clearTouchView();
15313 var rawValue = this.getRawValue();
15315 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15317 this.tickItems = [];
15319 this.store.data.each(function(d, rowIndex){
15320 var row = this.touchViewListGroup.createChild(template);
15322 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15323 row.addClass(d.data.cls);
15326 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15329 html : d.data[this.displayField]
15332 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15333 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15336 row.removeClass('selected');
15337 if(!this.multiple && this.valueField &&
15338 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15341 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15342 row.addClass('selected');
15345 if(this.multiple && this.valueField &&
15346 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15350 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15351 this.tickItems.push(d.data);
15354 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15358 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15360 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15362 if(this.modalTitle.length){
15363 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15366 var listHeight = this.touchViewListGroup.getHeight();
15370 if(firstChecked && listHeight > bodyHeight){
15371 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15376 onTouchViewLoadException : function()
15378 this.hideTouchView();
15381 onTouchViewEmptyResults : function()
15383 this.clearTouchView();
15385 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15387 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15391 clearTouchView : function()
15393 this.touchViewListGroup.dom.innerHTML = '';
15396 onTouchViewClick : function(e, el, o)
15398 e.preventDefault();
15401 var rowIndex = o.rowIndex;
15403 var r = this.store.getAt(rowIndex);
15405 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15407 if(!this.multiple){
15408 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15409 c.dom.removeAttribute('checked');
15412 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15414 this.setFromData(r.data);
15416 var close = this.closeTriggerEl();
15422 this.hideTouchView();
15424 this.fireEvent('select', this, r, rowIndex);
15429 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15430 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15431 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15435 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15436 this.addItem(r.data);
15437 this.tickItems.push(r.data);
15441 getAutoCreateNativeIOS : function()
15444 cls: 'form-group' //input-group,
15449 cls : 'roo-ios-select'
15453 combobox.name = this.name;
15456 if (this.disabled) {
15457 combobox.disabled = true;
15460 var settings = this;
15462 ['xs','sm','md','lg'].map(function(size){
15463 if (settings[size]) {
15464 cfg.cls += ' col-' + size + '-' + settings[size];
15474 initIOSView : function()
15476 this.store.on('load', this.onIOSViewLoad, this);
15481 onIOSViewLoad : function()
15483 if(this.store.getCount() < 1){
15487 this.clearIOSView();
15489 if(this.allowBlank) {
15491 var default_text = '-- SELECT --';
15493 if(this.placeholder.length){
15494 default_text = this.placeholder;
15497 if(this.emptyTitle.length){
15498 default_text += ' - ' + this.emptyTitle + ' -';
15501 var opt = this.inputEl().createChild({
15504 html : default_text
15508 o[this.valueField] = 0;
15509 o[this.displayField] = default_text;
15511 this.ios_options.push({
15518 this.store.data.each(function(d, rowIndex){
15522 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15523 html = d.data[this.displayField];
15528 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15529 value = d.data[this.valueField];
15538 if(this.value == d.data[this.valueField]){
15539 option['selected'] = true;
15542 var opt = this.inputEl().createChild(option);
15544 this.ios_options.push({
15551 this.inputEl().on('change', function(){
15552 this.fireEvent('select', this);
15557 clearIOSView: function()
15559 this.inputEl().dom.innerHTML = '';
15561 this.ios_options = [];
15564 setIOSValue: function(v)
15568 if(!this.ios_options){
15572 Roo.each(this.ios_options, function(opts){
15574 opts.el.dom.removeAttribute('selected');
15576 if(opts.data[this.valueField] != v){
15580 opts.el.dom.setAttribute('selected', true);
15586 * @cfg {Boolean} grow
15590 * @cfg {Number} growMin
15594 * @cfg {Number} growMax
15603 Roo.apply(Roo.bootstrap.ComboBox, {
15607 cls: 'modal-header',
15629 cls: 'list-group-item',
15633 cls: 'roo-combobox-list-group-item-value'
15637 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15651 listItemCheckbox : {
15653 cls: 'list-group-item',
15657 cls: 'roo-combobox-list-group-item-value'
15661 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15677 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15682 cls: 'modal-footer',
15690 cls: 'col-xs-6 text-left',
15693 cls: 'btn btn-danger roo-touch-view-cancel',
15699 cls: 'col-xs-6 text-right',
15702 cls: 'btn btn-success roo-touch-view-ok',
15713 Roo.apply(Roo.bootstrap.ComboBox, {
15715 touchViewTemplate : {
15717 cls: 'modal fade roo-combobox-touch-view',
15721 cls: 'modal-dialog',
15722 style : 'position:fixed', // we have to fix position....
15726 cls: 'modal-content',
15728 Roo.bootstrap.ComboBox.header,
15729 Roo.bootstrap.ComboBox.body,
15730 Roo.bootstrap.ComboBox.footer
15739 * Ext JS Library 1.1.1
15740 * Copyright(c) 2006-2007, Ext JS, LLC.
15742 * Originally Released Under LGPL - original licence link has changed is not relivant.
15745 * <script type="text/javascript">
15750 * @extends Roo.util.Observable
15751 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15752 * This class also supports single and multi selection modes. <br>
15753 * Create a data model bound view:
15755 var store = new Roo.data.Store(...);
15757 var view = new Roo.View({
15759 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15761 singleSelect: true,
15762 selectedClass: "ydataview-selected",
15766 // listen for node click?
15767 view.on("click", function(vw, index, node, e){
15768 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15772 dataModel.load("foobar.xml");
15774 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15776 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15777 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15779 * Note: old style constructor is still suported (container, template, config)
15782 * Create a new View
15783 * @param {Object} config The config object
15786 Roo.View = function(config, depreciated_tpl, depreciated_config){
15788 this.parent = false;
15790 if (typeof(depreciated_tpl) == 'undefined') {
15791 // new way.. - universal constructor.
15792 Roo.apply(this, config);
15793 this.el = Roo.get(this.el);
15796 this.el = Roo.get(config);
15797 this.tpl = depreciated_tpl;
15798 Roo.apply(this, depreciated_config);
15800 this.wrapEl = this.el.wrap().wrap();
15801 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15804 if(typeof(this.tpl) == "string"){
15805 this.tpl = new Roo.Template(this.tpl);
15807 // support xtype ctors..
15808 this.tpl = new Roo.factory(this.tpl, Roo);
15812 this.tpl.compile();
15817 * @event beforeclick
15818 * Fires before a click is processed. Returns false to cancel the default action.
15819 * @param {Roo.View} this
15820 * @param {Number} index The index of the target node
15821 * @param {HTMLElement} node The target node
15822 * @param {Roo.EventObject} e The raw event object
15824 "beforeclick" : true,
15827 * Fires when a template node is clicked.
15828 * @param {Roo.View} this
15829 * @param {Number} index The index of the target node
15830 * @param {HTMLElement} node The target node
15831 * @param {Roo.EventObject} e The raw event object
15836 * Fires when a template node is double clicked.
15837 * @param {Roo.View} this
15838 * @param {Number} index The index of the target node
15839 * @param {HTMLElement} node The target node
15840 * @param {Roo.EventObject} e The raw event object
15844 * @event contextmenu
15845 * Fires when a template node is right clicked.
15846 * @param {Roo.View} this
15847 * @param {Number} index The index of the target node
15848 * @param {HTMLElement} node The target node
15849 * @param {Roo.EventObject} e The raw event object
15851 "contextmenu" : true,
15853 * @event selectionchange
15854 * Fires when the selected nodes change.
15855 * @param {Roo.View} this
15856 * @param {Array} selections Array of the selected nodes
15858 "selectionchange" : true,
15861 * @event beforeselect
15862 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15863 * @param {Roo.View} this
15864 * @param {HTMLElement} node The node to be selected
15865 * @param {Array} selections Array of currently selected nodes
15867 "beforeselect" : true,
15869 * @event preparedata
15870 * Fires on every row to render, to allow you to change the data.
15871 * @param {Roo.View} this
15872 * @param {Object} data to be rendered (change this)
15874 "preparedata" : true
15882 "click": this.onClick,
15883 "dblclick": this.onDblClick,
15884 "contextmenu": this.onContextMenu,
15888 this.selections = [];
15890 this.cmp = new Roo.CompositeElementLite([]);
15892 this.store = Roo.factory(this.store, Roo.data);
15893 this.setStore(this.store, true);
15896 if ( this.footer && this.footer.xtype) {
15898 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15900 this.footer.dataSource = this.store;
15901 this.footer.container = fctr;
15902 this.footer = Roo.factory(this.footer, Roo);
15903 fctr.insertFirst(this.el);
15905 // this is a bit insane - as the paging toolbar seems to detach the el..
15906 // dom.parentNode.parentNode.parentNode
15907 // they get detached?
15911 Roo.View.superclass.constructor.call(this);
15916 Roo.extend(Roo.View, Roo.util.Observable, {
15919 * @cfg {Roo.data.Store} store Data store to load data from.
15924 * @cfg {String|Roo.Element} el The container element.
15929 * @cfg {String|Roo.Template} tpl The template used by this View
15933 * @cfg {String} dataName the named area of the template to use as the data area
15934 * Works with domtemplates roo-name="name"
15938 * @cfg {String} selectedClass The css class to add to selected nodes
15940 selectedClass : "x-view-selected",
15942 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15947 * @cfg {String} text to display on mask (default Loading)
15951 * @cfg {Boolean} multiSelect Allow multiple selection
15953 multiSelect : false,
15955 * @cfg {Boolean} singleSelect Allow single selection
15957 singleSelect: false,
15960 * @cfg {Boolean} toggleSelect - selecting
15962 toggleSelect : false,
15965 * @cfg {Boolean} tickable - selecting
15970 * Returns the element this view is bound to.
15971 * @return {Roo.Element}
15973 getEl : function(){
15974 return this.wrapEl;
15980 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15982 refresh : function(){
15983 //Roo.log('refresh');
15986 // if we are using something like 'domtemplate', then
15987 // the what gets used is:
15988 // t.applySubtemplate(NAME, data, wrapping data..)
15989 // the outer template then get' applied with
15990 // the store 'extra data'
15991 // and the body get's added to the
15992 // roo-name="data" node?
15993 // <span class='roo-tpl-{name}'></span> ?????
15997 this.clearSelections();
15998 this.el.update("");
16000 var records = this.store.getRange();
16001 if(records.length < 1) {
16003 // is this valid?? = should it render a template??
16005 this.el.update(this.emptyText);
16009 if (this.dataName) {
16010 this.el.update(t.apply(this.store.meta)); //????
16011 el = this.el.child('.roo-tpl-' + this.dataName);
16014 for(var i = 0, len = records.length; i < len; i++){
16015 var data = this.prepareData(records[i].data, i, records[i]);
16016 this.fireEvent("preparedata", this, data, i, records[i]);
16018 var d = Roo.apply({}, data);
16021 Roo.apply(d, {'roo-id' : Roo.id()});
16025 Roo.each(this.parent.item, function(item){
16026 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16029 Roo.apply(d, {'roo-data-checked' : 'checked'});
16033 html[html.length] = Roo.util.Format.trim(
16035 t.applySubtemplate(this.dataName, d, this.store.meta) :
16042 el.update(html.join(""));
16043 this.nodes = el.dom.childNodes;
16044 this.updateIndexes(0);
16049 * Function to override to reformat the data that is sent to
16050 * the template for each node.
16051 * DEPRICATED - use the preparedata event handler.
16052 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16053 * a JSON object for an UpdateManager bound view).
16055 prepareData : function(data, index, record)
16057 this.fireEvent("preparedata", this, data, index, record);
16061 onUpdate : function(ds, record){
16062 // Roo.log('on update');
16063 this.clearSelections();
16064 var index = this.store.indexOf(record);
16065 var n = this.nodes[index];
16066 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16067 n.parentNode.removeChild(n);
16068 this.updateIndexes(index, index);
16074 onAdd : function(ds, records, index)
16076 //Roo.log(['on Add', ds, records, index] );
16077 this.clearSelections();
16078 if(this.nodes.length == 0){
16082 var n = this.nodes[index];
16083 for(var i = 0, len = records.length; i < len; i++){
16084 var d = this.prepareData(records[i].data, i, records[i]);
16086 this.tpl.insertBefore(n, d);
16089 this.tpl.append(this.el, d);
16092 this.updateIndexes(index);
16095 onRemove : function(ds, record, index){
16096 // Roo.log('onRemove');
16097 this.clearSelections();
16098 var el = this.dataName ?
16099 this.el.child('.roo-tpl-' + this.dataName) :
16102 el.dom.removeChild(this.nodes[index]);
16103 this.updateIndexes(index);
16107 * Refresh an individual node.
16108 * @param {Number} index
16110 refreshNode : function(index){
16111 this.onUpdate(this.store, this.store.getAt(index));
16114 updateIndexes : function(startIndex, endIndex){
16115 var ns = this.nodes;
16116 startIndex = startIndex || 0;
16117 endIndex = endIndex || ns.length - 1;
16118 for(var i = startIndex; i <= endIndex; i++){
16119 ns[i].nodeIndex = i;
16124 * Changes the data store this view uses and refresh the view.
16125 * @param {Store} store
16127 setStore : function(store, initial){
16128 if(!initial && this.store){
16129 this.store.un("datachanged", this.refresh);
16130 this.store.un("add", this.onAdd);
16131 this.store.un("remove", this.onRemove);
16132 this.store.un("update", this.onUpdate);
16133 this.store.un("clear", this.refresh);
16134 this.store.un("beforeload", this.onBeforeLoad);
16135 this.store.un("load", this.onLoad);
16136 this.store.un("loadexception", this.onLoad);
16140 store.on("datachanged", this.refresh, this);
16141 store.on("add", this.onAdd, this);
16142 store.on("remove", this.onRemove, this);
16143 store.on("update", this.onUpdate, this);
16144 store.on("clear", this.refresh, this);
16145 store.on("beforeload", this.onBeforeLoad, this);
16146 store.on("load", this.onLoad, this);
16147 store.on("loadexception", this.onLoad, this);
16155 * onbeforeLoad - masks the loading area.
16158 onBeforeLoad : function(store,opts)
16160 //Roo.log('onBeforeLoad');
16162 this.el.update("");
16164 this.el.mask(this.mask ? this.mask : "Loading" );
16166 onLoad : function ()
16173 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16174 * @param {HTMLElement} node
16175 * @return {HTMLElement} The template node
16177 findItemFromChild : function(node){
16178 var el = this.dataName ?
16179 this.el.child('.roo-tpl-' + this.dataName,true) :
16182 if(!node || node.parentNode == el){
16185 var p = node.parentNode;
16186 while(p && p != el){
16187 if(p.parentNode == el){
16196 onClick : function(e){
16197 var item = this.findItemFromChild(e.getTarget());
16199 var index = this.indexOf(item);
16200 if(this.onItemClick(item, index, e) !== false){
16201 this.fireEvent("click", this, index, item, e);
16204 this.clearSelections();
16209 onContextMenu : function(e){
16210 var item = this.findItemFromChild(e.getTarget());
16212 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16217 onDblClick : function(e){
16218 var item = this.findItemFromChild(e.getTarget());
16220 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16224 onItemClick : function(item, index, e)
16226 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16229 if (this.toggleSelect) {
16230 var m = this.isSelected(item) ? 'unselect' : 'select';
16233 _t[m](item, true, false);
16236 if(this.multiSelect || this.singleSelect){
16237 if(this.multiSelect && e.shiftKey && this.lastSelection){
16238 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16240 this.select(item, this.multiSelect && e.ctrlKey);
16241 this.lastSelection = item;
16244 if(!this.tickable){
16245 e.preventDefault();
16253 * Get the number of selected nodes.
16256 getSelectionCount : function(){
16257 return this.selections.length;
16261 * Get the currently selected nodes.
16262 * @return {Array} An array of HTMLElements
16264 getSelectedNodes : function(){
16265 return this.selections;
16269 * Get the indexes of the selected nodes.
16272 getSelectedIndexes : function(){
16273 var indexes = [], s = this.selections;
16274 for(var i = 0, len = s.length; i < len; i++){
16275 indexes.push(s[i].nodeIndex);
16281 * Clear all selections
16282 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16284 clearSelections : function(suppressEvent){
16285 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16286 this.cmp.elements = this.selections;
16287 this.cmp.removeClass(this.selectedClass);
16288 this.selections = [];
16289 if(!suppressEvent){
16290 this.fireEvent("selectionchange", this, this.selections);
16296 * Returns true if the passed node is selected
16297 * @param {HTMLElement/Number} node The node or node index
16298 * @return {Boolean}
16300 isSelected : function(node){
16301 var s = this.selections;
16305 node = this.getNode(node);
16306 return s.indexOf(node) !== -1;
16311 * @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
16312 * @param {Boolean} keepExisting (optional) true to keep existing selections
16313 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16315 select : function(nodeInfo, keepExisting, suppressEvent){
16316 if(nodeInfo instanceof Array){
16318 this.clearSelections(true);
16320 for(var i = 0, len = nodeInfo.length; i < len; i++){
16321 this.select(nodeInfo[i], true, true);
16325 var node = this.getNode(nodeInfo);
16326 if(!node || this.isSelected(node)){
16327 return; // already selected.
16330 this.clearSelections(true);
16333 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16334 Roo.fly(node).addClass(this.selectedClass);
16335 this.selections.push(node);
16336 if(!suppressEvent){
16337 this.fireEvent("selectionchange", this, this.selections);
16345 * @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
16346 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16347 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16349 unselect : function(nodeInfo, keepExisting, suppressEvent)
16351 if(nodeInfo instanceof Array){
16352 Roo.each(this.selections, function(s) {
16353 this.unselect(s, nodeInfo);
16357 var node = this.getNode(nodeInfo);
16358 if(!node || !this.isSelected(node)){
16359 //Roo.log("not selected");
16360 return; // not selected.
16364 Roo.each(this.selections, function(s) {
16366 Roo.fly(node).removeClass(this.selectedClass);
16373 this.selections= ns;
16374 this.fireEvent("selectionchange", this, this.selections);
16378 * Gets a template node.
16379 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16380 * @return {HTMLElement} The node or null if it wasn't found
16382 getNode : function(nodeInfo){
16383 if(typeof nodeInfo == "string"){
16384 return document.getElementById(nodeInfo);
16385 }else if(typeof nodeInfo == "number"){
16386 return this.nodes[nodeInfo];
16392 * Gets a range template nodes.
16393 * @param {Number} startIndex
16394 * @param {Number} endIndex
16395 * @return {Array} An array of nodes
16397 getNodes : function(start, end){
16398 var ns = this.nodes;
16399 start = start || 0;
16400 end = typeof end == "undefined" ? ns.length - 1 : end;
16403 for(var i = start; i <= end; i++){
16407 for(var i = start; i >= end; i--){
16415 * Finds the index of the passed node
16416 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16417 * @return {Number} The index of the node or -1
16419 indexOf : function(node){
16420 node = this.getNode(node);
16421 if(typeof node.nodeIndex == "number"){
16422 return node.nodeIndex;
16424 var ns = this.nodes;
16425 for(var i = 0, len = ns.length; i < len; i++){
16436 * based on jquery fullcalendar
16440 Roo.bootstrap = Roo.bootstrap || {};
16442 * @class Roo.bootstrap.Calendar
16443 * @extends Roo.bootstrap.Component
16444 * Bootstrap Calendar class
16445 * @cfg {Boolean} loadMask (true|false) default false
16446 * @cfg {Object} header generate the user specific header of the calendar, default false
16449 * Create a new Container
16450 * @param {Object} config The config object
16455 Roo.bootstrap.Calendar = function(config){
16456 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16460 * Fires when a date is selected
16461 * @param {DatePicker} this
16462 * @param {Date} date The selected date
16466 * @event monthchange
16467 * Fires when the displayed month changes
16468 * @param {DatePicker} this
16469 * @param {Date} date The selected month
16471 'monthchange': true,
16473 * @event evententer
16474 * Fires when mouse over an event
16475 * @param {Calendar} this
16476 * @param {event} Event
16478 'evententer': true,
16480 * @event eventleave
16481 * Fires when the mouse leaves an
16482 * @param {Calendar} this
16485 'eventleave': true,
16487 * @event eventclick
16488 * Fires when the mouse click an
16489 * @param {Calendar} this
16498 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16501 * @cfg {Number} startDay
16502 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16510 getAutoCreate : function(){
16513 var fc_button = function(name, corner, style, content ) {
16514 return Roo.apply({},{
16516 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16518 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16521 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16532 style : 'width:100%',
16539 cls : 'fc-header-left',
16541 fc_button('prev', 'left', 'arrow', '‹' ),
16542 fc_button('next', 'right', 'arrow', '›' ),
16543 { tag: 'span', cls: 'fc-header-space' },
16544 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16552 cls : 'fc-header-center',
16556 cls: 'fc-header-title',
16559 html : 'month / year'
16567 cls : 'fc-header-right',
16569 /* fc_button('month', 'left', '', 'month' ),
16570 fc_button('week', '', '', 'week' ),
16571 fc_button('day', 'right', '', 'day' )
16583 header = this.header;
16586 var cal_heads = function() {
16588 // fixme - handle this.
16590 for (var i =0; i < Date.dayNames.length; i++) {
16591 var d = Date.dayNames[i];
16594 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16595 html : d.substring(0,3)
16599 ret[0].cls += ' fc-first';
16600 ret[6].cls += ' fc-last';
16603 var cal_cell = function(n) {
16606 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16611 cls: 'fc-day-number',
16615 cls: 'fc-day-content',
16619 style: 'position: relative;' // height: 17px;
16631 var cal_rows = function() {
16634 for (var r = 0; r < 6; r++) {
16641 for (var i =0; i < Date.dayNames.length; i++) {
16642 var d = Date.dayNames[i];
16643 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16646 row.cn[0].cls+=' fc-first';
16647 row.cn[0].cn[0].style = 'min-height:90px';
16648 row.cn[6].cls+=' fc-last';
16652 ret[0].cls += ' fc-first';
16653 ret[4].cls += ' fc-prev-last';
16654 ret[5].cls += ' fc-last';
16661 cls: 'fc-border-separate',
16662 style : 'width:100%',
16670 cls : 'fc-first fc-last',
16688 cls : 'fc-content',
16689 style : "position: relative;",
16692 cls : 'fc-view fc-view-month fc-grid',
16693 style : 'position: relative',
16694 unselectable : 'on',
16697 cls : 'fc-event-container',
16698 style : 'position:absolute;z-index:8;top:0;left:0;'
16716 initEvents : function()
16719 throw "can not find store for calendar";
16725 style: "text-align:center",
16729 style: "background-color:white;width:50%;margin:250 auto",
16733 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16744 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16746 var size = this.el.select('.fc-content', true).first().getSize();
16747 this.maskEl.setSize(size.width, size.height);
16748 this.maskEl.enableDisplayMode("block");
16749 if(!this.loadMask){
16750 this.maskEl.hide();
16753 this.store = Roo.factory(this.store, Roo.data);
16754 this.store.on('load', this.onLoad, this);
16755 this.store.on('beforeload', this.onBeforeLoad, this);
16759 this.cells = this.el.select('.fc-day',true);
16760 //Roo.log(this.cells);
16761 this.textNodes = this.el.query('.fc-day-number');
16762 this.cells.addClassOnOver('fc-state-hover');
16764 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16765 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16766 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16767 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16769 this.on('monthchange', this.onMonthChange, this);
16771 this.update(new Date().clearTime());
16774 resize : function() {
16775 var sz = this.el.getSize();
16777 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16778 this.el.select('.fc-day-content div',true).setHeight(34);
16783 showPrevMonth : function(e){
16784 this.update(this.activeDate.add("mo", -1));
16786 showToday : function(e){
16787 this.update(new Date().clearTime());
16790 showNextMonth : function(e){
16791 this.update(this.activeDate.add("mo", 1));
16795 showPrevYear : function(){
16796 this.update(this.activeDate.add("y", -1));
16800 showNextYear : function(){
16801 this.update(this.activeDate.add("y", 1));
16806 update : function(date)
16808 var vd = this.activeDate;
16809 this.activeDate = date;
16810 // if(vd && this.el){
16811 // var t = date.getTime();
16812 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16813 // Roo.log('using add remove');
16815 // this.fireEvent('monthchange', this, date);
16817 // this.cells.removeClass("fc-state-highlight");
16818 // this.cells.each(function(c){
16819 // if(c.dateValue == t){
16820 // c.addClass("fc-state-highlight");
16821 // setTimeout(function(){
16822 // try{c.dom.firstChild.focus();}catch(e){}
16832 var days = date.getDaysInMonth();
16834 var firstOfMonth = date.getFirstDateOfMonth();
16835 var startingPos = firstOfMonth.getDay()-this.startDay;
16837 if(startingPos < this.startDay){
16841 var pm = date.add(Date.MONTH, -1);
16842 var prevStart = pm.getDaysInMonth()-startingPos;
16844 this.cells = this.el.select('.fc-day',true);
16845 this.textNodes = this.el.query('.fc-day-number');
16846 this.cells.addClassOnOver('fc-state-hover');
16848 var cells = this.cells.elements;
16849 var textEls = this.textNodes;
16851 Roo.each(cells, function(cell){
16852 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16855 days += startingPos;
16857 // convert everything to numbers so it's fast
16858 var day = 86400000;
16859 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16862 //Roo.log(prevStart);
16864 var today = new Date().clearTime().getTime();
16865 var sel = date.clearTime().getTime();
16866 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16867 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16868 var ddMatch = this.disabledDatesRE;
16869 var ddText = this.disabledDatesText;
16870 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16871 var ddaysText = this.disabledDaysText;
16872 var format = this.format;
16874 var setCellClass = function(cal, cell){
16878 //Roo.log('set Cell Class');
16880 var t = d.getTime();
16884 cell.dateValue = t;
16886 cell.className += " fc-today";
16887 cell.className += " fc-state-highlight";
16888 cell.title = cal.todayText;
16891 // disable highlight in other month..
16892 //cell.className += " fc-state-highlight";
16897 cell.className = " fc-state-disabled";
16898 cell.title = cal.minText;
16902 cell.className = " fc-state-disabled";
16903 cell.title = cal.maxText;
16907 if(ddays.indexOf(d.getDay()) != -1){
16908 cell.title = ddaysText;
16909 cell.className = " fc-state-disabled";
16912 if(ddMatch && format){
16913 var fvalue = d.dateFormat(format);
16914 if(ddMatch.test(fvalue)){
16915 cell.title = ddText.replace("%0", fvalue);
16916 cell.className = " fc-state-disabled";
16920 if (!cell.initialClassName) {
16921 cell.initialClassName = cell.dom.className;
16924 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16929 for(; i < startingPos; i++) {
16930 textEls[i].innerHTML = (++prevStart);
16931 d.setDate(d.getDate()+1);
16933 cells[i].className = "fc-past fc-other-month";
16934 setCellClass(this, cells[i]);
16939 for(; i < days; i++){
16940 intDay = i - startingPos + 1;
16941 textEls[i].innerHTML = (intDay);
16942 d.setDate(d.getDate()+1);
16944 cells[i].className = ''; // "x-date-active";
16945 setCellClass(this, cells[i]);
16949 for(; i < 42; i++) {
16950 textEls[i].innerHTML = (++extraDays);
16951 d.setDate(d.getDate()+1);
16953 cells[i].className = "fc-future fc-other-month";
16954 setCellClass(this, cells[i]);
16957 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16959 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16961 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16962 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16964 if(totalRows != 6){
16965 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16966 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16969 this.fireEvent('monthchange', this, date);
16973 if(!this.internalRender){
16974 var main = this.el.dom.firstChild;
16975 var w = main.offsetWidth;
16976 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16977 Roo.fly(main).setWidth(w);
16978 this.internalRender = true;
16979 // opera does not respect the auto grow header center column
16980 // then, after it gets a width opera refuses to recalculate
16981 // without a second pass
16982 if(Roo.isOpera && !this.secondPass){
16983 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16984 this.secondPass = true;
16985 this.update.defer(10, this, [date]);
16992 findCell : function(dt) {
16993 dt = dt.clearTime().getTime();
16995 this.cells.each(function(c){
16996 //Roo.log("check " +c.dateValue + '?=' + dt);
16997 if(c.dateValue == dt){
17007 findCells : function(ev) {
17008 var s = ev.start.clone().clearTime().getTime();
17010 var e= ev.end.clone().clearTime().getTime();
17013 this.cells.each(function(c){
17014 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17016 if(c.dateValue > e){
17019 if(c.dateValue < s){
17028 // findBestRow: function(cells)
17032 // for (var i =0 ; i < cells.length;i++) {
17033 // ret = Math.max(cells[i].rows || 0,ret);
17040 addItem : function(ev)
17042 // look for vertical location slot in
17043 var cells = this.findCells(ev);
17045 // ev.row = this.findBestRow(cells);
17047 // work out the location.
17051 for(var i =0; i < cells.length; i++) {
17053 cells[i].row = cells[0].row;
17056 cells[i].row = cells[i].row + 1;
17066 if (crow.start.getY() == cells[i].getY()) {
17068 crow.end = cells[i];
17085 cells[0].events.push(ev);
17087 this.calevents.push(ev);
17090 clearEvents: function() {
17092 if(!this.calevents){
17096 Roo.each(this.cells.elements, function(c){
17102 Roo.each(this.calevents, function(e) {
17103 Roo.each(e.els, function(el) {
17104 el.un('mouseenter' ,this.onEventEnter, this);
17105 el.un('mouseleave' ,this.onEventLeave, this);
17110 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17116 renderEvents: function()
17120 this.cells.each(function(c) {
17129 if(c.row != c.events.length){
17130 r = 4 - (4 - (c.row - c.events.length));
17133 c.events = ev.slice(0, r);
17134 c.more = ev.slice(r);
17136 if(c.more.length && c.more.length == 1){
17137 c.events.push(c.more.pop());
17140 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17144 this.cells.each(function(c) {
17146 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17149 for (var e = 0; e < c.events.length; e++){
17150 var ev = c.events[e];
17151 var rows = ev.rows;
17153 for(var i = 0; i < rows.length; i++) {
17155 // how many rows should it span..
17158 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17159 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17161 unselectable : "on",
17164 cls: 'fc-event-inner',
17168 // cls: 'fc-event-time',
17169 // html : cells.length > 1 ? '' : ev.time
17173 cls: 'fc-event-title',
17174 html : String.format('{0}', ev.title)
17181 cls: 'ui-resizable-handle ui-resizable-e',
17182 html : '  '
17189 cfg.cls += ' fc-event-start';
17191 if ((i+1) == rows.length) {
17192 cfg.cls += ' fc-event-end';
17195 var ctr = _this.el.select('.fc-event-container',true).first();
17196 var cg = ctr.createChild(cfg);
17198 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17199 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17201 var r = (c.more.length) ? 1 : 0;
17202 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17203 cg.setWidth(ebox.right - sbox.x -2);
17205 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17206 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17207 cg.on('click', _this.onEventClick, _this, ev);
17218 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17219 style : 'position: absolute',
17220 unselectable : "on",
17223 cls: 'fc-event-inner',
17227 cls: 'fc-event-title',
17235 cls: 'ui-resizable-handle ui-resizable-e',
17236 html : '  '
17242 var ctr = _this.el.select('.fc-event-container',true).first();
17243 var cg = ctr.createChild(cfg);
17245 var sbox = c.select('.fc-day-content',true).first().getBox();
17246 var ebox = c.select('.fc-day-content',true).first().getBox();
17248 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17249 cg.setWidth(ebox.right - sbox.x -2);
17251 cg.on('click', _this.onMoreEventClick, _this, c.more);
17261 onEventEnter: function (e, el,event,d) {
17262 this.fireEvent('evententer', this, el, event);
17265 onEventLeave: function (e, el,event,d) {
17266 this.fireEvent('eventleave', this, el, event);
17269 onEventClick: function (e, el,event,d) {
17270 this.fireEvent('eventclick', this, el, event);
17273 onMonthChange: function () {
17277 onMoreEventClick: function(e, el, more)
17281 this.calpopover.placement = 'right';
17282 this.calpopover.setTitle('More');
17284 this.calpopover.setContent('');
17286 var ctr = this.calpopover.el.select('.popover-content', true).first();
17288 Roo.each(more, function(m){
17290 cls : 'fc-event-hori fc-event-draggable',
17293 var cg = ctr.createChild(cfg);
17295 cg.on('click', _this.onEventClick, _this, m);
17298 this.calpopover.show(el);
17303 onLoad: function ()
17305 this.calevents = [];
17308 if(this.store.getCount() > 0){
17309 this.store.data.each(function(d){
17312 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17313 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17314 time : d.data.start_time,
17315 title : d.data.title,
17316 description : d.data.description,
17317 venue : d.data.venue
17322 this.renderEvents();
17324 if(this.calevents.length && this.loadMask){
17325 this.maskEl.hide();
17329 onBeforeLoad: function()
17331 this.clearEvents();
17333 this.maskEl.show();
17347 * @class Roo.bootstrap.Popover
17348 * @extends Roo.bootstrap.Component
17349 * Bootstrap Popover class
17350 * @cfg {String} html contents of the popover (or false to use children..)
17351 * @cfg {String} title of popover (or false to hide)
17352 * @cfg {String} placement how it is placed
17353 * @cfg {String} trigger click || hover (or false to trigger manually)
17354 * @cfg {String} over what (parent or false to trigger manually.)
17355 * @cfg {Number} delay - delay before showing
17358 * Create a new Popover
17359 * @param {Object} config The config object
17362 Roo.bootstrap.Popover = function(config){
17363 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17369 * After the popover show
17371 * @param {Roo.bootstrap.Popover} this
17376 * After the popover hide
17378 * @param {Roo.bootstrap.Popover} this
17384 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17386 title: 'Fill in a title',
17389 placement : 'right',
17390 trigger : 'hover', // hover
17396 can_build_overlaid : false,
17398 getChildContainer : function()
17400 return this.el.select('.popover-content',true).first();
17403 getAutoCreate : function(){
17406 cls : 'popover roo-dynamic',
17407 style: 'display:block',
17413 cls : 'popover-inner',
17417 cls: 'popover-title',
17421 cls : 'popover-content',
17432 setTitle: function(str)
17435 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17437 setContent: function(str)
17440 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17442 // as it get's added to the bottom of the page.
17443 onRender : function(ct, position)
17445 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17447 var cfg = Roo.apply({}, this.getAutoCreate());
17451 cfg.cls += ' ' + this.cls;
17454 cfg.style = this.style;
17456 //Roo.log("adding to ");
17457 this.el = Roo.get(document.body).createChild(cfg, position);
17458 // Roo.log(this.el);
17463 initEvents : function()
17465 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17466 this.el.enableDisplayMode('block');
17468 if (this.over === false) {
17471 if (this.triggers === false) {
17474 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17475 var triggers = this.trigger ? this.trigger.split(' ') : [];
17476 Roo.each(triggers, function(trigger) {
17478 if (trigger == 'click') {
17479 on_el.on('click', this.toggle, this);
17480 } else if (trigger != 'manual') {
17481 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17482 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17484 on_el.on(eventIn ,this.enter, this);
17485 on_el.on(eventOut, this.leave, this);
17496 toggle : function () {
17497 this.hoverState == 'in' ? this.leave() : this.enter();
17500 enter : function () {
17502 clearTimeout(this.timeout);
17504 this.hoverState = 'in';
17506 if (!this.delay || !this.delay.show) {
17511 this.timeout = setTimeout(function () {
17512 if (_t.hoverState == 'in') {
17515 }, this.delay.show)
17518 leave : function() {
17519 clearTimeout(this.timeout);
17521 this.hoverState = 'out';
17523 if (!this.delay || !this.delay.hide) {
17528 this.timeout = setTimeout(function () {
17529 if (_t.hoverState == 'out') {
17532 }, this.delay.hide)
17535 show : function (on_el)
17538 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17542 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17543 if (this.html !== false) {
17544 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17546 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17547 if (!this.title.length) {
17548 this.el.select('.popover-title',true).hide();
17551 var placement = typeof this.placement == 'function' ?
17552 this.placement.call(this, this.el, on_el) :
17555 var autoToken = /\s?auto?\s?/i;
17556 var autoPlace = autoToken.test(placement);
17558 placement = placement.replace(autoToken, '') || 'top';
17562 //this.el.setXY([0,0]);
17564 this.el.dom.style.display='block';
17565 this.el.addClass(placement);
17567 //this.el.appendTo(on_el);
17569 var p = this.getPosition();
17570 var box = this.el.getBox();
17575 var align = Roo.bootstrap.Popover.alignment[placement];
17578 this.el.alignTo(on_el, align[0],align[1]);
17579 //var arrow = this.el.select('.arrow',true).first();
17580 //arrow.set(align[2],
17582 this.el.addClass('in');
17585 if (this.el.hasClass('fade')) {
17589 this.hoverState = 'in';
17591 this.fireEvent('show', this);
17596 this.el.setXY([0,0]);
17597 this.el.removeClass('in');
17599 this.hoverState = null;
17601 this.fireEvent('hide', this);
17606 Roo.bootstrap.Popover.alignment = {
17607 'left' : ['r-l', [-10,0], 'right'],
17608 'right' : ['l-r', [10,0], 'left'],
17609 'bottom' : ['t-b', [0,10], 'top'],
17610 'top' : [ 'b-t', [0,-10], 'bottom']
17621 * @class Roo.bootstrap.Progress
17622 * @extends Roo.bootstrap.Component
17623 * Bootstrap Progress class
17624 * @cfg {Boolean} striped striped of the progress bar
17625 * @cfg {Boolean} active animated of the progress bar
17629 * Create a new Progress
17630 * @param {Object} config The config object
17633 Roo.bootstrap.Progress = function(config){
17634 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17637 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17642 getAutoCreate : function(){
17650 cfg.cls += ' progress-striped';
17654 cfg.cls += ' active';
17673 * @class Roo.bootstrap.ProgressBar
17674 * @extends Roo.bootstrap.Component
17675 * Bootstrap ProgressBar class
17676 * @cfg {Number} aria_valuenow aria-value now
17677 * @cfg {Number} aria_valuemin aria-value min
17678 * @cfg {Number} aria_valuemax aria-value max
17679 * @cfg {String} label label for the progress bar
17680 * @cfg {String} panel (success | info | warning | danger )
17681 * @cfg {String} role role of the progress bar
17682 * @cfg {String} sr_only text
17686 * Create a new ProgressBar
17687 * @param {Object} config The config object
17690 Roo.bootstrap.ProgressBar = function(config){
17691 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17694 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17698 aria_valuemax : 100,
17704 getAutoCreate : function()
17709 cls: 'progress-bar',
17710 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17722 cfg.role = this.role;
17725 if(this.aria_valuenow){
17726 cfg['aria-valuenow'] = this.aria_valuenow;
17729 if(this.aria_valuemin){
17730 cfg['aria-valuemin'] = this.aria_valuemin;
17733 if(this.aria_valuemax){
17734 cfg['aria-valuemax'] = this.aria_valuemax;
17737 if(this.label && !this.sr_only){
17738 cfg.html = this.label;
17742 cfg.cls += ' progress-bar-' + this.panel;
17748 update : function(aria_valuenow)
17750 this.aria_valuenow = aria_valuenow;
17752 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17767 * @class Roo.bootstrap.TabGroup
17768 * @extends Roo.bootstrap.Column
17769 * Bootstrap Column class
17770 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17771 * @cfg {Boolean} carousel true to make the group behave like a carousel
17772 * @cfg {Boolean} bullets show bullets for the panels
17773 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17774 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17775 * @cfg {Boolean} showarrow (true|false) show arrow default true
17778 * Create a new TabGroup
17779 * @param {Object} config The config object
17782 Roo.bootstrap.TabGroup = function(config){
17783 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17785 this.navId = Roo.id();
17788 Roo.bootstrap.TabGroup.register(this);
17792 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17795 transition : false,
17800 slideOnTouch : false,
17803 getAutoCreate : function()
17805 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17807 cfg.cls += ' tab-content';
17809 if (this.carousel) {
17810 cfg.cls += ' carousel slide';
17813 cls : 'carousel-inner',
17817 if(this.bullets && !Roo.isTouch){
17820 cls : 'carousel-bullets',
17824 if(this.bullets_cls){
17825 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17832 cfg.cn[0].cn.push(bullets);
17835 if(this.showarrow){
17836 cfg.cn[0].cn.push({
17838 class : 'carousel-arrow',
17842 class : 'carousel-prev',
17846 class : 'fa fa-chevron-left'
17852 class : 'carousel-next',
17856 class : 'fa fa-chevron-right'
17869 initEvents: function()
17871 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17872 // this.el.on("touchstart", this.onTouchStart, this);
17875 if(this.autoslide){
17878 this.slideFn = window.setInterval(function() {
17879 _this.showPanelNext();
17883 if(this.showarrow){
17884 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17885 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17891 // onTouchStart : function(e, el, o)
17893 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17897 // this.showPanelNext();
17901 getChildContainer : function()
17903 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17907 * register a Navigation item
17908 * @param {Roo.bootstrap.NavItem} the navitem to add
17910 register : function(item)
17912 this.tabs.push( item);
17913 item.navId = this.navId; // not really needed..
17918 getActivePanel : function()
17921 Roo.each(this.tabs, function(t) {
17931 getPanelByName : function(n)
17934 Roo.each(this.tabs, function(t) {
17935 if (t.tabId == n) {
17943 indexOfPanel : function(p)
17946 Roo.each(this.tabs, function(t,i) {
17947 if (t.tabId == p.tabId) {
17956 * show a specific panel
17957 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17958 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17960 showPanel : function (pan)
17962 if(this.transition || typeof(pan) == 'undefined'){
17963 Roo.log("waiting for the transitionend");
17967 if (typeof(pan) == 'number') {
17968 pan = this.tabs[pan];
17971 if (typeof(pan) == 'string') {
17972 pan = this.getPanelByName(pan);
17975 var cur = this.getActivePanel();
17978 Roo.log('pan or acitve pan is undefined');
17982 if (pan.tabId == this.getActivePanel().tabId) {
17986 if (false === cur.fireEvent('beforedeactivate')) {
17990 if(this.bullets > 0 && !Roo.isTouch){
17991 this.setActiveBullet(this.indexOfPanel(pan));
17994 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17996 this.transition = true;
17997 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17998 var lr = dir == 'next' ? 'left' : 'right';
17999 pan.el.addClass(dir); // or prev
18000 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18001 cur.el.addClass(lr); // or right
18002 pan.el.addClass(lr);
18005 cur.el.on('transitionend', function() {
18006 Roo.log("trans end?");
18008 pan.el.removeClass([lr,dir]);
18009 pan.setActive(true);
18011 cur.el.removeClass([lr]);
18012 cur.setActive(false);
18014 _this.transition = false;
18016 }, this, { single: true } );
18021 cur.setActive(false);
18022 pan.setActive(true);
18027 showPanelNext : function()
18029 var i = this.indexOfPanel(this.getActivePanel());
18031 if (i >= this.tabs.length - 1 && !this.autoslide) {
18035 if (i >= this.tabs.length - 1 && this.autoslide) {
18039 this.showPanel(this.tabs[i+1]);
18042 showPanelPrev : function()
18044 var i = this.indexOfPanel(this.getActivePanel());
18046 if (i < 1 && !this.autoslide) {
18050 if (i < 1 && this.autoslide) {
18051 i = this.tabs.length;
18054 this.showPanel(this.tabs[i-1]);
18058 addBullet: function()
18060 if(!this.bullets || Roo.isTouch){
18063 var ctr = this.el.select('.carousel-bullets',true).first();
18064 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18065 var bullet = ctr.createChild({
18066 cls : 'bullet bullet-' + i
18067 },ctr.dom.lastChild);
18072 bullet.on('click', (function(e, el, o, ii, t){
18074 e.preventDefault();
18076 this.showPanel(ii);
18078 if(this.autoslide && this.slideFn){
18079 clearInterval(this.slideFn);
18080 this.slideFn = window.setInterval(function() {
18081 _this.showPanelNext();
18085 }).createDelegate(this, [i, bullet], true));
18090 setActiveBullet : function(i)
18096 Roo.each(this.el.select('.bullet', true).elements, function(el){
18097 el.removeClass('selected');
18100 var bullet = this.el.select('.bullet-' + i, true).first();
18106 bullet.addClass('selected');
18117 Roo.apply(Roo.bootstrap.TabGroup, {
18121 * register a Navigation Group
18122 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18124 register : function(navgrp)
18126 this.groups[navgrp.navId] = navgrp;
18130 * fetch a Navigation Group based on the navigation ID
18131 * if one does not exist , it will get created.
18132 * @param {string} the navgroup to add
18133 * @returns {Roo.bootstrap.NavGroup} the navgroup
18135 get: function(navId) {
18136 if (typeof(this.groups[navId]) == 'undefined') {
18137 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18139 return this.groups[navId] ;
18154 * @class Roo.bootstrap.TabPanel
18155 * @extends Roo.bootstrap.Component
18156 * Bootstrap TabPanel class
18157 * @cfg {Boolean} active panel active
18158 * @cfg {String} html panel content
18159 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18160 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18161 * @cfg {String} href click to link..
18165 * Create a new TabPanel
18166 * @param {Object} config The config object
18169 Roo.bootstrap.TabPanel = function(config){
18170 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18174 * Fires when the active status changes
18175 * @param {Roo.bootstrap.TabPanel} this
18176 * @param {Boolean} state the new state
18181 * @event beforedeactivate
18182 * Fires before a tab is de-activated - can be used to do validation on a form.
18183 * @param {Roo.bootstrap.TabPanel} this
18184 * @return {Boolean} false if there is an error
18187 'beforedeactivate': true
18190 this.tabId = this.tabId || Roo.id();
18194 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18202 getAutoCreate : function(){
18205 // item is needed for carousel - not sure if it has any effect otherwise
18206 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18207 html: this.html || ''
18211 cfg.cls += ' active';
18215 cfg.tabId = this.tabId;
18222 initEvents: function()
18224 var p = this.parent();
18226 this.navId = this.navId || p.navId;
18228 if (typeof(this.navId) != 'undefined') {
18229 // not really needed.. but just in case.. parent should be a NavGroup.
18230 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18234 var i = tg.tabs.length - 1;
18236 if(this.active && tg.bullets > 0 && i < tg.bullets){
18237 tg.setActiveBullet(i);
18241 this.el.on('click', this.onClick, this);
18244 this.el.on("touchstart", this.onTouchStart, this);
18245 this.el.on("touchmove", this.onTouchMove, this);
18246 this.el.on("touchend", this.onTouchEnd, this);
18251 onRender : function(ct, position)
18253 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18256 setActive : function(state)
18258 Roo.log("panel - set active " + this.tabId + "=" + state);
18260 this.active = state;
18262 this.el.removeClass('active');
18264 } else if (!this.el.hasClass('active')) {
18265 this.el.addClass('active');
18268 this.fireEvent('changed', this, state);
18271 onClick : function(e)
18273 e.preventDefault();
18275 if(!this.href.length){
18279 window.location.href = this.href;
18288 onTouchStart : function(e)
18290 this.swiping = false;
18292 this.startX = e.browserEvent.touches[0].clientX;
18293 this.startY = e.browserEvent.touches[0].clientY;
18296 onTouchMove : function(e)
18298 this.swiping = true;
18300 this.endX = e.browserEvent.touches[0].clientX;
18301 this.endY = e.browserEvent.touches[0].clientY;
18304 onTouchEnd : function(e)
18311 var tabGroup = this.parent();
18313 if(this.endX > this.startX){ // swiping right
18314 tabGroup.showPanelPrev();
18318 if(this.startX > this.endX){ // swiping left
18319 tabGroup.showPanelNext();
18338 * @class Roo.bootstrap.DateField
18339 * @extends Roo.bootstrap.Input
18340 * Bootstrap DateField class
18341 * @cfg {Number} weekStart default 0
18342 * @cfg {String} viewMode default empty, (months|years)
18343 * @cfg {String} minViewMode default empty, (months|years)
18344 * @cfg {Number} startDate default -Infinity
18345 * @cfg {Number} endDate default Infinity
18346 * @cfg {Boolean} todayHighlight default false
18347 * @cfg {Boolean} todayBtn default false
18348 * @cfg {Boolean} calendarWeeks default false
18349 * @cfg {Object} daysOfWeekDisabled default empty
18350 * @cfg {Boolean} singleMode default false (true | false)
18352 * @cfg {Boolean} keyboardNavigation default true
18353 * @cfg {String} language default en
18356 * Create a new DateField
18357 * @param {Object} config The config object
18360 Roo.bootstrap.DateField = function(config){
18361 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18365 * Fires when this field show.
18366 * @param {Roo.bootstrap.DateField} this
18367 * @param {Mixed} date The date value
18372 * Fires when this field hide.
18373 * @param {Roo.bootstrap.DateField} this
18374 * @param {Mixed} date The date value
18379 * Fires when select a date.
18380 * @param {Roo.bootstrap.DateField} this
18381 * @param {Mixed} date The date value
18385 * @event beforeselect
18386 * Fires when before select a date.
18387 * @param {Roo.bootstrap.DateField} this
18388 * @param {Mixed} date The date value
18390 beforeselect : true
18394 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18397 * @cfg {String} format
18398 * The default date format string which can be overriden for localization support. The format must be
18399 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18403 * @cfg {String} altFormats
18404 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18405 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18407 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18415 todayHighlight : false,
18421 keyboardNavigation: true,
18423 calendarWeeks: false,
18425 startDate: -Infinity,
18429 daysOfWeekDisabled: [],
18433 singleMode : false,
18435 UTCDate: function()
18437 return new Date(Date.UTC.apply(Date, arguments));
18440 UTCToday: function()
18442 var today = new Date();
18443 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18446 getDate: function() {
18447 var d = this.getUTCDate();
18448 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18451 getUTCDate: function() {
18455 setDate: function(d) {
18456 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18459 setUTCDate: function(d) {
18461 this.setValue(this.formatDate(this.date));
18464 onRender: function(ct, position)
18467 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18469 this.language = this.language || 'en';
18470 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18471 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18473 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18474 this.format = this.format || 'm/d/y';
18475 this.isInline = false;
18476 this.isInput = true;
18477 this.component = this.el.select('.add-on', true).first() || false;
18478 this.component = (this.component && this.component.length === 0) ? false : this.component;
18479 this.hasInput = this.component && this.inputEl().length;
18481 if (typeof(this.minViewMode === 'string')) {
18482 switch (this.minViewMode) {
18484 this.minViewMode = 1;
18487 this.minViewMode = 2;
18490 this.minViewMode = 0;
18495 if (typeof(this.viewMode === 'string')) {
18496 switch (this.viewMode) {
18509 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18511 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18513 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18515 this.picker().on('mousedown', this.onMousedown, this);
18516 this.picker().on('click', this.onClick, this);
18518 this.picker().addClass('datepicker-dropdown');
18520 this.startViewMode = this.viewMode;
18522 if(this.singleMode){
18523 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18524 v.setVisibilityMode(Roo.Element.DISPLAY);
18528 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18529 v.setStyle('width', '189px');
18533 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18534 if(!this.calendarWeeks){
18539 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18540 v.attr('colspan', function(i, val){
18541 return parseInt(val) + 1;
18546 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18548 this.setStartDate(this.startDate);
18549 this.setEndDate(this.endDate);
18551 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18558 if(this.isInline) {
18563 picker : function()
18565 return this.pickerEl;
18566 // return this.el.select('.datepicker', true).first();
18569 fillDow: function()
18571 var dowCnt = this.weekStart;
18580 if(this.calendarWeeks){
18588 while (dowCnt < this.weekStart + 7) {
18592 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18596 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18599 fillMonths: function()
18602 var months = this.picker().select('>.datepicker-months td', true).first();
18604 months.dom.innerHTML = '';
18610 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18613 months.createChild(month);
18620 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;
18622 if (this.date < this.startDate) {
18623 this.viewDate = new Date(this.startDate);
18624 } else if (this.date > this.endDate) {
18625 this.viewDate = new Date(this.endDate);
18627 this.viewDate = new Date(this.date);
18635 var d = new Date(this.viewDate),
18636 year = d.getUTCFullYear(),
18637 month = d.getUTCMonth(),
18638 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18639 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18640 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18641 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18642 currentDate = this.date && this.date.valueOf(),
18643 today = this.UTCToday();
18645 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18647 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18649 // this.picker.select('>tfoot th.today').
18650 // .text(dates[this.language].today)
18651 // .toggle(this.todayBtn !== false);
18653 this.updateNavArrows();
18656 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18658 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18660 prevMonth.setUTCDate(day);
18662 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18664 var nextMonth = new Date(prevMonth);
18666 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18668 nextMonth = nextMonth.valueOf();
18670 var fillMonths = false;
18672 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18674 while(prevMonth.valueOf() <= nextMonth) {
18677 if (prevMonth.getUTCDay() === this.weekStart) {
18679 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18687 if(this.calendarWeeks){
18688 // ISO 8601: First week contains first thursday.
18689 // ISO also states week starts on Monday, but we can be more abstract here.
18691 // Start of current week: based on weekstart/current date
18692 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18693 // Thursday of this week
18694 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18695 // First Thursday of year, year from thursday
18696 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18697 // Calendar week: ms between thursdays, div ms per day, div 7 days
18698 calWeek = (th - yth) / 864e5 / 7 + 1;
18700 fillMonths.cn.push({
18708 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18710 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18713 if (this.todayHighlight &&
18714 prevMonth.getUTCFullYear() == today.getFullYear() &&
18715 prevMonth.getUTCMonth() == today.getMonth() &&
18716 prevMonth.getUTCDate() == today.getDate()) {
18717 clsName += ' today';
18720 if (currentDate && prevMonth.valueOf() === currentDate) {
18721 clsName += ' active';
18724 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18725 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18726 clsName += ' disabled';
18729 fillMonths.cn.push({
18731 cls: 'day ' + clsName,
18732 html: prevMonth.getDate()
18735 prevMonth.setDate(prevMonth.getDate()+1);
18738 var currentYear = this.date && this.date.getUTCFullYear();
18739 var currentMonth = this.date && this.date.getUTCMonth();
18741 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18743 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18744 v.removeClass('active');
18746 if(currentYear === year && k === currentMonth){
18747 v.addClass('active');
18750 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18751 v.addClass('disabled');
18757 year = parseInt(year/10, 10) * 10;
18759 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18761 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18764 for (var i = -1; i < 11; i++) {
18765 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18767 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18775 showMode: function(dir)
18778 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18781 Roo.each(this.picker().select('>div',true).elements, function(v){
18782 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18785 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18790 if(this.isInline) {
18794 this.picker().removeClass(['bottom', 'top']);
18796 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18798 * place to the top of element!
18802 this.picker().addClass('top');
18803 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18808 this.picker().addClass('bottom');
18810 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18813 parseDate : function(value)
18815 if(!value || value instanceof Date){
18818 var v = Date.parseDate(value, this.format);
18819 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18820 v = Date.parseDate(value, 'Y-m-d');
18822 if(!v && this.altFormats){
18823 if(!this.altFormatsArray){
18824 this.altFormatsArray = this.altFormats.split("|");
18826 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18827 v = Date.parseDate(value, this.altFormatsArray[i]);
18833 formatDate : function(date, fmt)
18835 return (!date || !(date instanceof Date)) ?
18836 date : date.dateFormat(fmt || this.format);
18839 onFocus : function()
18841 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18845 onBlur : function()
18847 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18849 var d = this.inputEl().getValue();
18858 this.picker().show();
18862 this.fireEvent('show', this, this.date);
18867 if(this.isInline) {
18870 this.picker().hide();
18871 this.viewMode = this.startViewMode;
18874 this.fireEvent('hide', this, this.date);
18878 onMousedown: function(e)
18880 e.stopPropagation();
18881 e.preventDefault();
18886 Roo.bootstrap.DateField.superclass.keyup.call(this);
18890 setValue: function(v)
18892 if(this.fireEvent('beforeselect', this, v) !== false){
18893 var d = new Date(this.parseDate(v) ).clearTime();
18895 if(isNaN(d.getTime())){
18896 this.date = this.viewDate = '';
18897 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18901 v = this.formatDate(d);
18903 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18905 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18909 this.fireEvent('select', this, this.date);
18913 getValue: function()
18915 return this.formatDate(this.date);
18918 fireKey: function(e)
18920 if (!this.picker().isVisible()){
18921 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18927 var dateChanged = false,
18929 newDate, newViewDate;
18934 e.preventDefault();
18938 if (!this.keyboardNavigation) {
18941 dir = e.keyCode == 37 ? -1 : 1;
18944 newDate = this.moveYear(this.date, dir);
18945 newViewDate = this.moveYear(this.viewDate, dir);
18946 } else if (e.shiftKey){
18947 newDate = this.moveMonth(this.date, dir);
18948 newViewDate = this.moveMonth(this.viewDate, dir);
18950 newDate = new Date(this.date);
18951 newDate.setUTCDate(this.date.getUTCDate() + dir);
18952 newViewDate = new Date(this.viewDate);
18953 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18955 if (this.dateWithinRange(newDate)){
18956 this.date = newDate;
18957 this.viewDate = newViewDate;
18958 this.setValue(this.formatDate(this.date));
18960 e.preventDefault();
18961 dateChanged = true;
18966 if (!this.keyboardNavigation) {
18969 dir = e.keyCode == 38 ? -1 : 1;
18971 newDate = this.moveYear(this.date, dir);
18972 newViewDate = this.moveYear(this.viewDate, dir);
18973 } else if (e.shiftKey){
18974 newDate = this.moveMonth(this.date, dir);
18975 newViewDate = this.moveMonth(this.viewDate, dir);
18977 newDate = new Date(this.date);
18978 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18979 newViewDate = new Date(this.viewDate);
18980 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18982 if (this.dateWithinRange(newDate)){
18983 this.date = newDate;
18984 this.viewDate = newViewDate;
18985 this.setValue(this.formatDate(this.date));
18987 e.preventDefault();
18988 dateChanged = true;
18992 this.setValue(this.formatDate(this.date));
18994 e.preventDefault();
18997 this.setValue(this.formatDate(this.date));
19011 onClick: function(e)
19013 e.stopPropagation();
19014 e.preventDefault();
19016 var target = e.getTarget();
19018 if(target.nodeName.toLowerCase() === 'i'){
19019 target = Roo.get(target).dom.parentNode;
19022 var nodeName = target.nodeName;
19023 var className = target.className;
19024 var html = target.innerHTML;
19025 //Roo.log(nodeName);
19027 switch(nodeName.toLowerCase()) {
19029 switch(className) {
19035 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19036 switch(this.viewMode){
19038 this.viewDate = this.moveMonth(this.viewDate, dir);
19042 this.viewDate = this.moveYear(this.viewDate, dir);
19048 var date = new Date();
19049 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19051 this.setValue(this.formatDate(this.date));
19058 if (className.indexOf('disabled') < 0) {
19059 this.viewDate.setUTCDate(1);
19060 if (className.indexOf('month') > -1) {
19061 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19063 var year = parseInt(html, 10) || 0;
19064 this.viewDate.setUTCFullYear(year);
19068 if(this.singleMode){
19069 this.setValue(this.formatDate(this.viewDate));
19080 //Roo.log(className);
19081 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19082 var day = parseInt(html, 10) || 1;
19083 var year = this.viewDate.getUTCFullYear(),
19084 month = this.viewDate.getUTCMonth();
19086 if (className.indexOf('old') > -1) {
19093 } else if (className.indexOf('new') > -1) {
19101 //Roo.log([year,month,day]);
19102 this.date = this.UTCDate(year, month, day,0,0,0,0);
19103 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19105 //Roo.log(this.formatDate(this.date));
19106 this.setValue(this.formatDate(this.date));
19113 setStartDate: function(startDate)
19115 this.startDate = startDate || -Infinity;
19116 if (this.startDate !== -Infinity) {
19117 this.startDate = this.parseDate(this.startDate);
19120 this.updateNavArrows();
19123 setEndDate: function(endDate)
19125 this.endDate = endDate || Infinity;
19126 if (this.endDate !== Infinity) {
19127 this.endDate = this.parseDate(this.endDate);
19130 this.updateNavArrows();
19133 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19135 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19136 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19137 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19139 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19140 return parseInt(d, 10);
19143 this.updateNavArrows();
19146 updateNavArrows: function()
19148 if(this.singleMode){
19152 var d = new Date(this.viewDate),
19153 year = d.getUTCFullYear(),
19154 month = d.getUTCMonth();
19156 Roo.each(this.picker().select('.prev', true).elements, function(v){
19158 switch (this.viewMode) {
19161 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19167 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19174 Roo.each(this.picker().select('.next', true).elements, function(v){
19176 switch (this.viewMode) {
19179 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19185 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19193 moveMonth: function(date, dir)
19198 var new_date = new Date(date.valueOf()),
19199 day = new_date.getUTCDate(),
19200 month = new_date.getUTCMonth(),
19201 mag = Math.abs(dir),
19203 dir = dir > 0 ? 1 : -1;
19206 // If going back one month, make sure month is not current month
19207 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19209 return new_date.getUTCMonth() == month;
19211 // If going forward one month, make sure month is as expected
19212 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19214 return new_date.getUTCMonth() != new_month;
19216 new_month = month + dir;
19217 new_date.setUTCMonth(new_month);
19218 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19219 if (new_month < 0 || new_month > 11) {
19220 new_month = (new_month + 12) % 12;
19223 // For magnitudes >1, move one month at a time...
19224 for (var i=0; i<mag; i++) {
19225 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19226 new_date = this.moveMonth(new_date, dir);
19228 // ...then reset the day, keeping it in the new month
19229 new_month = new_date.getUTCMonth();
19230 new_date.setUTCDate(day);
19232 return new_month != new_date.getUTCMonth();
19235 // Common date-resetting loop -- if date is beyond end of month, make it
19238 new_date.setUTCDate(--day);
19239 new_date.setUTCMonth(new_month);
19244 moveYear: function(date, dir)
19246 return this.moveMonth(date, dir*12);
19249 dateWithinRange: function(date)
19251 return date >= this.startDate && date <= this.endDate;
19257 this.picker().remove();
19260 validateValue : function(value)
19262 if(this.getVisibilityEl().hasClass('hidden')){
19266 if(value.length < 1) {
19267 if(this.allowBlank){
19273 if(value.length < this.minLength){
19276 if(value.length > this.maxLength){
19280 var vt = Roo.form.VTypes;
19281 if(!vt[this.vtype](value, this)){
19285 if(typeof this.validator == "function"){
19286 var msg = this.validator(value);
19292 if(this.regex && !this.regex.test(value)){
19296 if(typeof(this.parseDate(value)) == 'undefined'){
19300 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19304 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19314 this.date = this.viewDate = '';
19316 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19319 setVisible : function(visible)
19325 this.getEl().removeClass('hidden');
19328 this.fireEvent('show', this);
19332 this.getEl().addClass('hidden');
19333 this.fireEvent('hide', this);
19338 Roo.apply(Roo.bootstrap.DateField, {
19349 html: '<i class="fa fa-arrow-left"/>'
19359 html: '<i class="fa fa-arrow-right"/>'
19401 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19402 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19403 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19404 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19405 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19418 navFnc: 'FullYear',
19423 navFnc: 'FullYear',
19428 Roo.apply(Roo.bootstrap.DateField, {
19432 cls: 'datepicker dropdown-menu roo-dynamic',
19436 cls: 'datepicker-days',
19440 cls: 'table-condensed',
19442 Roo.bootstrap.DateField.head,
19446 Roo.bootstrap.DateField.footer
19453 cls: 'datepicker-months',
19457 cls: 'table-condensed',
19459 Roo.bootstrap.DateField.head,
19460 Roo.bootstrap.DateField.content,
19461 Roo.bootstrap.DateField.footer
19468 cls: 'datepicker-years',
19472 cls: 'table-condensed',
19474 Roo.bootstrap.DateField.head,
19475 Roo.bootstrap.DateField.content,
19476 Roo.bootstrap.DateField.footer
19495 * @class Roo.bootstrap.TimeField
19496 * @extends Roo.bootstrap.Input
19497 * Bootstrap DateField class
19501 * Create a new TimeField
19502 * @param {Object} config The config object
19505 Roo.bootstrap.TimeField = function(config){
19506 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19510 * Fires when this field show.
19511 * @param {Roo.bootstrap.DateField} thisthis
19512 * @param {Mixed} date The date value
19517 * Fires when this field hide.
19518 * @param {Roo.bootstrap.DateField} this
19519 * @param {Mixed} date The date value
19524 * Fires when select a date.
19525 * @param {Roo.bootstrap.DateField} this
19526 * @param {Mixed} date The date value
19532 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
19535 * @cfg {String} format
19536 * The default time format string which can be overriden for localization support. The format must be
19537 * valid according to {@link Date#parseDate} (defaults to 'H:i').
19541 onRender: function(ct, position)
19544 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19546 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19548 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19550 this.pop = this.picker().select('>.datepicker-time',true).first();
19551 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19553 this.picker().on('mousedown', this.onMousedown, this);
19554 this.picker().on('click', this.onClick, this);
19556 this.picker().addClass('datepicker-dropdown');
19561 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19562 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19563 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19564 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19565 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19566 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19570 fireKey: function(e){
19571 if (!this.picker().isVisible()){
19572 if (e.keyCode == 27) { // allow escape to hide and re-show picker
19578 e.preventDefault();
19586 this.onTogglePeriod();
19589 this.onIncrementMinutes();
19592 this.onDecrementMinutes();
19601 onClick: function(e) {
19602 e.stopPropagation();
19603 e.preventDefault();
19606 picker : function()
19608 return this.el.select('.datepicker', true).first();
19611 fillTime: function()
19613 var time = this.pop.select('tbody', true).first();
19615 time.dom.innerHTML = '';
19630 cls: 'hours-up glyphicon glyphicon-chevron-up'
19650 cls: 'minutes-up glyphicon glyphicon-chevron-up'
19671 cls: 'timepicker-hour',
19686 cls: 'timepicker-minute',
19701 cls: 'btn btn-primary period',
19723 cls: 'hours-down glyphicon glyphicon-chevron-down'
19743 cls: 'minutes-down glyphicon glyphicon-chevron-down'
19761 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19768 var hours = this.time.getHours();
19769 var minutes = this.time.getMinutes();
19782 hours = hours - 12;
19786 hours = '0' + hours;
19790 minutes = '0' + minutes;
19793 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19794 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19795 this.pop.select('button', true).first().dom.innerHTML = period;
19801 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19803 var cls = ['bottom'];
19805 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19812 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19817 this.picker().addClass(cls.join('-'));
19821 Roo.each(cls, function(c){
19823 _this.picker().setTop(_this.inputEl().getHeight());
19827 _this.picker().setTop(0 - _this.picker().getHeight());
19832 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19836 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19843 onFocus : function()
19845 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19849 onBlur : function()
19851 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19857 this.picker().show();
19862 this.fireEvent('show', this, this.date);
19867 this.picker().hide();
19870 this.fireEvent('hide', this, this.date);
19873 setTime : function()
19876 this.setValue(this.time.format(this.format));
19878 this.fireEvent('select', this, this.date);
19883 onMousedown: function(e){
19884 e.stopPropagation();
19885 e.preventDefault();
19888 onIncrementHours: function()
19890 Roo.log('onIncrementHours');
19891 this.time = this.time.add(Date.HOUR, 1);
19896 onDecrementHours: function()
19898 Roo.log('onDecrementHours');
19899 this.time = this.time.add(Date.HOUR, -1);
19903 onIncrementMinutes: function()
19905 Roo.log('onIncrementMinutes');
19906 this.time = this.time.add(Date.MINUTE, 1);
19910 onDecrementMinutes: function()
19912 Roo.log('onDecrementMinutes');
19913 this.time = this.time.add(Date.MINUTE, -1);
19917 onTogglePeriod: function()
19919 Roo.log('onTogglePeriod');
19920 this.time = this.time.add(Date.HOUR, 12);
19927 Roo.apply(Roo.bootstrap.TimeField, {
19957 cls: 'btn btn-info ok',
19969 Roo.apply(Roo.bootstrap.TimeField, {
19973 cls: 'datepicker dropdown-menu',
19977 cls: 'datepicker-time',
19981 cls: 'table-condensed',
19983 Roo.bootstrap.TimeField.content,
19984 Roo.bootstrap.TimeField.footer
20003 * @class Roo.bootstrap.MonthField
20004 * @extends Roo.bootstrap.Input
20005 * Bootstrap MonthField class
20007 * @cfg {String} language default en
20010 * Create a new MonthField
20011 * @param {Object} config The config object
20014 Roo.bootstrap.MonthField = function(config){
20015 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20020 * Fires when this field show.
20021 * @param {Roo.bootstrap.MonthField} this
20022 * @param {Mixed} date The date value
20027 * Fires when this field hide.
20028 * @param {Roo.bootstrap.MonthField} this
20029 * @param {Mixed} date The date value
20034 * Fires when select a date.
20035 * @param {Roo.bootstrap.MonthField} this
20036 * @param {String} oldvalue The old value
20037 * @param {String} newvalue The new value
20043 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
20045 onRender: function(ct, position)
20048 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20050 this.language = this.language || 'en';
20051 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20052 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20054 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20055 this.isInline = false;
20056 this.isInput = true;
20057 this.component = this.el.select('.add-on', true).first() || false;
20058 this.component = (this.component && this.component.length === 0) ? false : this.component;
20059 this.hasInput = this.component && this.inputEL().length;
20061 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20063 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20065 this.picker().on('mousedown', this.onMousedown, this);
20066 this.picker().on('click', this.onClick, this);
20068 this.picker().addClass('datepicker-dropdown');
20070 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20071 v.setStyle('width', '189px');
20078 if(this.isInline) {
20084 setValue: function(v, suppressEvent)
20086 var o = this.getValue();
20088 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20092 if(suppressEvent !== true){
20093 this.fireEvent('select', this, o, v);
20098 getValue: function()
20103 onClick: function(e)
20105 e.stopPropagation();
20106 e.preventDefault();
20108 var target = e.getTarget();
20110 if(target.nodeName.toLowerCase() === 'i'){
20111 target = Roo.get(target).dom.parentNode;
20114 var nodeName = target.nodeName;
20115 var className = target.className;
20116 var html = target.innerHTML;
20118 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20122 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20124 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20130 picker : function()
20132 return this.pickerEl;
20135 fillMonths: function()
20138 var months = this.picker().select('>.datepicker-months td', true).first();
20140 months.dom.innerHTML = '';
20146 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20149 months.createChild(month);
20158 if(typeof(this.vIndex) == 'undefined' && this.value.length){
20159 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20162 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20163 e.removeClass('active');
20165 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20166 e.addClass('active');
20173 if(this.isInline) {
20177 this.picker().removeClass(['bottom', 'top']);
20179 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20181 * place to the top of element!
20185 this.picker().addClass('top');
20186 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20191 this.picker().addClass('bottom');
20193 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20196 onFocus : function()
20198 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20202 onBlur : function()
20204 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20206 var d = this.inputEl().getValue();
20215 this.picker().show();
20216 this.picker().select('>.datepicker-months', true).first().show();
20220 this.fireEvent('show', this, this.date);
20225 if(this.isInline) {
20228 this.picker().hide();
20229 this.fireEvent('hide', this, this.date);
20233 onMousedown: function(e)
20235 e.stopPropagation();
20236 e.preventDefault();
20241 Roo.bootstrap.MonthField.superclass.keyup.call(this);
20245 fireKey: function(e)
20247 if (!this.picker().isVisible()){
20248 if (e.keyCode == 27) {// allow escape to hide and re-show picker
20259 e.preventDefault();
20263 dir = e.keyCode == 37 ? -1 : 1;
20265 this.vIndex = this.vIndex + dir;
20267 if(this.vIndex < 0){
20271 if(this.vIndex > 11){
20275 if(isNaN(this.vIndex)){
20279 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20285 dir = e.keyCode == 38 ? -1 : 1;
20287 this.vIndex = this.vIndex + dir * 4;
20289 if(this.vIndex < 0){
20293 if(this.vIndex > 11){
20297 if(isNaN(this.vIndex)){
20301 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20306 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20307 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20311 e.preventDefault();
20314 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20315 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20331 this.picker().remove();
20336 Roo.apply(Roo.bootstrap.MonthField, {
20355 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20356 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20361 Roo.apply(Roo.bootstrap.MonthField, {
20365 cls: 'datepicker dropdown-menu roo-dynamic',
20369 cls: 'datepicker-months',
20373 cls: 'table-condensed',
20375 Roo.bootstrap.DateField.content
20395 * @class Roo.bootstrap.CheckBox
20396 * @extends Roo.bootstrap.Input
20397 * Bootstrap CheckBox class
20399 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20400 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20401 * @cfg {String} boxLabel The text that appears beside the checkbox
20402 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20403 * @cfg {Boolean} checked initnal the element
20404 * @cfg {Boolean} inline inline the element (default false)
20405 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20406 * @cfg {String} tooltip label tooltip
20409 * Create a new CheckBox
20410 * @param {Object} config The config object
20413 Roo.bootstrap.CheckBox = function(config){
20414 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20419 * Fires when the element is checked or unchecked.
20420 * @param {Roo.bootstrap.CheckBox} this This input
20421 * @param {Boolean} checked The new checked value
20426 * Fires when the element is click.
20427 * @param {Roo.bootstrap.CheckBox} this This input
20434 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
20436 inputType: 'checkbox',
20445 getAutoCreate : function()
20447 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20453 cfg.cls = 'form-group ' + this.inputType; //input-group
20456 cfg.cls += ' ' + this.inputType + '-inline';
20462 type : this.inputType,
20463 value : this.inputValue,
20464 cls : 'roo-' + this.inputType, //'form-box',
20465 placeholder : this.placeholder || ''
20469 if(this.inputType != 'radio'){
20473 cls : 'roo-hidden-value',
20474 value : this.checked ? this.inputValue : this.valueOff
20479 if (this.weight) { // Validity check?
20480 cfg.cls += " " + this.inputType + "-" + this.weight;
20483 if (this.disabled) {
20484 input.disabled=true;
20488 input.checked = this.checked;
20493 input.name = this.name;
20495 if(this.inputType != 'radio'){
20496 hidden.name = this.name;
20497 input.name = '_hidden_' + this.name;
20502 input.cls += ' input-' + this.size;
20507 ['xs','sm','md','lg'].map(function(size){
20508 if (settings[size]) {
20509 cfg.cls += ' col-' + size + '-' + settings[size];
20513 var inputblock = input;
20515 if (this.before || this.after) {
20518 cls : 'input-group',
20523 inputblock.cn.push({
20525 cls : 'input-group-addon',
20530 inputblock.cn.push(input);
20532 if(this.inputType != 'radio'){
20533 inputblock.cn.push(hidden);
20537 inputblock.cn.push({
20539 cls : 'input-group-addon',
20546 if (align ==='left' && this.fieldLabel.length) {
20547 // Roo.log("left and has label");
20552 cls : 'control-label',
20553 html : this.fieldLabel
20563 if(this.labelWidth > 12){
20564 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20567 if(this.labelWidth < 13 && this.labelmd == 0){
20568 this.labelmd = this.labelWidth;
20571 if(this.labellg > 0){
20572 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20573 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20576 if(this.labelmd > 0){
20577 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20578 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20581 if(this.labelsm > 0){
20582 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20583 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20586 if(this.labelxs > 0){
20587 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20588 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20591 } else if ( this.fieldLabel.length) {
20592 // Roo.log(" label");
20596 tag: this.boxLabel ? 'span' : 'label',
20598 cls: 'control-label box-input-label',
20599 //cls : 'input-group-addon',
20600 html : this.fieldLabel
20609 // Roo.log(" no label && no align");
20610 cfg.cn = [ inputblock ] ;
20616 var boxLabelCfg = {
20618 //'for': id, // box label is handled by onclick - so no for...
20620 html: this.boxLabel
20624 boxLabelCfg.tooltip = this.tooltip;
20627 cfg.cn.push(boxLabelCfg);
20630 if(this.inputType != 'radio'){
20631 cfg.cn.push(hidden);
20639 * return the real input element.
20641 inputEl: function ()
20643 return this.el.select('input.roo-' + this.inputType,true).first();
20645 hiddenEl: function ()
20647 return this.el.select('input.roo-hidden-value',true).first();
20650 labelEl: function()
20652 return this.el.select('label.control-label',true).first();
20654 /* depricated... */
20658 return this.labelEl();
20661 boxLabelEl: function()
20663 return this.el.select('label.box-label',true).first();
20666 initEvents : function()
20668 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20670 this.inputEl().on('click', this.onClick, this);
20672 if (this.boxLabel) {
20673 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
20676 this.startValue = this.getValue();
20679 Roo.bootstrap.CheckBox.register(this);
20683 onClick : function(e)
20685 if(this.fireEvent('click', this, e) !== false){
20686 this.setChecked(!this.checked);
20691 setChecked : function(state,suppressEvent)
20693 this.startValue = this.getValue();
20695 if(this.inputType == 'radio'){
20697 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20698 e.dom.checked = false;
20701 this.inputEl().dom.checked = true;
20703 this.inputEl().dom.value = this.inputValue;
20705 if(suppressEvent !== true){
20706 this.fireEvent('check', this, true);
20714 this.checked = state;
20716 this.inputEl().dom.checked = state;
20719 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20721 if(suppressEvent !== true){
20722 this.fireEvent('check', this, state);
20728 getValue : function()
20730 if(this.inputType == 'radio'){
20731 return this.getGroupValue();
20734 return this.hiddenEl().dom.value;
20738 getGroupValue : function()
20740 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20744 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20747 setValue : function(v,suppressEvent)
20749 if(this.inputType == 'radio'){
20750 this.setGroupValue(v, suppressEvent);
20754 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20759 setGroupValue : function(v, suppressEvent)
20761 this.startValue = this.getValue();
20763 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20764 e.dom.checked = false;
20766 if(e.dom.value == v){
20767 e.dom.checked = true;
20771 if(suppressEvent !== true){
20772 this.fireEvent('check', this, true);
20780 validate : function()
20782 if(this.getVisibilityEl().hasClass('hidden')){
20788 (this.inputType == 'radio' && this.validateRadio()) ||
20789 (this.inputType == 'checkbox' && this.validateCheckbox())
20795 this.markInvalid();
20799 validateRadio : function()
20801 if(this.getVisibilityEl().hasClass('hidden')){
20805 if(this.allowBlank){
20811 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20812 if(!e.dom.checked){
20824 validateCheckbox : function()
20827 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20828 //return (this.getValue() == this.inputValue) ? true : false;
20831 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20839 for(var i in group){
20840 if(group[i].el.isVisible(true)){
20848 for(var i in group){
20853 r = (group[i].getValue() == group[i].inputValue) ? true : false;
20860 * Mark this field as valid
20862 markValid : function()
20866 this.fireEvent('valid', this);
20868 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20871 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20878 if(this.inputType == 'radio'){
20879 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20880 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20881 e.findParent('.form-group', false, true).addClass(_this.validClass);
20888 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20889 this.el.findParent('.form-group', false, true).addClass(this.validClass);
20893 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20899 for(var i in group){
20900 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20901 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20906 * Mark this field as invalid
20907 * @param {String} msg The validation message
20909 markInvalid : function(msg)
20911 if(this.allowBlank){
20917 this.fireEvent('invalid', this, msg);
20919 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20922 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20926 label.markInvalid();
20929 if(this.inputType == 'radio'){
20930 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20931 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20932 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20939 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20940 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20944 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20950 for(var i in group){
20951 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20952 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20957 clearInvalid : function()
20959 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20961 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20963 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20965 if (label && label.iconEl) {
20966 label.iconEl.removeClass(label.validClass);
20967 label.iconEl.removeClass(label.invalidClass);
20971 disable : function()
20973 if(this.inputType != 'radio'){
20974 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20981 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20982 _this.getActionEl().addClass(this.disabledClass);
20983 e.dom.disabled = true;
20987 this.disabled = true;
20988 this.fireEvent("disable", this);
20992 enable : function()
20994 if(this.inputType != 'radio'){
20995 Roo.bootstrap.CheckBox.superclass.enable.call(this);
21002 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21003 _this.getActionEl().removeClass(this.disabledClass);
21004 e.dom.disabled = false;
21008 this.disabled = false;
21009 this.fireEvent("enable", this);
21013 setBoxLabel : function(v)
21018 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21024 Roo.apply(Roo.bootstrap.CheckBox, {
21029 * register a CheckBox Group
21030 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21032 register : function(checkbox)
21034 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21035 this.groups[checkbox.groupId] = {};
21038 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21042 this.groups[checkbox.groupId][checkbox.name] = checkbox;
21046 * fetch a CheckBox Group based on the group ID
21047 * @param {string} the group ID
21048 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21050 get: function(groupId) {
21051 if (typeof(this.groups[groupId]) == 'undefined') {
21055 return this.groups[groupId] ;
21068 * @class Roo.bootstrap.Radio
21069 * @extends Roo.bootstrap.Component
21070 * Bootstrap Radio class
21071 * @cfg {String} boxLabel - the label associated
21072 * @cfg {String} value - the value of radio
21075 * Create a new Radio
21076 * @param {Object} config The config object
21078 Roo.bootstrap.Radio = function(config){
21079 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21083 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21089 getAutoCreate : function()
21093 cls : 'form-group radio',
21098 html : this.boxLabel
21106 initEvents : function()
21108 this.parent().register(this);
21110 this.el.on('click', this.onClick, this);
21114 onClick : function(e)
21116 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21117 this.setChecked(true);
21121 setChecked : function(state, suppressEvent)
21123 this.parent().setValue(this.value, suppressEvent);
21127 setBoxLabel : function(v)
21132 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21147 * @class Roo.bootstrap.SecurePass
21148 * @extends Roo.bootstrap.Input
21149 * Bootstrap SecurePass class
21153 * Create a new SecurePass
21154 * @param {Object} config The config object
21157 Roo.bootstrap.SecurePass = function (config) {
21158 // these go here, so the translation tool can replace them..
21160 PwdEmpty: "Please type a password, and then retype it to confirm.",
21161 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21162 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21163 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21164 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21165 FNInPwd: "Your password can't contain your first name. Please type a different password.",
21166 LNInPwd: "Your password can't contain your last name. Please type a different password.",
21167 TooWeak: "Your password is Too Weak."
21169 this.meterLabel = "Password strength:";
21170 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21171 this.meterClass = [
21172 "roo-password-meter-tooweak",
21173 "roo-password-meter-weak",
21174 "roo-password-meter-medium",
21175 "roo-password-meter-strong",
21176 "roo-password-meter-grey"
21181 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21184 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21186 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21188 * PwdEmpty: "Please type a password, and then retype it to confirm.",
21189 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21190 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21191 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21192 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21193 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
21194 * LNInPwd: "Your password can't contain your last name. Please type a different password."
21204 * @cfg {String/Object} Label for the strength meter (defaults to
21205 * 'Password strength:')
21210 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21211 * ['Weak', 'Medium', 'Strong'])
21214 pwdStrengths: false,
21227 initEvents: function ()
21229 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21231 if (this.el.is('input[type=password]') && Roo.isSafari) {
21232 this.el.on('keydown', this.SafariOnKeyDown, this);
21235 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21238 onRender: function (ct, position)
21240 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21241 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21242 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21244 this.trigger.createChild({
21249 cls: 'roo-password-meter-grey col-xs-12',
21252 //width: this.meterWidth + 'px'
21256 cls: 'roo-password-meter-text'
21262 if (this.hideTrigger) {
21263 this.trigger.setDisplayed(false);
21265 this.setSize(this.width || '', this.height || '');
21268 onDestroy: function ()
21270 if (this.trigger) {
21271 this.trigger.removeAllListeners();
21272 this.trigger.remove();
21275 this.wrap.remove();
21277 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21280 checkStrength: function ()
21282 var pwd = this.inputEl().getValue();
21283 if (pwd == this._lastPwd) {
21288 if (this.ClientSideStrongPassword(pwd)) {
21290 } else if (this.ClientSideMediumPassword(pwd)) {
21292 } else if (this.ClientSideWeakPassword(pwd)) {
21298 Roo.log('strength1: ' + strength);
21300 //var pm = this.trigger.child('div/div/div').dom;
21301 var pm = this.trigger.child('div/div');
21302 pm.removeClass(this.meterClass);
21303 pm.addClass(this.meterClass[strength]);
21306 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21308 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21310 this._lastPwd = pwd;
21314 Roo.bootstrap.SecurePass.superclass.reset.call(this);
21316 this._lastPwd = '';
21318 var pm = this.trigger.child('div/div');
21319 pm.removeClass(this.meterClass);
21320 pm.addClass('roo-password-meter-grey');
21323 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21326 this.inputEl().dom.type='password';
21329 validateValue: function (value)
21332 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21335 if (value.length == 0) {
21336 if (this.allowBlank) {
21337 this.clearInvalid();
21341 this.markInvalid(this.errors.PwdEmpty);
21342 this.errorMsg = this.errors.PwdEmpty;
21350 if ('[\x21-\x7e]*'.match(value)) {
21351 this.markInvalid(this.errors.PwdBadChar);
21352 this.errorMsg = this.errors.PwdBadChar;
21355 if (value.length < 6) {
21356 this.markInvalid(this.errors.PwdShort);
21357 this.errorMsg = this.errors.PwdShort;
21360 if (value.length > 16) {
21361 this.markInvalid(this.errors.PwdLong);
21362 this.errorMsg = this.errors.PwdLong;
21366 if (this.ClientSideStrongPassword(value)) {
21368 } else if (this.ClientSideMediumPassword(value)) {
21370 } else if (this.ClientSideWeakPassword(value)) {
21377 if (strength < 2) {
21378 //this.markInvalid(this.errors.TooWeak);
21379 this.errorMsg = this.errors.TooWeak;
21384 console.log('strength2: ' + strength);
21386 //var pm = this.trigger.child('div/div/div').dom;
21388 var pm = this.trigger.child('div/div');
21389 pm.removeClass(this.meterClass);
21390 pm.addClass(this.meterClass[strength]);
21392 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
21394 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
21396 this.errorMsg = '';
21400 CharacterSetChecks: function (type)
21403 this.fResult = false;
21406 isctype: function (character, type)
21409 case this.kCapitalLetter:
21410 if (character >= 'A' && character <= 'Z') {
21415 case this.kSmallLetter:
21416 if (character >= 'a' && character <= 'z') {
21422 if (character >= '0' && character <= '9') {
21427 case this.kPunctuation:
21428 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21439 IsLongEnough: function (pwd, size)
21441 return !(pwd == null || isNaN(size) || pwd.length < size);
21444 SpansEnoughCharacterSets: function (word, nb)
21446 if (!this.IsLongEnough(word, nb))
21451 var characterSetChecks = new Array(
21452 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21453 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21456 for (var index = 0; index < word.length; ++index) {
21457 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21458 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21459 characterSetChecks[nCharSet].fResult = true;
21466 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21467 if (characterSetChecks[nCharSet].fResult) {
21472 if (nCharSets < nb) {
21478 ClientSideStrongPassword: function (pwd)
21480 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21483 ClientSideMediumPassword: function (pwd)
21485 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21488 ClientSideWeakPassword: function (pwd)
21490 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21493 })//<script type="text/javascript">
21496 * Based Ext JS Library 1.1.1
21497 * Copyright(c) 2006-2007, Ext JS, LLC.
21503 * @class Roo.HtmlEditorCore
21504 * @extends Roo.Component
21505 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21507 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21510 Roo.HtmlEditorCore = function(config){
21513 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21518 * @event initialize
21519 * Fires when the editor is fully initialized (including the iframe)
21520 * @param {Roo.HtmlEditorCore} this
21525 * Fires when the editor is first receives the focus. Any insertion must wait
21526 * until after this event.
21527 * @param {Roo.HtmlEditorCore} this
21531 * @event beforesync
21532 * Fires before the textarea is updated with content from the editor iframe. Return false
21533 * to cancel the sync.
21534 * @param {Roo.HtmlEditorCore} this
21535 * @param {String} html
21539 * @event beforepush
21540 * Fires before the iframe editor is updated with content from the textarea. Return false
21541 * to cancel the push.
21542 * @param {Roo.HtmlEditorCore} this
21543 * @param {String} html
21548 * Fires when the textarea is updated with content from the editor iframe.
21549 * @param {Roo.HtmlEditorCore} this
21550 * @param {String} html
21555 * Fires when the iframe editor is updated with content from the textarea.
21556 * @param {Roo.HtmlEditorCore} this
21557 * @param {String} html
21562 * @event editorevent
21563 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21564 * @param {Roo.HtmlEditorCore} this
21570 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21572 // defaults : white / black...
21573 this.applyBlacklists();
21580 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
21584 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
21590 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
21595 * @cfg {Number} height (in pixels)
21599 * @cfg {Number} width (in pixels)
21604 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21607 stylesheets: false,
21612 // private properties
21613 validationEvent : false,
21615 initialized : false,
21617 sourceEditMode : false,
21618 onFocus : Roo.emptyFn,
21620 hideMode:'offsets',
21624 // blacklist + whitelisted elements..
21631 * Protected method that will not generally be called directly. It
21632 * is called when the editor initializes the iframe with HTML contents. Override this method if you
21633 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21635 getDocMarkup : function(){
21639 // inherit styels from page...??
21640 if (this.stylesheets === false) {
21642 Roo.get(document.head).select('style').each(function(node) {
21643 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21646 Roo.get(document.head).select('link').each(function(node) {
21647 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21650 } else if (!this.stylesheets.length) {
21652 st = '<style type="text/css">' +
21653 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21656 st = '<style type="text/css">' +
21661 st += '<style type="text/css">' +
21662 'IMG { cursor: pointer } ' +
21665 var cls = 'roo-htmleditor-body';
21667 if(this.bodyCls.length){
21668 cls += ' ' + this.bodyCls;
21671 return '<html><head>' + st +
21672 //<style type="text/css">' +
21673 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21675 ' </head><body class="' + cls + '"></body></html>';
21679 onRender : function(ct, position)
21682 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21683 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21686 this.el.dom.style.border = '0 none';
21687 this.el.dom.setAttribute('tabIndex', -1);
21688 this.el.addClass('x-hidden hide');
21692 if(Roo.isIE){ // fix IE 1px bogus margin
21693 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21697 this.frameId = Roo.id();
21701 var iframe = this.owner.wrap.createChild({
21703 cls: 'form-control', // bootstrap..
21705 name: this.frameId,
21706 frameBorder : 'no',
21707 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
21712 this.iframe = iframe.dom;
21714 this.assignDocWin();
21716 this.doc.designMode = 'on';
21719 this.doc.write(this.getDocMarkup());
21723 var task = { // must defer to wait for browser to be ready
21725 //console.log("run task?" + this.doc.readyState);
21726 this.assignDocWin();
21727 if(this.doc.body || this.doc.readyState == 'complete'){
21729 this.doc.designMode="on";
21733 Roo.TaskMgr.stop(task);
21734 this.initEditor.defer(10, this);
21741 Roo.TaskMgr.start(task);
21746 onResize : function(w, h)
21748 Roo.log('resize: ' +w + ',' + h );
21749 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21753 if(typeof w == 'number'){
21755 this.iframe.style.width = w + 'px';
21757 if(typeof h == 'number'){
21759 this.iframe.style.height = h + 'px';
21761 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21768 * Toggles the editor between standard and source edit mode.
21769 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21771 toggleSourceEdit : function(sourceEditMode){
21773 this.sourceEditMode = sourceEditMode === true;
21775 if(this.sourceEditMode){
21777 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
21780 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21781 //this.iframe.className = '';
21784 //this.setSize(this.owner.wrap.getSize());
21785 //this.fireEvent('editmodechange', this, this.sourceEditMode);
21792 * Protected method that will not generally be called directly. If you need/want
21793 * custom HTML cleanup, this is the method you should override.
21794 * @param {String} html The HTML to be cleaned
21795 * return {String} The cleaned HTML
21797 cleanHtml : function(html){
21798 html = String(html);
21799 if(html.length > 5){
21800 if(Roo.isSafari){ // strip safari nonsense
21801 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21804 if(html == ' '){
21811 * HTML Editor -> Textarea
21812 * Protected method that will not generally be called directly. Syncs the contents
21813 * of the editor iframe with the textarea.
21815 syncValue : function(){
21816 if(this.initialized){
21817 var bd = (this.doc.body || this.doc.documentElement);
21818 //this.cleanUpPaste(); -- this is done else where and causes havoc..
21819 var html = bd.innerHTML;
21821 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21822 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21824 html = '<div style="'+m[0]+'">' + html + '</div>';
21827 html = this.cleanHtml(html);
21828 // fix up the special chars.. normaly like back quotes in word...
21829 // however we do not want to do this with chinese..
21830 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21831 var cc = b.charCodeAt();
21833 (cc >= 0x4E00 && cc < 0xA000 ) ||
21834 (cc >= 0x3400 && cc < 0x4E00 ) ||
21835 (cc >= 0xf900 && cc < 0xfb00 )
21841 if(this.owner.fireEvent('beforesync', this, html) !== false){
21842 this.el.dom.value = html;
21843 this.owner.fireEvent('sync', this, html);
21849 * Protected method that will not generally be called directly. Pushes the value of the textarea
21850 * into the iframe editor.
21852 pushValue : function(){
21853 if(this.initialized){
21854 var v = this.el.dom.value.trim();
21856 // if(v.length < 1){
21860 if(this.owner.fireEvent('beforepush', this, v) !== false){
21861 var d = (this.doc.body || this.doc.documentElement);
21863 this.cleanUpPaste();
21864 this.el.dom.value = d.innerHTML;
21865 this.owner.fireEvent('push', this, v);
21871 deferFocus : function(){
21872 this.focus.defer(10, this);
21876 focus : function(){
21877 if(this.win && !this.sourceEditMode){
21884 assignDocWin: function()
21886 var iframe = this.iframe;
21889 this.doc = iframe.contentWindow.document;
21890 this.win = iframe.contentWindow;
21892 // if (!Roo.get(this.frameId)) {
21895 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21896 // this.win = Roo.get(this.frameId).dom.contentWindow;
21898 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21902 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21903 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21908 initEditor : function(){
21909 //console.log("INIT EDITOR");
21910 this.assignDocWin();
21914 this.doc.designMode="on";
21916 this.doc.write(this.getDocMarkup());
21919 var dbody = (this.doc.body || this.doc.documentElement);
21920 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21921 // this copies styles from the containing element into thsi one..
21922 // not sure why we need all of this..
21923 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21925 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21926 //ss['background-attachment'] = 'fixed'; // w3c
21927 dbody.bgProperties = 'fixed'; // ie
21928 //Roo.DomHelper.applyStyles(dbody, ss);
21929 Roo.EventManager.on(this.doc, {
21930 //'mousedown': this.onEditorEvent,
21931 'mouseup': this.onEditorEvent,
21932 'dblclick': this.onEditorEvent,
21933 'click': this.onEditorEvent,
21934 'keyup': this.onEditorEvent,
21939 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21941 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21942 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21944 this.initialized = true;
21946 this.owner.fireEvent('initialize', this);
21951 onDestroy : function(){
21957 //for (var i =0; i < this.toolbars.length;i++) {
21958 // // fixme - ask toolbars for heights?
21959 // this.toolbars[i].onDestroy();
21962 //this.wrap.dom.innerHTML = '';
21963 //this.wrap.remove();
21968 onFirstFocus : function(){
21970 this.assignDocWin();
21973 this.activated = true;
21976 if(Roo.isGecko){ // prevent silly gecko errors
21978 var s = this.win.getSelection();
21979 if(!s.focusNode || s.focusNode.nodeType != 3){
21980 var r = s.getRangeAt(0);
21981 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21986 this.execCmd('useCSS', true);
21987 this.execCmd('styleWithCSS', false);
21990 this.owner.fireEvent('activate', this);
21994 adjustFont: function(btn){
21995 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21996 //if(Roo.isSafari){ // safari
21999 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22000 if(Roo.isSafari){ // safari
22001 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22002 v = (v < 10) ? 10 : v;
22003 v = (v > 48) ? 48 : v;
22004 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22009 v = Math.max(1, v+adjust);
22011 this.execCmd('FontSize', v );
22014 onEditorEvent : function(e)
22016 this.owner.fireEvent('editorevent', this, e);
22017 // this.updateToolbar();
22018 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22021 insertTag : function(tg)
22023 // could be a bit smarter... -> wrap the current selected tRoo..
22024 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22026 range = this.createRange(this.getSelection());
22027 var wrappingNode = this.doc.createElement(tg.toLowerCase());
22028 wrappingNode.appendChild(range.extractContents());
22029 range.insertNode(wrappingNode);
22036 this.execCmd("formatblock", tg);
22040 insertText : function(txt)
22044 var range = this.createRange();
22045 range.deleteContents();
22046 //alert(Sender.getAttribute('label'));
22048 range.insertNode(this.doc.createTextNode(txt));
22054 * Executes a Midas editor command on the editor document and performs necessary focus and
22055 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22056 * @param {String} cmd The Midas command
22057 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22059 relayCmd : function(cmd, value){
22061 this.execCmd(cmd, value);
22062 this.owner.fireEvent('editorevent', this);
22063 //this.updateToolbar();
22064 this.owner.deferFocus();
22068 * Executes a Midas editor command directly on the editor document.
22069 * For visual commands, you should use {@link #relayCmd} instead.
22070 * <b>This should only be called after the editor is initialized.</b>
22071 * @param {String} cmd The Midas command
22072 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22074 execCmd : function(cmd, value){
22075 this.doc.execCommand(cmd, false, value === undefined ? null : value);
22082 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22084 * @param {String} text | dom node..
22086 insertAtCursor : function(text)
22089 if(!this.activated){
22095 var r = this.doc.selection.createRange();
22106 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22110 // from jquery ui (MIT licenced)
22112 var win = this.win;
22114 if (win.getSelection && win.getSelection().getRangeAt) {
22115 range = win.getSelection().getRangeAt(0);
22116 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22117 range.insertNode(node);
22118 } else if (win.document.selection && win.document.selection.createRange) {
22119 // no firefox support
22120 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22121 win.document.selection.createRange().pasteHTML(txt);
22123 // no firefox support
22124 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22125 this.execCmd('InsertHTML', txt);
22134 mozKeyPress : function(e){
22136 var c = e.getCharCode(), cmd;
22139 c = String.fromCharCode(c).toLowerCase();
22153 this.cleanUpPaste.defer(100, this);
22161 e.preventDefault();
22169 fixKeys : function(){ // load time branching for fastest keydown performance
22171 return function(e){
22172 var k = e.getKey(), r;
22175 r = this.doc.selection.createRange();
22178 r.pasteHTML('    ');
22185 r = this.doc.selection.createRange();
22187 var target = r.parentElement();
22188 if(!target || target.tagName.toLowerCase() != 'li'){
22190 r.pasteHTML('<br />');
22196 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22197 this.cleanUpPaste.defer(100, this);
22203 }else if(Roo.isOpera){
22204 return function(e){
22205 var k = e.getKey();
22209 this.execCmd('InsertHTML','    ');
22212 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22213 this.cleanUpPaste.defer(100, this);
22218 }else if(Roo.isSafari){
22219 return function(e){
22220 var k = e.getKey();
22224 this.execCmd('InsertText','\t');
22228 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22229 this.cleanUpPaste.defer(100, this);
22237 getAllAncestors: function()
22239 var p = this.getSelectedNode();
22242 a.push(p); // push blank onto stack..
22243 p = this.getParentElement();
22247 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22251 a.push(this.doc.body);
22255 lastSelNode : false,
22258 getSelection : function()
22260 this.assignDocWin();
22261 return Roo.isIE ? this.doc.selection : this.win.getSelection();
22264 getSelectedNode: function()
22266 // this may only work on Gecko!!!
22268 // should we cache this!!!!
22273 var range = this.createRange(this.getSelection()).cloneRange();
22276 var parent = range.parentElement();
22278 var testRange = range.duplicate();
22279 testRange.moveToElementText(parent);
22280 if (testRange.inRange(range)) {
22283 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22286 parent = parent.parentElement;
22291 // is ancestor a text element.
22292 var ac = range.commonAncestorContainer;
22293 if (ac.nodeType == 3) {
22294 ac = ac.parentNode;
22297 var ar = ac.childNodes;
22300 var other_nodes = [];
22301 var has_other_nodes = false;
22302 for (var i=0;i<ar.length;i++) {
22303 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
22306 // fullly contained node.
22308 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22313 // probably selected..
22314 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22315 other_nodes.push(ar[i]);
22319 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
22324 has_other_nodes = true;
22326 if (!nodes.length && other_nodes.length) {
22327 nodes= other_nodes;
22329 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22335 createRange: function(sel)
22337 // this has strange effects when using with
22338 // top toolbar - not sure if it's a great idea.
22339 //this.editor.contentWindow.focus();
22340 if (typeof sel != "undefined") {
22342 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22344 return this.doc.createRange();
22347 return this.doc.createRange();
22350 getParentElement: function()
22353 this.assignDocWin();
22354 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22356 var range = this.createRange(sel);
22359 var p = range.commonAncestorContainer;
22360 while (p.nodeType == 3) { // text node
22371 * Range intersection.. the hard stuff...
22375 * [ -- selected range --- ]
22379 * if end is before start or hits it. fail.
22380 * if start is after end or hits it fail.
22382 * if either hits (but other is outside. - then it's not
22388 // @see http://www.thismuchiknow.co.uk/?p=64.
22389 rangeIntersectsNode : function(range, node)
22391 var nodeRange = node.ownerDocument.createRange();
22393 nodeRange.selectNode(node);
22395 nodeRange.selectNodeContents(node);
22398 var rangeStartRange = range.cloneRange();
22399 rangeStartRange.collapse(true);
22401 var rangeEndRange = range.cloneRange();
22402 rangeEndRange.collapse(false);
22404 var nodeStartRange = nodeRange.cloneRange();
22405 nodeStartRange.collapse(true);
22407 var nodeEndRange = nodeRange.cloneRange();
22408 nodeEndRange.collapse(false);
22410 return rangeStartRange.compareBoundaryPoints(
22411 Range.START_TO_START, nodeEndRange) == -1 &&
22412 rangeEndRange.compareBoundaryPoints(
22413 Range.START_TO_START, nodeStartRange) == 1;
22417 rangeCompareNode : function(range, node)
22419 var nodeRange = node.ownerDocument.createRange();
22421 nodeRange.selectNode(node);
22423 nodeRange.selectNodeContents(node);
22427 range.collapse(true);
22429 nodeRange.collapse(true);
22431 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22432 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
22434 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22436 var nodeIsBefore = ss == 1;
22437 var nodeIsAfter = ee == -1;
22439 if (nodeIsBefore && nodeIsAfter) {
22442 if (!nodeIsBefore && nodeIsAfter) {
22443 return 1; //right trailed.
22446 if (nodeIsBefore && !nodeIsAfter) {
22447 return 2; // left trailed.
22453 // private? - in a new class?
22454 cleanUpPaste : function()
22456 // cleans up the whole document..
22457 Roo.log('cleanuppaste');
22459 this.cleanUpChildren(this.doc.body);
22460 var clean = this.cleanWordChars(this.doc.body.innerHTML);
22461 if (clean != this.doc.body.innerHTML) {
22462 this.doc.body.innerHTML = clean;
22467 cleanWordChars : function(input) {// change the chars to hex code
22468 var he = Roo.HtmlEditorCore;
22470 var output = input;
22471 Roo.each(he.swapCodes, function(sw) {
22472 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22474 output = output.replace(swapper, sw[1]);
22481 cleanUpChildren : function (n)
22483 if (!n.childNodes.length) {
22486 for (var i = n.childNodes.length-1; i > -1 ; i--) {
22487 this.cleanUpChild(n.childNodes[i]);
22494 cleanUpChild : function (node)
22497 //console.log(node);
22498 if (node.nodeName == "#text") {
22499 // clean up silly Windows -- stuff?
22502 if (node.nodeName == "#comment") {
22503 node.parentNode.removeChild(node);
22504 // clean up silly Windows -- stuff?
22507 var lcname = node.tagName.toLowerCase();
22508 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22509 // whitelist of tags..
22511 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22513 node.parentNode.removeChild(node);
22518 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22520 // remove <a name=....> as rendering on yahoo mailer is borked with this.
22521 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22523 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22524 // remove_keep_children = true;
22527 if (remove_keep_children) {
22528 this.cleanUpChildren(node);
22529 // inserts everything just before this node...
22530 while (node.childNodes.length) {
22531 var cn = node.childNodes[0];
22532 node.removeChild(cn);
22533 node.parentNode.insertBefore(cn, node);
22535 node.parentNode.removeChild(node);
22539 if (!node.attributes || !node.attributes.length) {
22540 this.cleanUpChildren(node);
22544 function cleanAttr(n,v)
22547 if (v.match(/^\./) || v.match(/^\//)) {
22550 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22553 if (v.match(/^#/)) {
22556 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22557 node.removeAttribute(n);
22561 var cwhite = this.cwhite;
22562 var cblack = this.cblack;
22564 function cleanStyle(n,v)
22566 if (v.match(/expression/)) { //XSS?? should we even bother..
22567 node.removeAttribute(n);
22571 var parts = v.split(/;/);
22574 Roo.each(parts, function(p) {
22575 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22579 var l = p.split(':').shift().replace(/\s+/g,'');
22580 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22582 if ( cwhite.length && cblack.indexOf(l) > -1) {
22583 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22584 //node.removeAttribute(n);
22588 // only allow 'c whitelisted system attributes'
22589 if ( cwhite.length && cwhite.indexOf(l) < 0) {
22590 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22591 //node.removeAttribute(n);
22601 if (clean.length) {
22602 node.setAttribute(n, clean.join(';'));
22604 node.removeAttribute(n);
22610 for (var i = node.attributes.length-1; i > -1 ; i--) {
22611 var a = node.attributes[i];
22614 if (a.name.toLowerCase().substr(0,2)=='on') {
22615 node.removeAttribute(a.name);
22618 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22619 node.removeAttribute(a.name);
22622 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22623 cleanAttr(a.name,a.value); // fixme..
22626 if (a.name == 'style') {
22627 cleanStyle(a.name,a.value);
22630 /// clean up MS crap..
22631 // tecnically this should be a list of valid class'es..
22634 if (a.name == 'class') {
22635 if (a.value.match(/^Mso/)) {
22636 node.className = '';
22639 if (a.value.match(/^body$/)) {
22640 node.className = '';
22651 this.cleanUpChildren(node);
22657 * Clean up MS wordisms...
22659 cleanWord : function(node)
22664 this.cleanWord(this.doc.body);
22667 if (node.nodeName == "#text") {
22668 // clean up silly Windows -- stuff?
22671 if (node.nodeName == "#comment") {
22672 node.parentNode.removeChild(node);
22673 // clean up silly Windows -- stuff?
22677 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22678 node.parentNode.removeChild(node);
22682 // remove - but keep children..
22683 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22684 while (node.childNodes.length) {
22685 var cn = node.childNodes[0];
22686 node.removeChild(cn);
22687 node.parentNode.insertBefore(cn, node);
22689 node.parentNode.removeChild(node);
22690 this.iterateChildren(node, this.cleanWord);
22694 if (node.className.length) {
22696 var cn = node.className.split(/\W+/);
22698 Roo.each(cn, function(cls) {
22699 if (cls.match(/Mso[a-zA-Z]+/)) {
22704 node.className = cna.length ? cna.join(' ') : '';
22706 node.removeAttribute("class");
22710 if (node.hasAttribute("lang")) {
22711 node.removeAttribute("lang");
22714 if (node.hasAttribute("style")) {
22716 var styles = node.getAttribute("style").split(";");
22718 Roo.each(styles, function(s) {
22719 if (!s.match(/:/)) {
22722 var kv = s.split(":");
22723 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22726 // what ever is left... we allow.
22729 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22730 if (!nstyle.length) {
22731 node.removeAttribute('style');
22734 this.iterateChildren(node, this.cleanWord);
22740 * iterateChildren of a Node, calling fn each time, using this as the scole..
22741 * @param {DomNode} node node to iterate children of.
22742 * @param {Function} fn method of this class to call on each item.
22744 iterateChildren : function(node, fn)
22746 if (!node.childNodes.length) {
22749 for (var i = node.childNodes.length-1; i > -1 ; i--) {
22750 fn.call(this, node.childNodes[i])
22756 * cleanTableWidths.
22758 * Quite often pasting from word etc.. results in tables with column and widths.
22759 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22762 cleanTableWidths : function(node)
22767 this.cleanTableWidths(this.doc.body);
22772 if (node.nodeName == "#text" || node.nodeName == "#comment") {
22775 Roo.log(node.tagName);
22776 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22777 this.iterateChildren(node, this.cleanTableWidths);
22780 if (node.hasAttribute('width')) {
22781 node.removeAttribute('width');
22785 if (node.hasAttribute("style")) {
22788 var styles = node.getAttribute("style").split(";");
22790 Roo.each(styles, function(s) {
22791 if (!s.match(/:/)) {
22794 var kv = s.split(":");
22795 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22798 // what ever is left... we allow.
22801 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22802 if (!nstyle.length) {
22803 node.removeAttribute('style');
22807 this.iterateChildren(node, this.cleanTableWidths);
22815 domToHTML : function(currentElement, depth, nopadtext) {
22817 depth = depth || 0;
22818 nopadtext = nopadtext || false;
22820 if (!currentElement) {
22821 return this.domToHTML(this.doc.body);
22824 //Roo.log(currentElement);
22826 var allText = false;
22827 var nodeName = currentElement.nodeName;
22828 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22830 if (nodeName == '#text') {
22832 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22837 if (nodeName != 'BODY') {
22840 // Prints the node tagName, such as <A>, <IMG>, etc
22843 for(i = 0; i < currentElement.attributes.length;i++) {
22845 var aname = currentElement.attributes.item(i).name;
22846 if (!currentElement.attributes.item(i).value.length) {
22849 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22852 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22861 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22864 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22869 // Traverse the tree
22871 var currentElementChild = currentElement.childNodes.item(i);
22872 var allText = true;
22873 var innerHTML = '';
22875 while (currentElementChild) {
22876 // Formatting code (indent the tree so it looks nice on the screen)
22877 var nopad = nopadtext;
22878 if (lastnode == 'SPAN') {
22882 if (currentElementChild.nodeName == '#text') {
22883 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22884 toadd = nopadtext ? toadd : toadd.trim();
22885 if (!nopad && toadd.length > 80) {
22886 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
22888 innerHTML += toadd;
22891 currentElementChild = currentElement.childNodes.item(i);
22897 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
22899 // Recursively traverse the tree structure of the child node
22900 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
22901 lastnode = currentElementChild.nodeName;
22903 currentElementChild=currentElement.childNodes.item(i);
22909 // The remaining code is mostly for formatting the tree
22910 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
22915 ret+= "</"+tagName+">";
22921 applyBlacklists : function()
22923 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
22924 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
22928 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22929 if (b.indexOf(tag) > -1) {
22932 this.white.push(tag);
22936 Roo.each(w, function(tag) {
22937 if (b.indexOf(tag) > -1) {
22940 if (this.white.indexOf(tag) > -1) {
22943 this.white.push(tag);
22948 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22949 if (w.indexOf(tag) > -1) {
22952 this.black.push(tag);
22956 Roo.each(b, function(tag) {
22957 if (w.indexOf(tag) > -1) {
22960 if (this.black.indexOf(tag) > -1) {
22963 this.black.push(tag);
22968 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
22969 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
22973 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22974 if (b.indexOf(tag) > -1) {
22977 this.cwhite.push(tag);
22981 Roo.each(w, function(tag) {
22982 if (b.indexOf(tag) > -1) {
22985 if (this.cwhite.indexOf(tag) > -1) {
22988 this.cwhite.push(tag);
22993 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22994 if (w.indexOf(tag) > -1) {
22997 this.cblack.push(tag);
23001 Roo.each(b, function(tag) {
23002 if (w.indexOf(tag) > -1) {
23005 if (this.cblack.indexOf(tag) > -1) {
23008 this.cblack.push(tag);
23013 setStylesheets : function(stylesheets)
23015 if(typeof(stylesheets) == 'string'){
23016 Roo.get(this.iframe.contentDocument.head).createChild({
23018 rel : 'stylesheet',
23027 Roo.each(stylesheets, function(s) {
23032 Roo.get(_this.iframe.contentDocument.head).createChild({
23034 rel : 'stylesheet',
23043 removeStylesheets : function()
23047 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23052 setStyle : function(style)
23054 Roo.get(this.iframe.contentDocument.head).createChild({
23063 // hide stuff that is not compatible
23077 * @event specialkey
23081 * @cfg {String} fieldClass @hide
23084 * @cfg {String} focusClass @hide
23087 * @cfg {String} autoCreate @hide
23090 * @cfg {String} inputType @hide
23093 * @cfg {String} invalidClass @hide
23096 * @cfg {String} invalidText @hide
23099 * @cfg {String} msgFx @hide
23102 * @cfg {String} validateOnBlur @hide
23106 Roo.HtmlEditorCore.white = [
23107 'area', 'br', 'img', 'input', 'hr', 'wbr',
23109 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
23110 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
23111 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
23112 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
23113 'table', 'ul', 'xmp',
23115 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
23118 'dir', 'menu', 'ol', 'ul', 'dl',
23124 Roo.HtmlEditorCore.black = [
23125 // 'embed', 'object', // enable - backend responsiblity to clean thiese
23127 'base', 'basefont', 'bgsound', 'blink', 'body',
23128 'frame', 'frameset', 'head', 'html', 'ilayer',
23129 'iframe', 'layer', 'link', 'meta', 'object',
23130 'script', 'style' ,'title', 'xml' // clean later..
23132 Roo.HtmlEditorCore.clean = [
23133 'script', 'style', 'title', 'xml'
23135 Roo.HtmlEditorCore.remove = [
23140 Roo.HtmlEditorCore.ablack = [
23144 Roo.HtmlEditorCore.aclean = [
23145 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
23149 Roo.HtmlEditorCore.pwhite= [
23150 'http', 'https', 'mailto'
23153 // white listed style attributes.
23154 Roo.HtmlEditorCore.cwhite= [
23155 // 'text-align', /// default is to allow most things..
23161 // black listed style attributes.
23162 Roo.HtmlEditorCore.cblack= [
23163 // 'font-size' -- this can be set by the project
23167 Roo.HtmlEditorCore.swapCodes =[
23186 * @class Roo.bootstrap.HtmlEditor
23187 * @extends Roo.bootstrap.TextArea
23188 * Bootstrap HtmlEditor class
23191 * Create a new HtmlEditor
23192 * @param {Object} config The config object
23195 Roo.bootstrap.HtmlEditor = function(config){
23196 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23197 if (!this.toolbars) {
23198 this.toolbars = [];
23201 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23204 * @event initialize
23205 * Fires when the editor is fully initialized (including the iframe)
23206 * @param {HtmlEditor} this
23211 * Fires when the editor is first receives the focus. Any insertion must wait
23212 * until after this event.
23213 * @param {HtmlEditor} this
23217 * @event beforesync
23218 * Fires before the textarea is updated with content from the editor iframe. Return false
23219 * to cancel the sync.
23220 * @param {HtmlEditor} this
23221 * @param {String} html
23225 * @event beforepush
23226 * Fires before the iframe editor is updated with content from the textarea. Return false
23227 * to cancel the push.
23228 * @param {HtmlEditor} this
23229 * @param {String} html
23234 * Fires when the textarea is updated with content from the editor iframe.
23235 * @param {HtmlEditor} this
23236 * @param {String} html
23241 * Fires when the iframe editor is updated with content from the textarea.
23242 * @param {HtmlEditor} this
23243 * @param {String} html
23247 * @event editmodechange
23248 * Fires when the editor switches edit modes
23249 * @param {HtmlEditor} this
23250 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23252 editmodechange: true,
23254 * @event editorevent
23255 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23256 * @param {HtmlEditor} this
23260 * @event firstfocus
23261 * Fires when on first focus - needed by toolbars..
23262 * @param {HtmlEditor} this
23267 * Auto save the htmlEditor value as a file into Events
23268 * @param {HtmlEditor} this
23272 * @event savedpreview
23273 * preview the saved version of htmlEditor
23274 * @param {HtmlEditor} this
23281 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
23285 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23290 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23295 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
23300 * @cfg {Number} height (in pixels)
23304 * @cfg {Number} width (in pixels)
23309 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23312 stylesheets: false,
23317 // private properties
23318 validationEvent : false,
23320 initialized : false,
23323 onFocus : Roo.emptyFn,
23325 hideMode:'offsets',
23327 tbContainer : false,
23331 toolbarContainer :function() {
23332 return this.wrap.select('.x-html-editor-tb',true).first();
23336 * Protected method that will not generally be called directly. It
23337 * is called when the editor creates its toolbar. Override this method if you need to
23338 * add custom toolbar buttons.
23339 * @param {HtmlEditor} editor
23341 createToolbar : function(){
23342 Roo.log('renewing');
23343 Roo.log("create toolbars");
23345 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23346 this.toolbars[0].render(this.toolbarContainer());
23350 // if (!editor.toolbars || !editor.toolbars.length) {
23351 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23354 // for (var i =0 ; i < editor.toolbars.length;i++) {
23355 // editor.toolbars[i] = Roo.factory(
23356 // typeof(editor.toolbars[i]) == 'string' ?
23357 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
23358 // Roo.bootstrap.HtmlEditor);
23359 // editor.toolbars[i].init(editor);
23365 onRender : function(ct, position)
23367 // Roo.log("Call onRender: " + this.xtype);
23369 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23371 this.wrap = this.inputEl().wrap({
23372 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23375 this.editorcore.onRender(ct, position);
23377 if (this.resizable) {
23378 this.resizeEl = new Roo.Resizable(this.wrap, {
23382 minHeight : this.height,
23383 height: this.height,
23384 handles : this.resizable,
23387 resize : function(r, w, h) {
23388 _t.onResize(w,h); // -something
23394 this.createToolbar(this);
23397 if(!this.width && this.resizable){
23398 this.setSize(this.wrap.getSize());
23400 if (this.resizeEl) {
23401 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23402 // should trigger onReize..
23408 onResize : function(w, h)
23410 Roo.log('resize: ' +w + ',' + h );
23411 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23415 if(this.inputEl() ){
23416 if(typeof w == 'number'){
23417 var aw = w - this.wrap.getFrameWidth('lr');
23418 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23421 if(typeof h == 'number'){
23422 var tbh = -11; // fixme it needs to tool bar size!
23423 for (var i =0; i < this.toolbars.length;i++) {
23424 // fixme - ask toolbars for heights?
23425 tbh += this.toolbars[i].el.getHeight();
23426 //if (this.toolbars[i].footer) {
23427 // tbh += this.toolbars[i].footer.el.getHeight();
23435 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23436 ah -= 5; // knock a few pixes off for look..
23437 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23441 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23442 this.editorcore.onResize(ew,eh);
23447 * Toggles the editor between standard and source edit mode.
23448 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23450 toggleSourceEdit : function(sourceEditMode)
23452 this.editorcore.toggleSourceEdit(sourceEditMode);
23454 if(this.editorcore.sourceEditMode){
23455 Roo.log('editor - showing textarea');
23458 // Roo.log(this.syncValue());
23460 this.inputEl().removeClass(['hide', 'x-hidden']);
23461 this.inputEl().dom.removeAttribute('tabIndex');
23462 this.inputEl().focus();
23464 Roo.log('editor - hiding textarea');
23466 // Roo.log(this.pushValue());
23469 this.inputEl().addClass(['hide', 'x-hidden']);
23470 this.inputEl().dom.setAttribute('tabIndex', -1);
23471 //this.deferFocus();
23474 if(this.resizable){
23475 this.setSize(this.wrap.getSize());
23478 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23481 // private (for BoxComponent)
23482 adjustSize : Roo.BoxComponent.prototype.adjustSize,
23484 // private (for BoxComponent)
23485 getResizeEl : function(){
23489 // private (for BoxComponent)
23490 getPositionEl : function(){
23495 initEvents : function(){
23496 this.originalValue = this.getValue();
23500 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23503 // markInvalid : Roo.emptyFn,
23505 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23508 // clearInvalid : Roo.emptyFn,
23510 setValue : function(v){
23511 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23512 this.editorcore.pushValue();
23517 deferFocus : function(){
23518 this.focus.defer(10, this);
23522 focus : function(){
23523 this.editorcore.focus();
23529 onDestroy : function(){
23535 for (var i =0; i < this.toolbars.length;i++) {
23536 // fixme - ask toolbars for heights?
23537 this.toolbars[i].onDestroy();
23540 this.wrap.dom.innerHTML = '';
23541 this.wrap.remove();
23546 onFirstFocus : function(){
23547 //Roo.log("onFirstFocus");
23548 this.editorcore.onFirstFocus();
23549 for (var i =0; i < this.toolbars.length;i++) {
23550 this.toolbars[i].onFirstFocus();
23556 syncValue : function()
23558 this.editorcore.syncValue();
23561 pushValue : function()
23563 this.editorcore.pushValue();
23567 // hide stuff that is not compatible
23581 * @event specialkey
23585 * @cfg {String} fieldClass @hide
23588 * @cfg {String} focusClass @hide
23591 * @cfg {String} autoCreate @hide
23594 * @cfg {String} inputType @hide
23597 * @cfg {String} invalidClass @hide
23600 * @cfg {String} invalidText @hide
23603 * @cfg {String} msgFx @hide
23606 * @cfg {String} validateOnBlur @hide
23615 Roo.namespace('Roo.bootstrap.htmleditor');
23617 * @class Roo.bootstrap.HtmlEditorToolbar1
23622 new Roo.bootstrap.HtmlEditor({
23625 new Roo.bootstrap.HtmlEditorToolbar1({
23626 disable : { fonts: 1 , format: 1, ..., ... , ...],
23632 * @cfg {Object} disable List of elements to disable..
23633 * @cfg {Array} btns List of additional buttons.
23637 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23640 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23643 Roo.apply(this, config);
23645 // default disabled, based on 'good practice'..
23646 this.disable = this.disable || {};
23647 Roo.applyIf(this.disable, {
23650 specialElements : true
23652 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23654 this.editor = config.editor;
23655 this.editorcore = config.editor.editorcore;
23657 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23659 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23660 // dont call parent... till later.
23662 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
23667 editorcore : false,
23672 "h1","h2","h3","h4","h5","h6",
23674 "abbr", "acronym", "address", "cite", "samp", "var",
23678 onRender : function(ct, position)
23680 // Roo.log("Call onRender: " + this.xtype);
23682 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23684 this.el.dom.style.marginBottom = '0';
23686 var editorcore = this.editorcore;
23687 var editor= this.editor;
23690 var btn = function(id,cmd , toggle, handler, html){
23692 var event = toggle ? 'toggle' : 'click';
23697 xns: Roo.bootstrap,
23700 enableToggle:toggle !== false,
23702 pressed : toggle ? false : null,
23705 a.listeners[toggle ? 'toggle' : 'click'] = function() {
23706 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
23712 // var cb_box = function...
23717 xns: Roo.bootstrap,
23718 glyphicon : 'font',
23722 xns: Roo.bootstrap,
23726 Roo.each(this.formats, function(f) {
23727 style.menu.items.push({
23729 xns: Roo.bootstrap,
23730 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23735 editorcore.insertTag(this.tagname);
23742 children.push(style);
23744 btn('bold',false,true);
23745 btn('italic',false,true);
23746 btn('align-left', 'justifyleft',true);
23747 btn('align-center', 'justifycenter',true);
23748 btn('align-right' , 'justifyright',true);
23749 btn('link', false, false, function(btn) {
23750 //Roo.log("create link?");
23751 var url = prompt(this.createLinkText, this.defaultLinkValue);
23752 if(url && url != 'http:/'+'/'){
23753 this.editorcore.relayCmd('createlink', url);
23756 btn('list','insertunorderedlist',true);
23757 btn('pencil', false,true, function(btn){
23759 this.toggleSourceEdit(btn.pressed);
23762 if (this.editor.btns.length > 0) {
23763 for (var i = 0; i<this.editor.btns.length; i++) {
23764 children.push(this.editor.btns[i]);
23772 xns: Roo.bootstrap,
23777 xns: Roo.bootstrap,
23782 cog.menu.items.push({
23784 xns: Roo.bootstrap,
23785 html : Clean styles,
23790 editorcore.insertTag(this.tagname);
23799 this.xtype = 'NavSimplebar';
23801 for(var i=0;i< children.length;i++) {
23803 this.buttons.add(this.addxtypeChild(children[i]));
23807 editor.on('editorevent', this.updateToolbar, this);
23809 onBtnClick : function(id)
23811 this.editorcore.relayCmd(id);
23812 this.editorcore.focus();
23816 * Protected method that will not generally be called directly. It triggers
23817 * a toolbar update by reading the markup state of the current selection in the editor.
23819 updateToolbar: function(){
23821 if(!this.editorcore.activated){
23822 this.editor.onFirstFocus(); // is this neeed?
23826 var btns = this.buttons;
23827 var doc = this.editorcore.doc;
23828 btns.get('bold').setActive(doc.queryCommandState('bold'));
23829 btns.get('italic').setActive(doc.queryCommandState('italic'));
23830 //btns.get('underline').setActive(doc.queryCommandState('underline'));
23832 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23833 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23834 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23836 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23837 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23840 var ans = this.editorcore.getAllAncestors();
23841 if (this.formatCombo) {
23844 var store = this.formatCombo.store;
23845 this.formatCombo.setValue("");
23846 for (var i =0; i < ans.length;i++) {
23847 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23849 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23857 // hides menus... - so this cant be on a menu...
23858 Roo.bootstrap.MenuMgr.hideAll();
23860 Roo.bootstrap.MenuMgr.hideAll();
23861 //this.editorsyncValue();
23863 onFirstFocus: function() {
23864 this.buttons.each(function(item){
23868 toggleSourceEdit : function(sourceEditMode){
23871 if(sourceEditMode){
23872 Roo.log("disabling buttons");
23873 this.buttons.each( function(item){
23874 if(item.cmd != 'pencil'){
23880 Roo.log("enabling buttons");
23881 if(this.editorcore.initialized){
23882 this.buttons.each( function(item){
23888 Roo.log("calling toggole on editor");
23889 // tell the editor that it's been pressed..
23890 this.editor.toggleSourceEdit(sourceEditMode);
23900 * @class Roo.bootstrap.Table.AbstractSelectionModel
23901 * @extends Roo.util.Observable
23902 * Abstract base class for grid SelectionModels. It provides the interface that should be
23903 * implemented by descendant classes. This class should not be directly instantiated.
23906 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23907 this.locked = false;
23908 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23912 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
23913 /** @ignore Called by the grid automatically. Do not call directly. */
23914 init : function(grid){
23920 * Locks the selections.
23923 this.locked = true;
23927 * Unlocks the selections.
23929 unlock : function(){
23930 this.locked = false;
23934 * Returns true if the selections are locked.
23935 * @return {Boolean}
23937 isLocked : function(){
23938 return this.locked;
23942 * @extends Roo.bootstrap.Table.AbstractSelectionModel
23943 * @class Roo.bootstrap.Table.RowSelectionModel
23944 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23945 * It supports multiple selections and keyboard selection/navigation.
23947 * @param {Object} config
23950 Roo.bootstrap.Table.RowSelectionModel = function(config){
23951 Roo.apply(this, config);
23952 this.selections = new Roo.util.MixedCollection(false, function(o){
23957 this.lastActive = false;
23961 * @event selectionchange
23962 * Fires when the selection changes
23963 * @param {SelectionModel} this
23965 "selectionchange" : true,
23967 * @event afterselectionchange
23968 * Fires after the selection changes (eg. by key press or clicking)
23969 * @param {SelectionModel} this
23971 "afterselectionchange" : true,
23973 * @event beforerowselect
23974 * Fires when a row is selected being selected, return false to cancel.
23975 * @param {SelectionModel} this
23976 * @param {Number} rowIndex The selected index
23977 * @param {Boolean} keepExisting False if other selections will be cleared
23979 "beforerowselect" : true,
23982 * Fires when a row is selected.
23983 * @param {SelectionModel} this
23984 * @param {Number} rowIndex The selected index
23985 * @param {Roo.data.Record} r The record
23987 "rowselect" : true,
23989 * @event rowdeselect
23990 * Fires when a row is deselected.
23991 * @param {SelectionModel} this
23992 * @param {Number} rowIndex The selected index
23994 "rowdeselect" : true
23996 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23997 this.locked = false;
24000 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
24002 * @cfg {Boolean} singleSelect
24003 * True to allow selection of only one row at a time (defaults to false)
24005 singleSelect : false,
24008 initEvents : function()
24011 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24012 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
24013 //}else{ // allow click to work like normal
24014 // this.grid.on("rowclick", this.handleDragableRowClick, this);
24016 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24017 this.grid.on("rowclick", this.handleMouseDown, this);
24019 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24020 "up" : function(e){
24022 this.selectPrevious(e.shiftKey);
24023 }else if(this.last !== false && this.lastActive !== false){
24024 var last = this.last;
24025 this.selectRange(this.last, this.lastActive-1);
24026 this.grid.getView().focusRow(this.lastActive);
24027 if(last !== false){
24031 this.selectFirstRow();
24033 this.fireEvent("afterselectionchange", this);
24035 "down" : function(e){
24037 this.selectNext(e.shiftKey);
24038 }else if(this.last !== false && this.lastActive !== false){
24039 var last = this.last;
24040 this.selectRange(this.last, this.lastActive+1);
24041 this.grid.getView().focusRow(this.lastActive);
24042 if(last !== false){
24046 this.selectFirstRow();
24048 this.fireEvent("afterselectionchange", this);
24052 this.grid.store.on('load', function(){
24053 this.selections.clear();
24056 var view = this.grid.view;
24057 view.on("refresh", this.onRefresh, this);
24058 view.on("rowupdated", this.onRowUpdated, this);
24059 view.on("rowremoved", this.onRemove, this);
24064 onRefresh : function()
24066 var ds = this.grid.store, i, v = this.grid.view;
24067 var s = this.selections;
24068 s.each(function(r){
24069 if((i = ds.indexOfId(r.id)) != -1){
24078 onRemove : function(v, index, r){
24079 this.selections.remove(r);
24083 onRowUpdated : function(v, index, r){
24084 if(this.isSelected(r)){
24085 v.onRowSelect(index);
24091 * @param {Array} records The records to select
24092 * @param {Boolean} keepExisting (optional) True to keep existing selections
24094 selectRecords : function(records, keepExisting)
24097 this.clearSelections();
24099 var ds = this.grid.store;
24100 for(var i = 0, len = records.length; i < len; i++){
24101 this.selectRow(ds.indexOf(records[i]), true);
24106 * Gets the number of selected rows.
24109 getCount : function(){
24110 return this.selections.length;
24114 * Selects the first row in the grid.
24116 selectFirstRow : function(){
24121 * Select the last row.
24122 * @param {Boolean} keepExisting (optional) True to keep existing selections
24124 selectLastRow : function(keepExisting){
24125 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24126 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24130 * Selects the row immediately following the last selected row.
24131 * @param {Boolean} keepExisting (optional) True to keep existing selections
24133 selectNext : function(keepExisting)
24135 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24136 this.selectRow(this.last+1, keepExisting);
24137 this.grid.getView().focusRow(this.last);
24142 * Selects the row that precedes the last selected row.
24143 * @param {Boolean} keepExisting (optional) True to keep existing selections
24145 selectPrevious : function(keepExisting){
24147 this.selectRow(this.last-1, keepExisting);
24148 this.grid.getView().focusRow(this.last);
24153 * Returns the selected records
24154 * @return {Array} Array of selected records
24156 getSelections : function(){
24157 return [].concat(this.selections.items);
24161 * Returns the first selected record.
24164 getSelected : function(){
24165 return this.selections.itemAt(0);
24170 * Clears all selections.
24172 clearSelections : function(fast)
24178 var ds = this.grid.store;
24179 var s = this.selections;
24180 s.each(function(r){
24181 this.deselectRow(ds.indexOfId(r.id));
24185 this.selections.clear();
24192 * Selects all rows.
24194 selectAll : function(){
24198 this.selections.clear();
24199 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24200 this.selectRow(i, true);
24205 * Returns True if there is a selection.
24206 * @return {Boolean}
24208 hasSelection : function(){
24209 return this.selections.length > 0;
24213 * Returns True if the specified row is selected.
24214 * @param {Number/Record} record The record or index of the record to check
24215 * @return {Boolean}
24217 isSelected : function(index){
24218 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24219 return (r && this.selections.key(r.id) ? true : false);
24223 * Returns True if the specified record id is selected.
24224 * @param {String} id The id of record to check
24225 * @return {Boolean}
24227 isIdSelected : function(id){
24228 return (this.selections.key(id) ? true : false);
24233 handleMouseDBClick : function(e, t){
24237 handleMouseDown : function(e, t)
24239 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24240 if(this.isLocked() || rowIndex < 0 ){
24243 if(e.shiftKey && this.last !== false){
24244 var last = this.last;
24245 this.selectRange(last, rowIndex, e.ctrlKey);
24246 this.last = last; // reset the last
24250 var isSelected = this.isSelected(rowIndex);
24251 //Roo.log("select row:" + rowIndex);
24253 this.deselectRow(rowIndex);
24255 this.selectRow(rowIndex, true);
24259 if(e.button !== 0 && isSelected){
24260 alert('rowIndex 2: ' + rowIndex);
24261 view.focusRow(rowIndex);
24262 }else if(e.ctrlKey && isSelected){
24263 this.deselectRow(rowIndex);
24264 }else if(!isSelected){
24265 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24266 view.focusRow(rowIndex);
24270 this.fireEvent("afterselectionchange", this);
24273 handleDragableRowClick : function(grid, rowIndex, e)
24275 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24276 this.selectRow(rowIndex, false);
24277 grid.view.focusRow(rowIndex);
24278 this.fireEvent("afterselectionchange", this);
24283 * Selects multiple rows.
24284 * @param {Array} rows Array of the indexes of the row to select
24285 * @param {Boolean} keepExisting (optional) True to keep existing selections
24287 selectRows : function(rows, keepExisting){
24289 this.clearSelections();
24291 for(var i = 0, len = rows.length; i < len; i++){
24292 this.selectRow(rows[i], true);
24297 * Selects a range of rows. All rows in between startRow and endRow are also selected.
24298 * @param {Number} startRow The index of the first row in the range
24299 * @param {Number} endRow The index of the last row in the range
24300 * @param {Boolean} keepExisting (optional) True to retain existing selections
24302 selectRange : function(startRow, endRow, keepExisting){
24307 this.clearSelections();
24309 if(startRow <= endRow){
24310 for(var i = startRow; i <= endRow; i++){
24311 this.selectRow(i, true);
24314 for(var i = startRow; i >= endRow; i--){
24315 this.selectRow(i, true);
24321 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24322 * @param {Number} startRow The index of the first row in the range
24323 * @param {Number} endRow The index of the last row in the range
24325 deselectRange : function(startRow, endRow, preventViewNotify){
24329 for(var i = startRow; i <= endRow; i++){
24330 this.deselectRow(i, preventViewNotify);
24336 * @param {Number} row The index of the row to select
24337 * @param {Boolean} keepExisting (optional) True to keep existing selections
24339 selectRow : function(index, keepExisting, preventViewNotify)
24341 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24344 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24345 if(!keepExisting || this.singleSelect){
24346 this.clearSelections();
24349 var r = this.grid.store.getAt(index);
24350 //console.log('selectRow - record id :' + r.id);
24352 this.selections.add(r);
24353 this.last = this.lastActive = index;
24354 if(!preventViewNotify){
24355 var proxy = new Roo.Element(
24356 this.grid.getRowDom(index)
24358 proxy.addClass('bg-info info');
24360 this.fireEvent("rowselect", this, index, r);
24361 this.fireEvent("selectionchange", this);
24367 * @param {Number} row The index of the row to deselect
24369 deselectRow : function(index, preventViewNotify)
24374 if(this.last == index){
24377 if(this.lastActive == index){
24378 this.lastActive = false;
24381 var r = this.grid.store.getAt(index);
24386 this.selections.remove(r);
24387 //.console.log('deselectRow - record id :' + r.id);
24388 if(!preventViewNotify){
24390 var proxy = new Roo.Element(
24391 this.grid.getRowDom(index)
24393 proxy.removeClass('bg-info info');
24395 this.fireEvent("rowdeselect", this, index);
24396 this.fireEvent("selectionchange", this);
24400 restoreLast : function(){
24402 this.last = this._last;
24407 acceptsNav : function(row, col, cm){
24408 return !cm.isHidden(col) && cm.isCellEditable(col, row);
24412 onEditorKey : function(field, e){
24413 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24418 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24420 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24422 }else if(k == e.ENTER && !e.ctrlKey){
24426 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24428 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24430 }else if(k == e.ESC){
24434 g.startEditing(newCell[0], newCell[1]);
24440 * Ext JS Library 1.1.1
24441 * Copyright(c) 2006-2007, Ext JS, LLC.
24443 * Originally Released Under LGPL - original licence link has changed is not relivant.
24446 * <script type="text/javascript">
24450 * @class Roo.bootstrap.PagingToolbar
24451 * @extends Roo.bootstrap.NavSimplebar
24452 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24454 * Create a new PagingToolbar
24455 * @param {Object} config The config object
24456 * @param {Roo.data.Store} store
24458 Roo.bootstrap.PagingToolbar = function(config)
24460 // old args format still supported... - xtype is prefered..
24461 // created from xtype...
24463 this.ds = config.dataSource;
24465 if (config.store && !this.ds) {
24466 this.store= Roo.factory(config.store, Roo.data);
24467 this.ds = this.store;
24468 this.ds.xmodule = this.xmodule || false;
24471 this.toolbarItems = [];
24472 if (config.items) {
24473 this.toolbarItems = config.items;
24476 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24481 this.bind(this.ds);
24484 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24488 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24490 * @cfg {Roo.data.Store} dataSource
24491 * The underlying data store providing the paged data
24494 * @cfg {String/HTMLElement/Element} container
24495 * container The id or element that will contain the toolbar
24498 * @cfg {Boolean} displayInfo
24499 * True to display the displayMsg (defaults to false)
24502 * @cfg {Number} pageSize
24503 * The number of records to display per page (defaults to 20)
24507 * @cfg {String} displayMsg
24508 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24510 displayMsg : 'Displaying {0} - {1} of {2}',
24512 * @cfg {String} emptyMsg
24513 * The message to display when no records are found (defaults to "No data to display")
24515 emptyMsg : 'No data to display',
24517 * Customizable piece of the default paging text (defaults to "Page")
24520 beforePageText : "Page",
24522 * Customizable piece of the default paging text (defaults to "of %0")
24525 afterPageText : "of {0}",
24527 * Customizable piece of the default paging text (defaults to "First Page")
24530 firstText : "First Page",
24532 * Customizable piece of the default paging text (defaults to "Previous Page")
24535 prevText : "Previous Page",
24537 * Customizable piece of the default paging text (defaults to "Next Page")
24540 nextText : "Next Page",
24542 * Customizable piece of the default paging text (defaults to "Last Page")
24545 lastText : "Last Page",
24547 * Customizable piece of the default paging text (defaults to "Refresh")
24550 refreshText : "Refresh",
24554 onRender : function(ct, position)
24556 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24557 this.navgroup.parentId = this.id;
24558 this.navgroup.onRender(this.el, null);
24559 // add the buttons to the navgroup
24561 if(this.displayInfo){
24562 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24563 this.displayEl = this.el.select('.x-paging-info', true).first();
24564 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24565 // this.displayEl = navel.el.select('span',true).first();
24571 Roo.each(_this.buttons, function(e){ // this might need to use render????
24572 Roo.factory(e).render(_this.el);
24576 Roo.each(_this.toolbarItems, function(e) {
24577 _this.navgroup.addItem(e);
24581 this.first = this.navgroup.addItem({
24582 tooltip: this.firstText,
24584 icon : 'fa fa-backward',
24586 preventDefault: true,
24587 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24590 this.prev = this.navgroup.addItem({
24591 tooltip: this.prevText,
24593 icon : 'fa fa-step-backward',
24595 preventDefault: true,
24596 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
24598 //this.addSeparator();
24601 var field = this.navgroup.addItem( {
24603 cls : 'x-paging-position',
24605 html : this.beforePageText +
24606 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24607 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
24610 this.field = field.el.select('input', true).first();
24611 this.field.on("keydown", this.onPagingKeydown, this);
24612 this.field.on("focus", function(){this.dom.select();});
24615 this.afterTextEl = field.el.select('.x-paging-after',true).first();
24616 //this.field.setHeight(18);
24617 //this.addSeparator();
24618 this.next = this.navgroup.addItem({
24619 tooltip: this.nextText,
24621 html : ' <i class="fa fa-step-forward">',
24623 preventDefault: true,
24624 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
24626 this.last = this.navgroup.addItem({
24627 tooltip: this.lastText,
24628 icon : 'fa fa-forward',
24631 preventDefault: true,
24632 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
24634 //this.addSeparator();
24635 this.loading = this.navgroup.addItem({
24636 tooltip: this.refreshText,
24637 icon: 'fa fa-refresh',
24638 preventDefault: true,
24639 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24645 updateInfo : function(){
24646 if(this.displayEl){
24647 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24648 var msg = count == 0 ?
24652 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
24654 this.displayEl.update(msg);
24659 onLoad : function(ds, r, o)
24661 this.cursor = o.params.start ? o.params.start : 0;
24663 var d = this.getPageData(),
24668 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24669 this.field.dom.value = ap;
24670 this.first.setDisabled(ap == 1);
24671 this.prev.setDisabled(ap == 1);
24672 this.next.setDisabled(ap == ps);
24673 this.last.setDisabled(ap == ps);
24674 this.loading.enable();
24679 getPageData : function(){
24680 var total = this.ds.getTotalCount();
24683 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24684 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24689 onLoadError : function(){
24690 this.loading.enable();
24694 onPagingKeydown : function(e){
24695 var k = e.getKey();
24696 var d = this.getPageData();
24698 var v = this.field.dom.value, pageNum;
24699 if(!v || isNaN(pageNum = parseInt(v, 10))){
24700 this.field.dom.value = d.activePage;
24703 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24704 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24707 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))
24709 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24710 this.field.dom.value = pageNum;
24711 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24714 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24716 var v = this.field.dom.value, pageNum;
24717 var increment = (e.shiftKey) ? 10 : 1;
24718 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24721 if(!v || isNaN(pageNum = parseInt(v, 10))) {
24722 this.field.dom.value = d.activePage;
24725 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24727 this.field.dom.value = parseInt(v, 10) + increment;
24728 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24729 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24736 beforeLoad : function(){
24738 this.loading.disable();
24743 onClick : function(which){
24752 ds.load({params:{start: 0, limit: this.pageSize}});
24755 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24758 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24761 var total = ds.getTotalCount();
24762 var extra = total % this.pageSize;
24763 var lastStart = extra ? (total - extra) : total-this.pageSize;
24764 ds.load({params:{start: lastStart, limit: this.pageSize}});
24767 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24773 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24774 * @param {Roo.data.Store} store The data store to unbind
24776 unbind : function(ds){
24777 ds.un("beforeload", this.beforeLoad, this);
24778 ds.un("load", this.onLoad, this);
24779 ds.un("loadexception", this.onLoadError, this);
24780 ds.un("remove", this.updateInfo, this);
24781 ds.un("add", this.updateInfo, this);
24782 this.ds = undefined;
24786 * Binds the paging toolbar to the specified {@link Roo.data.Store}
24787 * @param {Roo.data.Store} store The data store to bind
24789 bind : function(ds){
24790 ds.on("beforeload", this.beforeLoad, this);
24791 ds.on("load", this.onLoad, this);
24792 ds.on("loadexception", this.onLoadError, this);
24793 ds.on("remove", this.updateInfo, this);
24794 ds.on("add", this.updateInfo, this);
24805 * @class Roo.bootstrap.MessageBar
24806 * @extends Roo.bootstrap.Component
24807 * Bootstrap MessageBar class
24808 * @cfg {String} html contents of the MessageBar
24809 * @cfg {String} weight (info | success | warning | danger) default info
24810 * @cfg {String} beforeClass insert the bar before the given class
24811 * @cfg {Boolean} closable (true | false) default false
24812 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24815 * Create a new Element
24816 * @param {Object} config The config object
24819 Roo.bootstrap.MessageBar = function(config){
24820 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24823 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
24829 beforeClass: 'bootstrap-sticky-wrap',
24831 getAutoCreate : function(){
24835 cls: 'alert alert-dismissable alert-' + this.weight,
24840 html: this.html || ''
24846 cfg.cls += ' alert-messages-fixed';
24860 onRender : function(ct, position)
24862 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24865 var cfg = Roo.apply({}, this.getAutoCreate());
24869 cfg.cls += ' ' + this.cls;
24872 cfg.style = this.style;
24874 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24876 this.el.setVisibilityMode(Roo.Element.DISPLAY);
24879 this.el.select('>button.close').on('click', this.hide, this);
24885 if (!this.rendered) {
24891 this.fireEvent('show', this);
24897 if (!this.rendered) {
24903 this.fireEvent('hide', this);
24906 update : function()
24908 // var e = this.el.dom.firstChild;
24910 // if(this.closable){
24911 // e = e.nextSibling;
24914 // e.data = this.html || '';
24916 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24932 * @class Roo.bootstrap.Graph
24933 * @extends Roo.bootstrap.Component
24934 * Bootstrap Graph class
24938 @cfg {String} graphtype bar | vbar | pie
24939 @cfg {number} g_x coodinator | centre x (pie)
24940 @cfg {number} g_y coodinator | centre y (pie)
24941 @cfg {number} g_r radius (pie)
24942 @cfg {number} g_height height of the chart (respected by all elements in the set)
24943 @cfg {number} g_width width of the chart (respected by all elements in the set)
24944 @cfg {Object} title The title of the chart
24947 -opts (object) options for the chart
24949 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24950 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24952 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.
24953 o stacked (boolean) whether or not to tread values as in a stacked bar chart
24955 o stretch (boolean)
24957 -opts (object) options for the pie
24960 o startAngle (number)
24961 o endAngle (number)
24965 * Create a new Input
24966 * @param {Object} config The config object
24969 Roo.bootstrap.Graph = function(config){
24970 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24976 * The img click event for the img.
24977 * @param {Roo.EventObject} e
24983 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
24994 //g_colors: this.colors,
25001 getAutoCreate : function(){
25012 onRender : function(ct,position){
25015 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25017 if (typeof(Raphael) == 'undefined') {
25018 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25022 this.raphael = Raphael(this.el.dom);
25024 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25025 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25026 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25027 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25029 r.text(160, 10, "Single Series Chart").attr(txtattr);
25030 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25031 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25032 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25034 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25035 r.barchart(330, 10, 300, 220, data1);
25036 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25037 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25040 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25041 // r.barchart(30, 30, 560, 250, xdata, {
25042 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25043 // axis : "0 0 1 1",
25044 // axisxlabels : xdata
25045 // //yvalues : cols,
25048 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25050 // this.load(null,xdata,{
25051 // axis : "0 0 1 1",
25052 // axisxlabels : xdata
25057 load : function(graphtype,xdata,opts)
25059 this.raphael.clear();
25061 graphtype = this.graphtype;
25066 var r = this.raphael,
25067 fin = function () {
25068 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25070 fout = function () {
25071 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25073 pfin = function() {
25074 this.sector.stop();
25075 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25078 this.label[0].stop();
25079 this.label[0].attr({ r: 7.5 });
25080 this.label[1].attr({ "font-weight": 800 });
25083 pfout = function() {
25084 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25087 this.label[0].animate({ r: 5 }, 500, "bounce");
25088 this.label[1].attr({ "font-weight": 400 });
25094 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25097 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25100 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
25101 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25103 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25110 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25115 setTitle: function(o)
25120 initEvents: function() {
25123 this.el.on('click', this.onClick, this);
25127 onClick : function(e)
25129 Roo.log('img onclick');
25130 this.fireEvent('click', this, e);
25142 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25145 * @class Roo.bootstrap.dash.NumberBox
25146 * @extends Roo.bootstrap.Component
25147 * Bootstrap NumberBox class
25148 * @cfg {String} headline Box headline
25149 * @cfg {String} content Box content
25150 * @cfg {String} icon Box icon
25151 * @cfg {String} footer Footer text
25152 * @cfg {String} fhref Footer href
25155 * Create a new NumberBox
25156 * @param {Object} config The config object
25160 Roo.bootstrap.dash.NumberBox = function(config){
25161 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25165 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
25174 getAutoCreate : function(){
25178 cls : 'small-box ',
25186 cls : 'roo-headline',
25187 html : this.headline
25191 cls : 'roo-content',
25192 html : this.content
25206 cls : 'ion ' + this.icon
25215 cls : 'small-box-footer',
25216 href : this.fhref || '#',
25220 cfg.cn.push(footer);
25227 onRender : function(ct,position){
25228 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25235 setHeadline: function (value)
25237 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25240 setFooter: function (value, href)
25242 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25245 this.el.select('a.small-box-footer',true).first().attr('href', href);
25250 setContent: function (value)
25252 this.el.select('.roo-content',true).first().dom.innerHTML = value;
25255 initEvents: function()
25269 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25272 * @class Roo.bootstrap.dash.TabBox
25273 * @extends Roo.bootstrap.Component
25274 * Bootstrap TabBox class
25275 * @cfg {String} title Title of the TabBox
25276 * @cfg {String} icon Icon of the TabBox
25277 * @cfg {Boolean} showtabs (true|false) show the tabs default true
25278 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25281 * Create a new TabBox
25282 * @param {Object} config The config object
25286 Roo.bootstrap.dash.TabBox = function(config){
25287 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25292 * When a pane is added
25293 * @param {Roo.bootstrap.dash.TabPane} pane
25297 * @event activatepane
25298 * When a pane is activated
25299 * @param {Roo.bootstrap.dash.TabPane} pane
25301 "activatepane" : true
25309 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
25314 tabScrollable : false,
25316 getChildContainer : function()
25318 return this.el.select('.tab-content', true).first();
25321 getAutoCreate : function(){
25325 cls: 'pull-left header',
25333 cls: 'fa ' + this.icon
25339 cls: 'nav nav-tabs pull-right',
25345 if(this.tabScrollable){
25352 cls: 'nav nav-tabs pull-right',
25363 cls: 'nav-tabs-custom',
25368 cls: 'tab-content no-padding',
25376 initEvents : function()
25378 //Roo.log('add add pane handler');
25379 this.on('addpane', this.onAddPane, this);
25382 * Updates the box title
25383 * @param {String} html to set the title to.
25385 setTitle : function(value)
25387 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25389 onAddPane : function(pane)
25391 this.panes.push(pane);
25392 //Roo.log('addpane');
25394 // tabs are rendere left to right..
25395 if(!this.showtabs){
25399 var ctr = this.el.select('.nav-tabs', true).first();
25402 var existing = ctr.select('.nav-tab',true);
25403 var qty = existing.getCount();;
25406 var tab = ctr.createChild({
25408 cls : 'nav-tab' + (qty ? '' : ' active'),
25416 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25419 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25421 pane.el.addClass('active');
25426 onTabClick : function(ev,un,ob,pane)
25428 //Roo.log('tab - prev default');
25429 ev.preventDefault();
25432 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25433 pane.tab.addClass('active');
25434 //Roo.log(pane.title);
25435 this.getChildContainer().select('.tab-pane',true).removeClass('active');
25436 // technically we should have a deactivate event.. but maybe add later.
25437 // and it should not de-activate the selected tab...
25438 this.fireEvent('activatepane', pane);
25439 pane.el.addClass('active');
25440 pane.fireEvent('activate');
25445 getActivePane : function()
25448 Roo.each(this.panes, function(p) {
25449 if(p.el.hasClass('active')){
25470 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25472 * @class Roo.bootstrap.TabPane
25473 * @extends Roo.bootstrap.Component
25474 * Bootstrap TabPane class
25475 * @cfg {Boolean} active (false | true) Default false
25476 * @cfg {String} title title of panel
25480 * Create a new TabPane
25481 * @param {Object} config The config object
25484 Roo.bootstrap.dash.TabPane = function(config){
25485 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25491 * When a pane is activated
25492 * @param {Roo.bootstrap.dash.TabPane} pane
25499 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
25504 // the tabBox that this is attached to.
25507 getAutoCreate : function()
25515 cfg.cls += ' active';
25520 initEvents : function()
25522 //Roo.log('trigger add pane handler');
25523 this.parent().fireEvent('addpane', this)
25527 * Updates the tab title
25528 * @param {String} html to set the title to.
25530 setTitle: function(str)
25536 this.tab.select('a', true).first().dom.innerHTML = str;
25553 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25556 * @class Roo.bootstrap.menu.Menu
25557 * @extends Roo.bootstrap.Component
25558 * Bootstrap Menu class - container for Menu
25559 * @cfg {String} html Text of the menu
25560 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25561 * @cfg {String} icon Font awesome icon
25562 * @cfg {String} pos Menu align to (top | bottom) default bottom
25566 * Create a new Menu
25567 * @param {Object} config The config object
25571 Roo.bootstrap.menu.Menu = function(config){
25572 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25576 * @event beforeshow
25577 * Fires before this menu is displayed
25578 * @param {Roo.bootstrap.menu.Menu} this
25582 * @event beforehide
25583 * Fires before this menu is hidden
25584 * @param {Roo.bootstrap.menu.Menu} this
25589 * Fires after this menu is displayed
25590 * @param {Roo.bootstrap.menu.Menu} this
25595 * Fires after this menu is hidden
25596 * @param {Roo.bootstrap.menu.Menu} this
25601 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25602 * @param {Roo.bootstrap.menu.Menu} this
25603 * @param {Roo.EventObject} e
25610 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
25614 weight : 'default',
25619 getChildContainer : function() {
25620 if(this.isSubMenu){
25624 return this.el.select('ul.dropdown-menu', true).first();
25627 getAutoCreate : function()
25632 cls : 'roo-menu-text',
25640 cls : 'fa ' + this.icon
25651 cls : 'dropdown-button btn btn-' + this.weight,
25656 cls : 'dropdown-toggle btn btn-' + this.weight,
25666 cls : 'dropdown-menu'
25672 if(this.pos == 'top'){
25673 cfg.cls += ' dropup';
25676 if(this.isSubMenu){
25679 cls : 'dropdown-menu'
25686 onRender : function(ct, position)
25688 this.isSubMenu = ct.hasClass('dropdown-submenu');
25690 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25693 initEvents : function()
25695 if(this.isSubMenu){
25699 this.hidden = true;
25701 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25702 this.triggerEl.on('click', this.onTriggerPress, this);
25704 this.buttonEl = this.el.select('button.dropdown-button', true).first();
25705 this.buttonEl.on('click', this.onClick, this);
25711 if(this.isSubMenu){
25715 return this.el.select('ul.dropdown-menu', true).first();
25718 onClick : function(e)
25720 this.fireEvent("click", this, e);
25723 onTriggerPress : function(e)
25725 if (this.isVisible()) {
25732 isVisible : function(){
25733 return !this.hidden;
25738 this.fireEvent("beforeshow", this);
25740 this.hidden = false;
25741 this.el.addClass('open');
25743 Roo.get(document).on("mouseup", this.onMouseUp, this);
25745 this.fireEvent("show", this);
25752 this.fireEvent("beforehide", this);
25754 this.hidden = true;
25755 this.el.removeClass('open');
25757 Roo.get(document).un("mouseup", this.onMouseUp);
25759 this.fireEvent("hide", this);
25762 onMouseUp : function()
25776 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25779 * @class Roo.bootstrap.menu.Item
25780 * @extends Roo.bootstrap.Component
25781 * Bootstrap MenuItem class
25782 * @cfg {Boolean} submenu (true | false) default false
25783 * @cfg {String} html text of the item
25784 * @cfg {String} href the link
25785 * @cfg {Boolean} disable (true | false) default false
25786 * @cfg {Boolean} preventDefault (true | false) default true
25787 * @cfg {String} icon Font awesome icon
25788 * @cfg {String} pos Submenu align to (left | right) default right
25792 * Create a new Item
25793 * @param {Object} config The config object
25797 Roo.bootstrap.menu.Item = function(config){
25798 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25802 * Fires when the mouse is hovering over this menu
25803 * @param {Roo.bootstrap.menu.Item} this
25804 * @param {Roo.EventObject} e
25809 * Fires when the mouse exits this menu
25810 * @param {Roo.bootstrap.menu.Item} this
25811 * @param {Roo.EventObject} e
25817 * The raw click event for the entire grid.
25818 * @param {Roo.EventObject} e
25824 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
25829 preventDefault: true,
25834 getAutoCreate : function()
25839 cls : 'roo-menu-item-text',
25847 cls : 'fa ' + this.icon
25856 href : this.href || '#',
25863 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25867 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25869 if(this.pos == 'left'){
25870 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25877 initEvents : function()
25879 this.el.on('mouseover', this.onMouseOver, this);
25880 this.el.on('mouseout', this.onMouseOut, this);
25882 this.el.select('a', true).first().on('click', this.onClick, this);
25886 onClick : function(e)
25888 if(this.preventDefault){
25889 e.preventDefault();
25892 this.fireEvent("click", this, e);
25895 onMouseOver : function(e)
25897 if(this.submenu && this.pos == 'left'){
25898 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25901 this.fireEvent("mouseover", this, e);
25904 onMouseOut : function(e)
25906 this.fireEvent("mouseout", this, e);
25918 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25921 * @class Roo.bootstrap.menu.Separator
25922 * @extends Roo.bootstrap.Component
25923 * Bootstrap Separator class
25926 * Create a new Separator
25927 * @param {Object} config The config object
25931 Roo.bootstrap.menu.Separator = function(config){
25932 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25935 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
25937 getAutoCreate : function(){
25958 * @class Roo.bootstrap.Tooltip
25959 * Bootstrap Tooltip class
25960 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25961 * to determine which dom element triggers the tooltip.
25963 * It needs to add support for additional attributes like tooltip-position
25966 * Create a new Toolti
25967 * @param {Object} config The config object
25970 Roo.bootstrap.Tooltip = function(config){
25971 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25973 this.alignment = Roo.bootstrap.Tooltip.alignment;
25975 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25976 this.alignment = config.alignment;
25981 Roo.apply(Roo.bootstrap.Tooltip, {
25983 * @function init initialize tooltip monitoring.
25987 currentTip : false,
25988 currentRegion : false,
25994 Roo.get(document).on('mouseover', this.enter ,this);
25995 Roo.get(document).on('mouseout', this.leave, this);
25998 this.currentTip = new Roo.bootstrap.Tooltip();
26001 enter : function(ev)
26003 var dom = ev.getTarget();
26005 //Roo.log(['enter',dom]);
26006 var el = Roo.fly(dom);
26007 if (this.currentEl) {
26009 //Roo.log(this.currentEl);
26010 //Roo.log(this.currentEl.contains(dom));
26011 if (this.currentEl == el) {
26014 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26020 if (this.currentTip.el) {
26021 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26025 if(!el || el.dom == document){
26031 // you can not look for children, as if el is the body.. then everythign is the child..
26032 if (!el.attr('tooltip')) { //
26033 if (!el.select("[tooltip]").elements.length) {
26036 // is the mouse over this child...?
26037 bindEl = el.select("[tooltip]").first();
26038 var xy = ev.getXY();
26039 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26040 //Roo.log("not in region.");
26043 //Roo.log("child element over..");
26046 this.currentEl = bindEl;
26047 this.currentTip.bind(bindEl);
26048 this.currentRegion = Roo.lib.Region.getRegion(dom);
26049 this.currentTip.enter();
26052 leave : function(ev)
26054 var dom = ev.getTarget();
26055 //Roo.log(['leave',dom]);
26056 if (!this.currentEl) {
26061 if (dom != this.currentEl.dom) {
26064 var xy = ev.getXY();
26065 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
26068 // only activate leave if mouse cursor is outside... bounding box..
26073 if (this.currentTip) {
26074 this.currentTip.leave();
26076 //Roo.log('clear currentEl');
26077 this.currentEl = false;
26082 'left' : ['r-l', [-2,0], 'right'],
26083 'right' : ['l-r', [2,0], 'left'],
26084 'bottom' : ['t-b', [0,2], 'top'],
26085 'top' : [ 'b-t', [0,-2], 'bottom']
26091 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
26096 delay : null, // can be { show : 300 , hide: 500}
26100 hoverState : null, //???
26102 placement : 'bottom',
26106 getAutoCreate : function(){
26113 cls : 'tooltip-arrow'
26116 cls : 'tooltip-inner'
26123 bind : function(el)
26129 enter : function () {
26131 if (this.timeout != null) {
26132 clearTimeout(this.timeout);
26135 this.hoverState = 'in';
26136 //Roo.log("enter - show");
26137 if (!this.delay || !this.delay.show) {
26142 this.timeout = setTimeout(function () {
26143 if (_t.hoverState == 'in') {
26146 }, this.delay.show);
26150 clearTimeout(this.timeout);
26152 this.hoverState = 'out';
26153 if (!this.delay || !this.delay.hide) {
26159 this.timeout = setTimeout(function () {
26160 //Roo.log("leave - timeout");
26162 if (_t.hoverState == 'out') {
26164 Roo.bootstrap.Tooltip.currentEl = false;
26169 show : function (msg)
26172 this.render(document.body);
26175 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26177 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26179 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26181 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26183 var placement = typeof this.placement == 'function' ?
26184 this.placement.call(this, this.el, on_el) :
26187 var autoToken = /\s?auto?\s?/i;
26188 var autoPlace = autoToken.test(placement);
26190 placement = placement.replace(autoToken, '') || 'top';
26194 //this.el.setXY([0,0]);
26196 //this.el.dom.style.display='block';
26198 //this.el.appendTo(on_el);
26200 var p = this.getPosition();
26201 var box = this.el.getBox();
26207 var align = this.alignment[placement];
26209 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26211 if(placement == 'top' || placement == 'bottom'){
26213 placement = 'right';
26216 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26217 placement = 'left';
26220 var scroll = Roo.select('body', true).first().getScroll();
26222 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26228 this.el.alignTo(this.bindEl, align[0],align[1]);
26229 //var arrow = this.el.select('.arrow',true).first();
26230 //arrow.set(align[2],
26232 this.el.addClass(placement);
26234 this.el.addClass('in fade');
26236 this.hoverState = null;
26238 if (this.el.hasClass('fade')) {
26249 //this.el.setXY([0,0]);
26250 this.el.removeClass('in');
26266 * @class Roo.bootstrap.LocationPicker
26267 * @extends Roo.bootstrap.Component
26268 * Bootstrap LocationPicker class
26269 * @cfg {Number} latitude Position when init default 0
26270 * @cfg {Number} longitude Position when init default 0
26271 * @cfg {Number} zoom default 15
26272 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26273 * @cfg {Boolean} mapTypeControl default false
26274 * @cfg {Boolean} disableDoubleClickZoom default false
26275 * @cfg {Boolean} scrollwheel default true
26276 * @cfg {Boolean} streetViewControl default false
26277 * @cfg {Number} radius default 0
26278 * @cfg {String} locationName
26279 * @cfg {Boolean} draggable default true
26280 * @cfg {Boolean} enableAutocomplete default false
26281 * @cfg {Boolean} enableReverseGeocode default true
26282 * @cfg {String} markerTitle
26285 * Create a new LocationPicker
26286 * @param {Object} config The config object
26290 Roo.bootstrap.LocationPicker = function(config){
26292 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26297 * Fires when the picker initialized.
26298 * @param {Roo.bootstrap.LocationPicker} this
26299 * @param {Google Location} location
26303 * @event positionchanged
26304 * Fires when the picker position changed.
26305 * @param {Roo.bootstrap.LocationPicker} this
26306 * @param {Google Location} location
26308 positionchanged : true,
26311 * Fires when the map resize.
26312 * @param {Roo.bootstrap.LocationPicker} this
26317 * Fires when the map show.
26318 * @param {Roo.bootstrap.LocationPicker} this
26323 * Fires when the map hide.
26324 * @param {Roo.bootstrap.LocationPicker} this
26329 * Fires when click the map.
26330 * @param {Roo.bootstrap.LocationPicker} this
26331 * @param {Map event} e
26335 * @event mapRightClick
26336 * Fires when right click the map.
26337 * @param {Roo.bootstrap.LocationPicker} this
26338 * @param {Map event} e
26340 mapRightClick : true,
26342 * @event markerClick
26343 * Fires when click the marker.
26344 * @param {Roo.bootstrap.LocationPicker} this
26345 * @param {Map event} e
26347 markerClick : true,
26349 * @event markerRightClick
26350 * Fires when right click the marker.
26351 * @param {Roo.bootstrap.LocationPicker} this
26352 * @param {Map event} e
26354 markerRightClick : true,
26356 * @event OverlayViewDraw
26357 * Fires when OverlayView Draw
26358 * @param {Roo.bootstrap.LocationPicker} this
26360 OverlayViewDraw : true,
26362 * @event OverlayViewOnAdd
26363 * Fires when OverlayView Draw
26364 * @param {Roo.bootstrap.LocationPicker} this
26366 OverlayViewOnAdd : true,
26368 * @event OverlayViewOnRemove
26369 * Fires when OverlayView Draw
26370 * @param {Roo.bootstrap.LocationPicker} this
26372 OverlayViewOnRemove : true,
26374 * @event OverlayViewShow
26375 * Fires when OverlayView Draw
26376 * @param {Roo.bootstrap.LocationPicker} this
26377 * @param {Pixel} cpx
26379 OverlayViewShow : true,
26381 * @event OverlayViewHide
26382 * Fires when OverlayView Draw
26383 * @param {Roo.bootstrap.LocationPicker} this
26385 OverlayViewHide : true,
26387 * @event loadexception
26388 * Fires when load google lib failed.
26389 * @param {Roo.bootstrap.LocationPicker} this
26391 loadexception : true
26396 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26398 gMapContext: false,
26404 mapTypeControl: false,
26405 disableDoubleClickZoom: false,
26407 streetViewControl: false,
26411 enableAutocomplete: false,
26412 enableReverseGeocode: true,
26415 getAutoCreate: function()
26420 cls: 'roo-location-picker'
26426 initEvents: function(ct, position)
26428 if(!this.el.getWidth() || this.isApplied()){
26432 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26437 initial: function()
26439 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26440 this.fireEvent('loadexception', this);
26444 if(!this.mapTypeId){
26445 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26448 this.gMapContext = this.GMapContext();
26450 this.initOverlayView();
26452 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26456 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26457 _this.setPosition(_this.gMapContext.marker.position);
26460 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26461 _this.fireEvent('mapClick', this, event);
26465 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26466 _this.fireEvent('mapRightClick', this, event);
26470 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26471 _this.fireEvent('markerClick', this, event);
26475 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26476 _this.fireEvent('markerRightClick', this, event);
26480 this.setPosition(this.gMapContext.location);
26482 this.fireEvent('initial', this, this.gMapContext.location);
26485 initOverlayView: function()
26489 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26493 _this.fireEvent('OverlayViewDraw', _this);
26498 _this.fireEvent('OverlayViewOnAdd', _this);
26501 onRemove: function()
26503 _this.fireEvent('OverlayViewOnRemove', _this);
26506 show: function(cpx)
26508 _this.fireEvent('OverlayViewShow', _this, cpx);
26513 _this.fireEvent('OverlayViewHide', _this);
26519 fromLatLngToContainerPixel: function(event)
26521 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26524 isApplied: function()
26526 return this.getGmapContext() == false ? false : true;
26529 getGmapContext: function()
26531 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26534 GMapContext: function()
26536 var position = new google.maps.LatLng(this.latitude, this.longitude);
26538 var _map = new google.maps.Map(this.el.dom, {
26541 mapTypeId: this.mapTypeId,
26542 mapTypeControl: this.mapTypeControl,
26543 disableDoubleClickZoom: this.disableDoubleClickZoom,
26544 scrollwheel: this.scrollwheel,
26545 streetViewControl: this.streetViewControl,
26546 locationName: this.locationName,
26547 draggable: this.draggable,
26548 enableAutocomplete: this.enableAutocomplete,
26549 enableReverseGeocode: this.enableReverseGeocode
26552 var _marker = new google.maps.Marker({
26553 position: position,
26555 title: this.markerTitle,
26556 draggable: this.draggable
26563 location: position,
26564 radius: this.radius,
26565 locationName: this.locationName,
26566 addressComponents: {
26567 formatted_address: null,
26568 addressLine1: null,
26569 addressLine2: null,
26571 streetNumber: null,
26575 stateOrProvince: null
26578 domContainer: this.el.dom,
26579 geodecoder: new google.maps.Geocoder()
26583 drawCircle: function(center, radius, options)
26585 if (this.gMapContext.circle != null) {
26586 this.gMapContext.circle.setMap(null);
26590 options = Roo.apply({}, options, {
26591 strokeColor: "#0000FF",
26592 strokeOpacity: .35,
26594 fillColor: "#0000FF",
26598 options.map = this.gMapContext.map;
26599 options.radius = radius;
26600 options.center = center;
26601 this.gMapContext.circle = new google.maps.Circle(options);
26602 return this.gMapContext.circle;
26608 setPosition: function(location)
26610 this.gMapContext.location = location;
26611 this.gMapContext.marker.setPosition(location);
26612 this.gMapContext.map.panTo(location);
26613 this.drawCircle(location, this.gMapContext.radius, {});
26617 if (this.gMapContext.settings.enableReverseGeocode) {
26618 this.gMapContext.geodecoder.geocode({
26619 latLng: this.gMapContext.location
26620 }, function(results, status) {
26622 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26623 _this.gMapContext.locationName = results[0].formatted_address;
26624 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26626 _this.fireEvent('positionchanged', this, location);
26633 this.fireEvent('positionchanged', this, location);
26638 google.maps.event.trigger(this.gMapContext.map, "resize");
26640 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26642 this.fireEvent('resize', this);
26645 setPositionByLatLng: function(latitude, longitude)
26647 this.setPosition(new google.maps.LatLng(latitude, longitude));
26650 getCurrentPosition: function()
26653 latitude: this.gMapContext.location.lat(),
26654 longitude: this.gMapContext.location.lng()
26658 getAddressName: function()
26660 return this.gMapContext.locationName;
26663 getAddressComponents: function()
26665 return this.gMapContext.addressComponents;
26668 address_component_from_google_geocode: function(address_components)
26672 for (var i = 0; i < address_components.length; i++) {
26673 var component = address_components[i];
26674 if (component.types.indexOf("postal_code") >= 0) {
26675 result.postalCode = component.short_name;
26676 } else if (component.types.indexOf("street_number") >= 0) {
26677 result.streetNumber = component.short_name;
26678 } else if (component.types.indexOf("route") >= 0) {
26679 result.streetName = component.short_name;
26680 } else if (component.types.indexOf("neighborhood") >= 0) {
26681 result.city = component.short_name;
26682 } else if (component.types.indexOf("locality") >= 0) {
26683 result.city = component.short_name;
26684 } else if (component.types.indexOf("sublocality") >= 0) {
26685 result.district = component.short_name;
26686 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26687 result.stateOrProvince = component.short_name;
26688 } else if (component.types.indexOf("country") >= 0) {
26689 result.country = component.short_name;
26693 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26694 result.addressLine2 = "";
26698 setZoomLevel: function(zoom)
26700 this.gMapContext.map.setZoom(zoom);
26713 this.fireEvent('show', this);
26724 this.fireEvent('hide', this);
26729 Roo.apply(Roo.bootstrap.LocationPicker, {
26731 OverlayView : function(map, options)
26733 options = options || {};
26747 * @class Roo.bootstrap.Alert
26748 * @extends Roo.bootstrap.Component
26749 * Bootstrap Alert class
26750 * @cfg {String} title The title of alert
26751 * @cfg {String} html The content of alert
26752 * @cfg {String} weight ( success | info | warning | danger )
26753 * @cfg {String} faicon font-awesomeicon
26756 * Create a new alert
26757 * @param {Object} config The config object
26761 Roo.bootstrap.Alert = function(config){
26762 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26766 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26773 getAutoCreate : function()
26782 cls : 'roo-alert-icon'
26787 cls : 'roo-alert-title',
26792 cls : 'roo-alert-text',
26799 cfg.cn[0].cls += ' fa ' + this.faicon;
26803 cfg.cls += ' alert-' + this.weight;
26809 initEvents: function()
26811 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26814 setTitle : function(str)
26816 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26819 setText : function(str)
26821 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26824 setWeight : function(weight)
26827 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26830 this.weight = weight;
26832 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26835 setIcon : function(icon)
26838 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26841 this.faicon = icon;
26843 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26864 * @class Roo.bootstrap.UploadCropbox
26865 * @extends Roo.bootstrap.Component
26866 * Bootstrap UploadCropbox class
26867 * @cfg {String} emptyText show when image has been loaded
26868 * @cfg {String} rotateNotify show when image too small to rotate
26869 * @cfg {Number} errorTimeout default 3000
26870 * @cfg {Number} minWidth default 300
26871 * @cfg {Number} minHeight default 300
26872 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26873 * @cfg {Boolean} isDocument (true|false) default false
26874 * @cfg {String} url action url
26875 * @cfg {String} paramName default 'imageUpload'
26876 * @cfg {String} method default POST
26877 * @cfg {Boolean} loadMask (true|false) default true
26878 * @cfg {Boolean} loadingText default 'Loading...'
26881 * Create a new UploadCropbox
26882 * @param {Object} config The config object
26885 Roo.bootstrap.UploadCropbox = function(config){
26886 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26890 * @event beforeselectfile
26891 * Fire before select file
26892 * @param {Roo.bootstrap.UploadCropbox} this
26894 "beforeselectfile" : true,
26897 * Fire after initEvent
26898 * @param {Roo.bootstrap.UploadCropbox} this
26903 * Fire after initEvent
26904 * @param {Roo.bootstrap.UploadCropbox} this
26905 * @param {String} data
26910 * Fire when preparing the file data
26911 * @param {Roo.bootstrap.UploadCropbox} this
26912 * @param {Object} file
26917 * Fire when get exception
26918 * @param {Roo.bootstrap.UploadCropbox} this
26919 * @param {XMLHttpRequest} xhr
26921 "exception" : true,
26923 * @event beforeloadcanvas
26924 * Fire before load the canvas
26925 * @param {Roo.bootstrap.UploadCropbox} this
26926 * @param {String} src
26928 "beforeloadcanvas" : true,
26931 * Fire when trash image
26932 * @param {Roo.bootstrap.UploadCropbox} this
26937 * Fire when download the image
26938 * @param {Roo.bootstrap.UploadCropbox} this
26942 * @event footerbuttonclick
26943 * Fire when footerbuttonclick
26944 * @param {Roo.bootstrap.UploadCropbox} this
26945 * @param {String} type
26947 "footerbuttonclick" : true,
26951 * @param {Roo.bootstrap.UploadCropbox} this
26956 * Fire when rotate the image
26957 * @param {Roo.bootstrap.UploadCropbox} this
26958 * @param {String} pos
26963 * Fire when inspect the file
26964 * @param {Roo.bootstrap.UploadCropbox} this
26965 * @param {Object} file
26970 * Fire when xhr upload the file
26971 * @param {Roo.bootstrap.UploadCropbox} this
26972 * @param {Object} data
26977 * Fire when arrange the file data
26978 * @param {Roo.bootstrap.UploadCropbox} this
26979 * @param {Object} formData
26984 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26987 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26989 emptyText : 'Click to upload image',
26990 rotateNotify : 'Image is too small to rotate',
26991 errorTimeout : 3000,
27005 cropType : 'image/jpeg',
27007 canvasLoaded : false,
27008 isDocument : false,
27010 paramName : 'imageUpload',
27012 loadingText : 'Loading...',
27015 getAutoCreate : function()
27019 cls : 'roo-upload-cropbox',
27023 cls : 'roo-upload-cropbox-selector',
27028 cls : 'roo-upload-cropbox-body',
27029 style : 'cursor:pointer',
27033 cls : 'roo-upload-cropbox-preview'
27037 cls : 'roo-upload-cropbox-thumb'
27041 cls : 'roo-upload-cropbox-empty-notify',
27042 html : this.emptyText
27046 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27047 html : this.rotateNotify
27053 cls : 'roo-upload-cropbox-footer',
27056 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27066 onRender : function(ct, position)
27068 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27070 if (this.buttons.length) {
27072 Roo.each(this.buttons, function(bb) {
27074 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27076 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27082 this.maskEl = this.el;
27086 initEvents : function()
27088 this.urlAPI = (window.createObjectURL && window) ||
27089 (window.URL && URL.revokeObjectURL && URL) ||
27090 (window.webkitURL && webkitURL);
27092 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27093 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27096 this.selectorEl.hide();
27098 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27099 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27101 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27102 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27103 this.thumbEl.hide();
27105 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27106 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27108 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27109 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27110 this.errorEl.hide();
27112 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27113 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27114 this.footerEl.hide();
27116 this.setThumbBoxSize();
27122 this.fireEvent('initial', this);
27129 window.addEventListener("resize", function() { _this.resize(); } );
27131 this.bodyEl.on('click', this.beforeSelectFile, this);
27134 this.bodyEl.on('touchstart', this.onTouchStart, this);
27135 this.bodyEl.on('touchmove', this.onTouchMove, this);
27136 this.bodyEl.on('touchend', this.onTouchEnd, this);
27140 this.bodyEl.on('mousedown', this.onMouseDown, this);
27141 this.bodyEl.on('mousemove', this.onMouseMove, this);
27142 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27143 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27144 Roo.get(document).on('mouseup', this.onMouseUp, this);
27147 this.selectorEl.on('change', this.onFileSelected, this);
27153 this.baseScale = 1;
27155 this.baseRotate = 1;
27156 this.dragable = false;
27157 this.pinching = false;
27160 this.cropData = false;
27161 this.notifyEl.dom.innerHTML = this.emptyText;
27163 this.selectorEl.dom.value = '';
27167 resize : function()
27169 if(this.fireEvent('resize', this) != false){
27170 this.setThumbBoxPosition();
27171 this.setCanvasPosition();
27175 onFooterButtonClick : function(e, el, o, type)
27178 case 'rotate-left' :
27179 this.onRotateLeft(e);
27181 case 'rotate-right' :
27182 this.onRotateRight(e);
27185 this.beforeSelectFile(e);
27200 this.fireEvent('footerbuttonclick', this, type);
27203 beforeSelectFile : function(e)
27205 e.preventDefault();
27207 if(this.fireEvent('beforeselectfile', this) != false){
27208 this.selectorEl.dom.click();
27212 onFileSelected : function(e)
27214 e.preventDefault();
27216 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27220 var file = this.selectorEl.dom.files[0];
27222 if(this.fireEvent('inspect', this, file) != false){
27223 this.prepare(file);
27228 trash : function(e)
27230 this.fireEvent('trash', this);
27233 download : function(e)
27235 this.fireEvent('download', this);
27238 loadCanvas : function(src)
27240 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27244 this.imageEl = document.createElement('img');
27248 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27250 this.imageEl.src = src;
27254 onLoadCanvas : function()
27256 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27257 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27259 this.bodyEl.un('click', this.beforeSelectFile, this);
27261 this.notifyEl.hide();
27262 this.thumbEl.show();
27263 this.footerEl.show();
27265 this.baseRotateLevel();
27267 if(this.isDocument){
27268 this.setThumbBoxSize();
27271 this.setThumbBoxPosition();
27273 this.baseScaleLevel();
27279 this.canvasLoaded = true;
27282 this.maskEl.unmask();
27287 setCanvasPosition : function()
27289 if(!this.canvasEl){
27293 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27294 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27296 this.previewEl.setLeft(pw);
27297 this.previewEl.setTop(ph);
27301 onMouseDown : function(e)
27305 this.dragable = true;
27306 this.pinching = false;
27308 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27309 this.dragable = false;
27313 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27314 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27318 onMouseMove : function(e)
27322 if(!this.canvasLoaded){
27326 if (!this.dragable){
27330 var minX = Math.ceil(this.thumbEl.getLeft(true));
27331 var minY = Math.ceil(this.thumbEl.getTop(true));
27333 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27334 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27336 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27337 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27339 x = x - this.mouseX;
27340 y = y - this.mouseY;
27342 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27343 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27345 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27346 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27348 this.previewEl.setLeft(bgX);
27349 this.previewEl.setTop(bgY);
27351 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27352 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27355 onMouseUp : function(e)
27359 this.dragable = false;
27362 onMouseWheel : function(e)
27366 this.startScale = this.scale;
27368 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27370 if(!this.zoomable()){
27371 this.scale = this.startScale;
27380 zoomable : function()
27382 var minScale = this.thumbEl.getWidth() / this.minWidth;
27384 if(this.minWidth < this.minHeight){
27385 minScale = this.thumbEl.getHeight() / this.minHeight;
27388 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27389 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27393 (this.rotate == 0 || this.rotate == 180) &&
27395 width > this.imageEl.OriginWidth ||
27396 height > this.imageEl.OriginHeight ||
27397 (width < this.minWidth && height < this.minHeight)
27405 (this.rotate == 90 || this.rotate == 270) &&
27407 width > this.imageEl.OriginWidth ||
27408 height > this.imageEl.OriginHeight ||
27409 (width < this.minHeight && height < this.minWidth)
27416 !this.isDocument &&
27417 (this.rotate == 0 || this.rotate == 180) &&
27419 width < this.minWidth ||
27420 width > this.imageEl.OriginWidth ||
27421 height < this.minHeight ||
27422 height > this.imageEl.OriginHeight
27429 !this.isDocument &&
27430 (this.rotate == 90 || this.rotate == 270) &&
27432 width < this.minHeight ||
27433 width > this.imageEl.OriginWidth ||
27434 height < this.minWidth ||
27435 height > this.imageEl.OriginHeight
27445 onRotateLeft : function(e)
27447 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27449 var minScale = this.thumbEl.getWidth() / this.minWidth;
27451 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27452 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27454 this.startScale = this.scale;
27456 while (this.getScaleLevel() < minScale){
27458 this.scale = this.scale + 1;
27460 if(!this.zoomable()){
27465 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27466 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27471 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27478 this.scale = this.startScale;
27480 this.onRotateFail();
27485 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27487 if(this.isDocument){
27488 this.setThumbBoxSize();
27489 this.setThumbBoxPosition();
27490 this.setCanvasPosition();
27495 this.fireEvent('rotate', this, 'left');
27499 onRotateRight : function(e)
27501 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27503 var minScale = this.thumbEl.getWidth() / this.minWidth;
27505 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27506 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27508 this.startScale = this.scale;
27510 while (this.getScaleLevel() < minScale){
27512 this.scale = this.scale + 1;
27514 if(!this.zoomable()){
27519 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27520 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27525 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27532 this.scale = this.startScale;
27534 this.onRotateFail();
27539 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27541 if(this.isDocument){
27542 this.setThumbBoxSize();
27543 this.setThumbBoxPosition();
27544 this.setCanvasPosition();
27549 this.fireEvent('rotate', this, 'right');
27552 onRotateFail : function()
27554 this.errorEl.show(true);
27558 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27563 this.previewEl.dom.innerHTML = '';
27565 var canvasEl = document.createElement("canvas");
27567 var contextEl = canvasEl.getContext("2d");
27569 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27570 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27571 var center = this.imageEl.OriginWidth / 2;
27573 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27574 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27575 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27576 center = this.imageEl.OriginHeight / 2;
27579 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27581 contextEl.translate(center, center);
27582 contextEl.rotate(this.rotate * Math.PI / 180);
27584 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27586 this.canvasEl = document.createElement("canvas");
27588 this.contextEl = this.canvasEl.getContext("2d");
27590 switch (this.rotate) {
27593 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27594 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27596 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27601 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27602 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27604 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27605 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);
27609 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27614 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27615 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27617 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27618 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);
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);
27627 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27628 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27630 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27631 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
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);
27642 this.previewEl.appendChild(this.canvasEl);
27644 this.setCanvasPosition();
27649 if(!this.canvasLoaded){
27653 var imageCanvas = document.createElement("canvas");
27655 var imageContext = imageCanvas.getContext("2d");
27657 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27658 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27660 var center = imageCanvas.width / 2;
27662 imageContext.translate(center, center);
27664 imageContext.rotate(this.rotate * Math.PI / 180);
27666 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27668 var canvas = document.createElement("canvas");
27670 var context = canvas.getContext("2d");
27672 canvas.width = this.minWidth;
27673 canvas.height = this.minHeight;
27675 switch (this.rotate) {
27678 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27679 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27681 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27682 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27684 var targetWidth = this.minWidth - 2 * x;
27685 var targetHeight = this.minHeight - 2 * y;
27689 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27690 scale = targetWidth / width;
27693 if(x > 0 && y == 0){
27694 scale = targetHeight / height;
27697 if(x > 0 && y > 0){
27698 scale = targetWidth / width;
27700 if(width < height){
27701 scale = targetHeight / height;
27705 context.scale(scale, scale);
27707 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27708 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27710 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27711 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27713 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27718 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27719 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27721 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27722 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27724 var targetWidth = this.minWidth - 2 * x;
27725 var targetHeight = this.minHeight - 2 * y;
27729 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27730 scale = targetWidth / width;
27733 if(x > 0 && y == 0){
27734 scale = targetHeight / height;
27737 if(x > 0 && y > 0){
27738 scale = targetWidth / width;
27740 if(width < height){
27741 scale = targetHeight / height;
27745 context.scale(scale, scale);
27747 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27748 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27750 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27751 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27753 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27755 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27760 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27761 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27763 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27764 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27766 var targetWidth = this.minWidth - 2 * x;
27767 var targetHeight = this.minHeight - 2 * y;
27771 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27772 scale = targetWidth / width;
27775 if(x > 0 && y == 0){
27776 scale = targetHeight / height;
27779 if(x > 0 && y > 0){
27780 scale = targetWidth / width;
27782 if(width < height){
27783 scale = targetHeight / height;
27787 context.scale(scale, scale);
27789 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27790 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27792 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27793 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27795 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27796 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27798 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27803 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27804 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27806 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27807 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27809 var targetWidth = this.minWidth - 2 * x;
27810 var targetHeight = this.minHeight - 2 * y;
27814 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27815 scale = targetWidth / width;
27818 if(x > 0 && y == 0){
27819 scale = targetHeight / height;
27822 if(x > 0 && y > 0){
27823 scale = targetWidth / width;
27825 if(width < height){
27826 scale = targetHeight / height;
27830 context.scale(scale, scale);
27832 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27833 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27835 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27836 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27838 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27840 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27847 this.cropData = canvas.toDataURL(this.cropType);
27849 if(this.fireEvent('crop', this, this.cropData) !== false){
27850 this.process(this.file, this.cropData);
27857 setThumbBoxSize : function()
27861 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27862 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27863 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27865 this.minWidth = width;
27866 this.minHeight = height;
27868 if(this.rotate == 90 || this.rotate == 270){
27869 this.minWidth = height;
27870 this.minHeight = width;
27875 width = Math.ceil(this.minWidth * height / this.minHeight);
27877 if(this.minWidth > this.minHeight){
27879 height = Math.ceil(this.minHeight * width / this.minWidth);
27882 this.thumbEl.setStyle({
27883 width : width + 'px',
27884 height : height + 'px'
27891 setThumbBoxPosition : function()
27893 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27894 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27896 this.thumbEl.setLeft(x);
27897 this.thumbEl.setTop(y);
27901 baseRotateLevel : function()
27903 this.baseRotate = 1;
27906 typeof(this.exif) != 'undefined' &&
27907 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27908 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27910 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27913 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27917 baseScaleLevel : function()
27921 if(this.isDocument){
27923 if(this.baseRotate == 6 || this.baseRotate == 8){
27925 height = this.thumbEl.getHeight();
27926 this.baseScale = height / this.imageEl.OriginWidth;
27928 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27929 width = this.thumbEl.getWidth();
27930 this.baseScale = width / this.imageEl.OriginHeight;
27936 height = this.thumbEl.getHeight();
27937 this.baseScale = height / this.imageEl.OriginHeight;
27939 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27940 width = this.thumbEl.getWidth();
27941 this.baseScale = width / this.imageEl.OriginWidth;
27947 if(this.baseRotate == 6 || this.baseRotate == 8){
27949 width = this.thumbEl.getHeight();
27950 this.baseScale = width / this.imageEl.OriginHeight;
27952 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27953 height = this.thumbEl.getWidth();
27954 this.baseScale = height / this.imageEl.OriginHeight;
27957 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27958 height = this.thumbEl.getWidth();
27959 this.baseScale = height / this.imageEl.OriginHeight;
27961 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27962 width = this.thumbEl.getHeight();
27963 this.baseScale = width / this.imageEl.OriginWidth;
27970 width = this.thumbEl.getWidth();
27971 this.baseScale = width / this.imageEl.OriginWidth;
27973 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27974 height = this.thumbEl.getHeight();
27975 this.baseScale = height / this.imageEl.OriginHeight;
27978 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27980 height = this.thumbEl.getHeight();
27981 this.baseScale = height / this.imageEl.OriginHeight;
27983 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27984 width = this.thumbEl.getWidth();
27985 this.baseScale = width / this.imageEl.OriginWidth;
27993 getScaleLevel : function()
27995 return this.baseScale * Math.pow(1.1, this.scale);
27998 onTouchStart : function(e)
28000 if(!this.canvasLoaded){
28001 this.beforeSelectFile(e);
28005 var touches = e.browserEvent.touches;
28011 if(touches.length == 1){
28012 this.onMouseDown(e);
28016 if(touches.length != 2){
28022 for(var i = 0, finger; finger = touches[i]; i++){
28023 coords.push(finger.pageX, finger.pageY);
28026 var x = Math.pow(coords[0] - coords[2], 2);
28027 var y = Math.pow(coords[1] - coords[3], 2);
28029 this.startDistance = Math.sqrt(x + y);
28031 this.startScale = this.scale;
28033 this.pinching = true;
28034 this.dragable = false;
28038 onTouchMove : function(e)
28040 if(!this.pinching && !this.dragable){
28044 var touches = e.browserEvent.touches;
28051 this.onMouseMove(e);
28057 for(var i = 0, finger; finger = touches[i]; i++){
28058 coords.push(finger.pageX, finger.pageY);
28061 var x = Math.pow(coords[0] - coords[2], 2);
28062 var y = Math.pow(coords[1] - coords[3], 2);
28064 this.endDistance = Math.sqrt(x + y);
28066 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28068 if(!this.zoomable()){
28069 this.scale = this.startScale;
28077 onTouchEnd : function(e)
28079 this.pinching = false;
28080 this.dragable = false;
28084 process : function(file, crop)
28087 this.maskEl.mask(this.loadingText);
28090 this.xhr = new XMLHttpRequest();
28092 file.xhr = this.xhr;
28094 this.xhr.open(this.method, this.url, true);
28097 "Accept": "application/json",
28098 "Cache-Control": "no-cache",
28099 "X-Requested-With": "XMLHttpRequest"
28102 for (var headerName in headers) {
28103 var headerValue = headers[headerName];
28105 this.xhr.setRequestHeader(headerName, headerValue);
28111 this.xhr.onload = function()
28113 _this.xhrOnLoad(_this.xhr);
28116 this.xhr.onerror = function()
28118 _this.xhrOnError(_this.xhr);
28121 var formData = new FormData();
28123 formData.append('returnHTML', 'NO');
28126 formData.append('crop', crop);
28129 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28130 formData.append(this.paramName, file, file.name);
28133 if(typeof(file.filename) != 'undefined'){
28134 formData.append('filename', file.filename);
28137 if(typeof(file.mimetype) != 'undefined'){
28138 formData.append('mimetype', file.mimetype);
28141 if(this.fireEvent('arrange', this, formData) != false){
28142 this.xhr.send(formData);
28146 xhrOnLoad : function(xhr)
28149 this.maskEl.unmask();
28152 if (xhr.readyState !== 4) {
28153 this.fireEvent('exception', this, xhr);
28157 var response = Roo.decode(xhr.responseText);
28159 if(!response.success){
28160 this.fireEvent('exception', this, xhr);
28164 var response = Roo.decode(xhr.responseText);
28166 this.fireEvent('upload', this, response);
28170 xhrOnError : function()
28173 this.maskEl.unmask();
28176 Roo.log('xhr on error');
28178 var response = Roo.decode(xhr.responseText);
28184 prepare : function(file)
28187 this.maskEl.mask(this.loadingText);
28193 if(typeof(file) === 'string'){
28194 this.loadCanvas(file);
28198 if(!file || !this.urlAPI){
28203 this.cropType = file.type;
28207 if(this.fireEvent('prepare', this, this.file) != false){
28209 var reader = new FileReader();
28211 reader.onload = function (e) {
28212 if (e.target.error) {
28213 Roo.log(e.target.error);
28217 var buffer = e.target.result,
28218 dataView = new DataView(buffer),
28220 maxOffset = dataView.byteLength - 4,
28224 if (dataView.getUint16(0) === 0xffd8) {
28225 while (offset < maxOffset) {
28226 markerBytes = dataView.getUint16(offset);
28228 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28229 markerLength = dataView.getUint16(offset + 2) + 2;
28230 if (offset + markerLength > dataView.byteLength) {
28231 Roo.log('Invalid meta data: Invalid segment size.');
28235 if(markerBytes == 0xffe1){
28236 _this.parseExifData(
28243 offset += markerLength;
28253 var url = _this.urlAPI.createObjectURL(_this.file);
28255 _this.loadCanvas(url);
28260 reader.readAsArrayBuffer(this.file);
28266 parseExifData : function(dataView, offset, length)
28268 var tiffOffset = offset + 10,
28272 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28273 // No Exif data, might be XMP data instead
28277 // Check for the ASCII code for "Exif" (0x45786966):
28278 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28279 // No Exif data, might be XMP data instead
28282 if (tiffOffset + 8 > dataView.byteLength) {
28283 Roo.log('Invalid Exif data: Invalid segment size.');
28286 // Check for the two null bytes:
28287 if (dataView.getUint16(offset + 8) !== 0x0000) {
28288 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28291 // Check the byte alignment:
28292 switch (dataView.getUint16(tiffOffset)) {
28294 littleEndian = true;
28297 littleEndian = false;
28300 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28303 // Check for the TIFF tag marker (0x002A):
28304 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28305 Roo.log('Invalid Exif data: Missing TIFF marker.');
28308 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28309 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28311 this.parseExifTags(
28314 tiffOffset + dirOffset,
28319 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28324 if (dirOffset + 6 > dataView.byteLength) {
28325 Roo.log('Invalid Exif data: Invalid directory offset.');
28328 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28329 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28330 if (dirEndOffset + 4 > dataView.byteLength) {
28331 Roo.log('Invalid Exif data: Invalid directory size.');
28334 for (i = 0; i < tagsNumber; i += 1) {
28338 dirOffset + 2 + 12 * i, // tag offset
28342 // Return the offset to the next directory:
28343 return dataView.getUint32(dirEndOffset, littleEndian);
28346 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28348 var tag = dataView.getUint16(offset, littleEndian);
28350 this.exif[tag] = this.getExifValue(
28354 dataView.getUint16(offset + 2, littleEndian), // tag type
28355 dataView.getUint32(offset + 4, littleEndian), // tag length
28360 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28362 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28371 Roo.log('Invalid Exif data: Invalid tag type.');
28375 tagSize = tagType.size * length;
28376 // Determine if the value is contained in the dataOffset bytes,
28377 // or if the value at the dataOffset is a pointer to the actual data:
28378 dataOffset = tagSize > 4 ?
28379 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28380 if (dataOffset + tagSize > dataView.byteLength) {
28381 Roo.log('Invalid Exif data: Invalid data offset.');
28384 if (length === 1) {
28385 return tagType.getValue(dataView, dataOffset, littleEndian);
28388 for (i = 0; i < length; i += 1) {
28389 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28392 if (tagType.ascii) {
28394 // Concatenate the chars:
28395 for (i = 0; i < values.length; i += 1) {
28397 // Ignore the terminating NULL byte(s):
28398 if (c === '\u0000') {
28410 Roo.apply(Roo.bootstrap.UploadCropbox, {
28412 'Orientation': 0x0112
28416 1: 0, //'top-left',
28418 3: 180, //'bottom-right',
28419 // 4: 'bottom-left',
28421 6: 90, //'right-top',
28422 // 7: 'right-bottom',
28423 8: 270 //'left-bottom'
28427 // byte, 8-bit unsigned int:
28429 getValue: function (dataView, dataOffset) {
28430 return dataView.getUint8(dataOffset);
28434 // ascii, 8-bit byte:
28436 getValue: function (dataView, dataOffset) {
28437 return String.fromCharCode(dataView.getUint8(dataOffset));
28442 // short, 16 bit int:
28444 getValue: function (dataView, dataOffset, littleEndian) {
28445 return dataView.getUint16(dataOffset, littleEndian);
28449 // long, 32 bit int:
28451 getValue: function (dataView, dataOffset, littleEndian) {
28452 return dataView.getUint32(dataOffset, littleEndian);
28456 // rational = two long values, first is numerator, second is denominator:
28458 getValue: function (dataView, dataOffset, littleEndian) {
28459 return dataView.getUint32(dataOffset, littleEndian) /
28460 dataView.getUint32(dataOffset + 4, littleEndian);
28464 // slong, 32 bit signed int:
28466 getValue: function (dataView, dataOffset, littleEndian) {
28467 return dataView.getInt32(dataOffset, littleEndian);
28471 // srational, two slongs, first is numerator, second is denominator:
28473 getValue: function (dataView, dataOffset, littleEndian) {
28474 return dataView.getInt32(dataOffset, littleEndian) /
28475 dataView.getInt32(dataOffset + 4, littleEndian);
28485 cls : 'btn-group roo-upload-cropbox-rotate-left',
28486 action : 'rotate-left',
28490 cls : 'btn btn-default',
28491 html : '<i class="fa fa-undo"></i>'
28497 cls : 'btn-group roo-upload-cropbox-picture',
28498 action : 'picture',
28502 cls : 'btn btn-default',
28503 html : '<i class="fa fa-picture-o"></i>'
28509 cls : 'btn-group roo-upload-cropbox-rotate-right',
28510 action : 'rotate-right',
28514 cls : 'btn btn-default',
28515 html : '<i class="fa fa-repeat"></i>'
28523 cls : 'btn-group roo-upload-cropbox-rotate-left',
28524 action : 'rotate-left',
28528 cls : 'btn btn-default',
28529 html : '<i class="fa fa-undo"></i>'
28535 cls : 'btn-group roo-upload-cropbox-download',
28536 action : 'download',
28540 cls : 'btn btn-default',
28541 html : '<i class="fa fa-download"></i>'
28547 cls : 'btn-group roo-upload-cropbox-crop',
28552 cls : 'btn btn-default',
28553 html : '<i class="fa fa-crop"></i>'
28559 cls : 'btn-group roo-upload-cropbox-trash',
28564 cls : 'btn btn-default',
28565 html : '<i class="fa fa-trash"></i>'
28571 cls : 'btn-group roo-upload-cropbox-rotate-right',
28572 action : 'rotate-right',
28576 cls : 'btn btn-default',
28577 html : '<i class="fa fa-repeat"></i>'
28585 cls : 'btn-group roo-upload-cropbox-rotate-left',
28586 action : 'rotate-left',
28590 cls : 'btn btn-default',
28591 html : '<i class="fa fa-undo"></i>'
28597 cls : 'btn-group roo-upload-cropbox-rotate-right',
28598 action : 'rotate-right',
28602 cls : 'btn btn-default',
28603 html : '<i class="fa fa-repeat"></i>'
28616 * @class Roo.bootstrap.DocumentManager
28617 * @extends Roo.bootstrap.Component
28618 * Bootstrap DocumentManager class
28619 * @cfg {String} paramName default 'imageUpload'
28620 * @cfg {String} toolTipName default 'filename'
28621 * @cfg {String} method default POST
28622 * @cfg {String} url action url
28623 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28624 * @cfg {Boolean} multiple multiple upload default true
28625 * @cfg {Number} thumbSize default 300
28626 * @cfg {String} fieldLabel
28627 * @cfg {Number} labelWidth default 4
28628 * @cfg {String} labelAlign (left|top) default left
28629 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28630 * @cfg {Number} labellg set the width of label (1-12)
28631 * @cfg {Number} labelmd set the width of label (1-12)
28632 * @cfg {Number} labelsm set the width of label (1-12)
28633 * @cfg {Number} labelxs set the width of label (1-12)
28636 * Create a new DocumentManager
28637 * @param {Object} config The config object
28640 Roo.bootstrap.DocumentManager = function(config){
28641 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28644 this.delegates = [];
28649 * Fire when initial the DocumentManager
28650 * @param {Roo.bootstrap.DocumentManager} this
28655 * inspect selected file
28656 * @param {Roo.bootstrap.DocumentManager} this
28657 * @param {File} file
28662 * Fire when xhr load exception
28663 * @param {Roo.bootstrap.DocumentManager} this
28664 * @param {XMLHttpRequest} xhr
28666 "exception" : true,
28668 * @event afterupload
28669 * Fire when xhr load exception
28670 * @param {Roo.bootstrap.DocumentManager} this
28671 * @param {XMLHttpRequest} xhr
28673 "afterupload" : true,
28676 * prepare the form data
28677 * @param {Roo.bootstrap.DocumentManager} this
28678 * @param {Object} formData
28683 * Fire when remove the file
28684 * @param {Roo.bootstrap.DocumentManager} this
28685 * @param {Object} file
28690 * Fire after refresh the file
28691 * @param {Roo.bootstrap.DocumentManager} this
28696 * Fire after click the image
28697 * @param {Roo.bootstrap.DocumentManager} this
28698 * @param {Object} file
28703 * Fire when upload a image and editable set to true
28704 * @param {Roo.bootstrap.DocumentManager} this
28705 * @param {Object} file
28709 * @event beforeselectfile
28710 * Fire before select file
28711 * @param {Roo.bootstrap.DocumentManager} this
28713 "beforeselectfile" : true,
28716 * Fire before process file
28717 * @param {Roo.bootstrap.DocumentManager} this
28718 * @param {Object} file
28722 * @event previewrendered
28723 * Fire when preview rendered
28724 * @param {Roo.bootstrap.DocumentManager} this
28725 * @param {Object} file
28727 "previewrendered" : true,
28730 "previewResize" : true
28735 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28744 paramName : 'imageUpload',
28745 toolTipName : 'filename',
28748 labelAlign : 'left',
28758 getAutoCreate : function()
28760 var managerWidget = {
28762 cls : 'roo-document-manager',
28766 cls : 'roo-document-manager-selector',
28771 cls : 'roo-document-manager-uploader',
28775 cls : 'roo-document-manager-upload-btn',
28776 html : '<i class="fa fa-plus"></i>'
28787 cls : 'column col-md-12',
28792 if(this.fieldLabel.length){
28797 cls : 'column col-md-12',
28798 html : this.fieldLabel
28802 cls : 'column col-md-12',
28807 if(this.labelAlign == 'left'){
28812 html : this.fieldLabel
28821 if(this.labelWidth > 12){
28822 content[0].style = "width: " + this.labelWidth + 'px';
28825 if(this.labelWidth < 13 && this.labelmd == 0){
28826 this.labelmd = this.labelWidth;
28829 if(this.labellg > 0){
28830 content[0].cls += ' col-lg-' + this.labellg;
28831 content[1].cls += ' col-lg-' + (12 - this.labellg);
28834 if(this.labelmd > 0){
28835 content[0].cls += ' col-md-' + this.labelmd;
28836 content[1].cls += ' col-md-' + (12 - this.labelmd);
28839 if(this.labelsm > 0){
28840 content[0].cls += ' col-sm-' + this.labelsm;
28841 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28844 if(this.labelxs > 0){
28845 content[0].cls += ' col-xs-' + this.labelxs;
28846 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28854 cls : 'row clearfix',
28862 initEvents : function()
28864 this.managerEl = this.el.select('.roo-document-manager', true).first();
28865 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28867 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28868 this.selectorEl.hide();
28871 this.selectorEl.attr('multiple', 'multiple');
28874 this.selectorEl.on('change', this.onFileSelected, this);
28876 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28877 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28879 this.uploader.on('click', this.onUploaderClick, this);
28881 this.renderProgressDialog();
28885 window.addEventListener("resize", function() { _this.refresh(); } );
28887 this.fireEvent('initial', this);
28890 renderProgressDialog : function()
28894 this.progressDialog = new Roo.bootstrap.Modal({
28895 cls : 'roo-document-manager-progress-dialog',
28896 allow_close : false,
28906 btnclick : function() {
28907 _this.uploadCancel();
28913 this.progressDialog.render(Roo.get(document.body));
28915 this.progress = new Roo.bootstrap.Progress({
28916 cls : 'roo-document-manager-progress',
28921 this.progress.render(this.progressDialog.getChildContainer());
28923 this.progressBar = new Roo.bootstrap.ProgressBar({
28924 cls : 'roo-document-manager-progress-bar',
28927 aria_valuemax : 12,
28931 this.progressBar.render(this.progress.getChildContainer());
28934 onUploaderClick : function(e)
28936 e.preventDefault();
28938 if(this.fireEvent('beforeselectfile', this) != false){
28939 this.selectorEl.dom.click();
28944 onFileSelected : function(e)
28946 e.preventDefault();
28948 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28952 Roo.each(this.selectorEl.dom.files, function(file){
28953 if(this.fireEvent('inspect', this, file) != false){
28954 this.files.push(file);
28964 this.selectorEl.dom.value = '';
28966 if(!this.files || !this.files.length){
28970 if(this.boxes > 0 && this.files.length > this.boxes){
28971 this.files = this.files.slice(0, this.boxes);
28974 this.uploader.show();
28976 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28977 this.uploader.hide();
28986 Roo.each(this.files, function(file){
28988 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28989 var f = this.renderPreview(file);
28994 if(file.type.indexOf('image') != -1){
28995 this.delegates.push(
28997 _this.process(file);
28998 }).createDelegate(this)
29006 _this.process(file);
29007 }).createDelegate(this)
29012 this.files = files;
29014 this.delegates = this.delegates.concat(docs);
29016 if(!this.delegates.length){
29021 this.progressBar.aria_valuemax = this.delegates.length;
29028 arrange : function()
29030 if(!this.delegates.length){
29031 this.progressDialog.hide();
29036 var delegate = this.delegates.shift();
29038 this.progressDialog.show();
29040 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29042 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29047 refresh : function()
29049 this.uploader.show();
29051 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29052 this.uploader.hide();
29055 Roo.isTouch ? this.closable(false) : this.closable(true);
29057 this.fireEvent('refresh', this);
29060 onRemove : function(e, el, o)
29062 e.preventDefault();
29064 this.fireEvent('remove', this, o);
29068 remove : function(o)
29072 Roo.each(this.files, function(file){
29073 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29082 this.files = files;
29089 Roo.each(this.files, function(file){
29094 file.target.remove();
29103 onClick : function(e, el, o)
29105 e.preventDefault();
29107 this.fireEvent('click', this, o);
29111 closable : function(closable)
29113 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29115 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29127 xhrOnLoad : function(xhr)
29129 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29133 if (xhr.readyState !== 4) {
29135 this.fireEvent('exception', this, xhr);
29139 var response = Roo.decode(xhr.responseText);
29141 if(!response.success){
29143 this.fireEvent('exception', this, xhr);
29147 var file = this.renderPreview(response.data);
29149 this.files.push(file);
29153 this.fireEvent('afterupload', this, xhr);
29157 xhrOnError : function(xhr)
29159 Roo.log('xhr on error');
29161 var response = Roo.decode(xhr.responseText);
29168 process : function(file)
29170 if(this.fireEvent('process', this, file) !== false){
29171 if(this.editable && file.type.indexOf('image') != -1){
29172 this.fireEvent('edit', this, file);
29176 this.uploadStart(file, false);
29183 uploadStart : function(file, crop)
29185 this.xhr = new XMLHttpRequest();
29187 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29192 file.xhr = this.xhr;
29194 this.managerEl.createChild({
29196 cls : 'roo-document-manager-loading',
29200 tooltip : file.name,
29201 cls : 'roo-document-manager-thumb',
29202 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29208 this.xhr.open(this.method, this.url, true);
29211 "Accept": "application/json",
29212 "Cache-Control": "no-cache",
29213 "X-Requested-With": "XMLHttpRequest"
29216 for (var headerName in headers) {
29217 var headerValue = headers[headerName];
29219 this.xhr.setRequestHeader(headerName, headerValue);
29225 this.xhr.onload = function()
29227 _this.xhrOnLoad(_this.xhr);
29230 this.xhr.onerror = function()
29232 _this.xhrOnError(_this.xhr);
29235 var formData = new FormData();
29237 formData.append('returnHTML', 'NO');
29240 formData.append('crop', crop);
29243 formData.append(this.paramName, file, file.name);
29250 if(this.fireEvent('prepare', this, formData, options) != false){
29252 if(options.manually){
29256 this.xhr.send(formData);
29260 this.uploadCancel();
29263 uploadCancel : function()
29269 this.delegates = [];
29271 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29278 renderPreview : function(file)
29280 if(typeof(file.target) != 'undefined' && file.target){
29284 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29286 var previewEl = this.managerEl.createChild({
29288 cls : 'roo-document-manager-preview',
29292 tooltip : file[this.toolTipName],
29293 cls : 'roo-document-manager-thumb',
29294 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29299 html : '<i class="fa fa-times-circle"></i>'
29304 var close = previewEl.select('button.close', true).first();
29306 close.on('click', this.onRemove, this, file);
29308 file.target = previewEl;
29310 var image = previewEl.select('img', true).first();
29314 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29316 image.on('click', this.onClick, this, file);
29318 this.fireEvent('previewrendered', this, file);
29324 onPreviewLoad : function(file, image)
29326 if(typeof(file.target) == 'undefined' || !file.target){
29330 var width = image.dom.naturalWidth || image.dom.width;
29331 var height = image.dom.naturalHeight || image.dom.height;
29333 if(!this.previewResize) {
29337 if(width > height){
29338 file.target.addClass('wide');
29342 file.target.addClass('tall');
29347 uploadFromSource : function(file, crop)
29349 this.xhr = new XMLHttpRequest();
29351 this.managerEl.createChild({
29353 cls : 'roo-document-manager-loading',
29357 tooltip : file.name,
29358 cls : 'roo-document-manager-thumb',
29359 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29365 this.xhr.open(this.method, this.url, true);
29368 "Accept": "application/json",
29369 "Cache-Control": "no-cache",
29370 "X-Requested-With": "XMLHttpRequest"
29373 for (var headerName in headers) {
29374 var headerValue = headers[headerName];
29376 this.xhr.setRequestHeader(headerName, headerValue);
29382 this.xhr.onload = function()
29384 _this.xhrOnLoad(_this.xhr);
29387 this.xhr.onerror = function()
29389 _this.xhrOnError(_this.xhr);
29392 var formData = new FormData();
29394 formData.append('returnHTML', 'NO');
29396 formData.append('crop', crop);
29398 if(typeof(file.filename) != 'undefined'){
29399 formData.append('filename', file.filename);
29402 if(typeof(file.mimetype) != 'undefined'){
29403 formData.append('mimetype', file.mimetype);
29408 if(this.fireEvent('prepare', this, formData) != false){
29409 this.xhr.send(formData);
29419 * @class Roo.bootstrap.DocumentViewer
29420 * @extends Roo.bootstrap.Component
29421 * Bootstrap DocumentViewer class
29422 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29423 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29426 * Create a new DocumentViewer
29427 * @param {Object} config The config object
29430 Roo.bootstrap.DocumentViewer = function(config){
29431 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29436 * Fire after initEvent
29437 * @param {Roo.bootstrap.DocumentViewer} this
29443 * @param {Roo.bootstrap.DocumentViewer} this
29448 * Fire after download button
29449 * @param {Roo.bootstrap.DocumentViewer} this
29454 * Fire after trash button
29455 * @param {Roo.bootstrap.DocumentViewer} this
29462 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29464 showDownload : true,
29468 getAutoCreate : function()
29472 cls : 'roo-document-viewer',
29476 cls : 'roo-document-viewer-body',
29480 cls : 'roo-document-viewer-thumb',
29484 cls : 'roo-document-viewer-image'
29492 cls : 'roo-document-viewer-footer',
29495 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29499 cls : 'btn-group roo-document-viewer-download',
29503 cls : 'btn btn-default',
29504 html : '<i class="fa fa-download"></i>'
29510 cls : 'btn-group roo-document-viewer-trash',
29514 cls : 'btn btn-default',
29515 html : '<i class="fa fa-trash"></i>'
29528 initEvents : function()
29530 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29531 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29533 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29534 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29536 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29537 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29539 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29540 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29542 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29543 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29545 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29546 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29548 this.bodyEl.on('click', this.onClick, this);
29549 this.downloadBtn.on('click', this.onDownload, this);
29550 this.trashBtn.on('click', this.onTrash, this);
29552 this.downloadBtn.hide();
29553 this.trashBtn.hide();
29555 if(this.showDownload){
29556 this.downloadBtn.show();
29559 if(this.showTrash){
29560 this.trashBtn.show();
29563 if(!this.showDownload && !this.showTrash) {
29564 this.footerEl.hide();
29569 initial : function()
29571 this.fireEvent('initial', this);
29575 onClick : function(e)
29577 e.preventDefault();
29579 this.fireEvent('click', this);
29582 onDownload : function(e)
29584 e.preventDefault();
29586 this.fireEvent('download', this);
29589 onTrash : function(e)
29591 e.preventDefault();
29593 this.fireEvent('trash', this);
29605 * @class Roo.bootstrap.NavProgressBar
29606 * @extends Roo.bootstrap.Component
29607 * Bootstrap NavProgressBar class
29610 * Create a new nav progress bar
29611 * @param {Object} config The config object
29614 Roo.bootstrap.NavProgressBar = function(config){
29615 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29617 this.bullets = this.bullets || [];
29619 // Roo.bootstrap.NavProgressBar.register(this);
29623 * Fires when the active item changes
29624 * @param {Roo.bootstrap.NavProgressBar} this
29625 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29626 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29633 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29638 getAutoCreate : function()
29640 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29644 cls : 'roo-navigation-bar-group',
29648 cls : 'roo-navigation-top-bar'
29652 cls : 'roo-navigation-bullets-bar',
29656 cls : 'roo-navigation-bar'
29663 cls : 'roo-navigation-bottom-bar'
29673 initEvents: function()
29678 onRender : function(ct, position)
29680 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29682 if(this.bullets.length){
29683 Roo.each(this.bullets, function(b){
29692 addItem : function(cfg)
29694 var item = new Roo.bootstrap.NavProgressItem(cfg);
29696 item.parentId = this.id;
29697 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29700 var top = new Roo.bootstrap.Element({
29702 cls : 'roo-navigation-bar-text'
29705 var bottom = new Roo.bootstrap.Element({
29707 cls : 'roo-navigation-bar-text'
29710 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29711 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29713 var topText = new Roo.bootstrap.Element({
29715 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29718 var bottomText = new Roo.bootstrap.Element({
29720 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29723 topText.onRender(top.el, null);
29724 bottomText.onRender(bottom.el, null);
29727 item.bottomEl = bottom;
29730 this.barItems.push(item);
29735 getActive : function()
29737 var active = false;
29739 Roo.each(this.barItems, function(v){
29741 if (!v.isActive()) {
29753 setActiveItem : function(item)
29757 Roo.each(this.barItems, function(v){
29758 if (v.rid == item.rid) {
29762 if (v.isActive()) {
29763 v.setActive(false);
29768 item.setActive(true);
29770 this.fireEvent('changed', this, item, prev);
29773 getBarItem: function(rid)
29777 Roo.each(this.barItems, function(e) {
29778 if (e.rid != rid) {
29789 indexOfItem : function(item)
29793 Roo.each(this.barItems, function(v, i){
29795 if (v.rid != item.rid) {
29806 setActiveNext : function()
29808 var i = this.indexOfItem(this.getActive());
29810 if (i > this.barItems.length) {
29814 this.setActiveItem(this.barItems[i+1]);
29817 setActivePrev : function()
29819 var i = this.indexOfItem(this.getActive());
29825 this.setActiveItem(this.barItems[i-1]);
29828 format : function()
29830 if(!this.barItems.length){
29834 var width = 100 / this.barItems.length;
29836 Roo.each(this.barItems, function(i){
29837 i.el.setStyle('width', width + '%');
29838 i.topEl.el.setStyle('width', width + '%');
29839 i.bottomEl.el.setStyle('width', width + '%');
29848 * Nav Progress Item
29853 * @class Roo.bootstrap.NavProgressItem
29854 * @extends Roo.bootstrap.Component
29855 * Bootstrap NavProgressItem class
29856 * @cfg {String} rid the reference id
29857 * @cfg {Boolean} active (true|false) Is item active default false
29858 * @cfg {Boolean} disabled (true|false) Is item active default false
29859 * @cfg {String} html
29860 * @cfg {String} position (top|bottom) text position default bottom
29861 * @cfg {String} icon show icon instead of number
29864 * Create a new NavProgressItem
29865 * @param {Object} config The config object
29867 Roo.bootstrap.NavProgressItem = function(config){
29868 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29873 * The raw click event for the entire grid.
29874 * @param {Roo.bootstrap.NavProgressItem} this
29875 * @param {Roo.EventObject} e
29882 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29888 position : 'bottom',
29891 getAutoCreate : function()
29893 var iconCls = 'roo-navigation-bar-item-icon';
29895 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29899 cls: 'roo-navigation-bar-item',
29909 cfg.cls += ' active';
29912 cfg.cls += ' disabled';
29918 disable : function()
29920 this.setDisabled(true);
29923 enable : function()
29925 this.setDisabled(false);
29928 initEvents: function()
29930 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29932 this.iconEl.on('click', this.onClick, this);
29935 onClick : function(e)
29937 e.preventDefault();
29943 if(this.fireEvent('click', this, e) === false){
29947 this.parent().setActiveItem(this);
29950 isActive: function ()
29952 return this.active;
29955 setActive : function(state)
29957 if(this.active == state){
29961 this.active = state;
29964 this.el.addClass('active');
29968 this.el.removeClass('active');
29973 setDisabled : function(state)
29975 if(this.disabled == state){
29979 this.disabled = state;
29982 this.el.addClass('disabled');
29986 this.el.removeClass('disabled');
29989 tooltipEl : function()
29991 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30004 * @class Roo.bootstrap.FieldLabel
30005 * @extends Roo.bootstrap.Component
30006 * Bootstrap FieldLabel class
30007 * @cfg {String} html contents of the element
30008 * @cfg {String} tag tag of the element default label
30009 * @cfg {String} cls class of the element
30010 * @cfg {String} target label target
30011 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30012 * @cfg {String} invalidClass default "text-warning"
30013 * @cfg {String} validClass default "text-success"
30014 * @cfg {String} iconTooltip default "This field is required"
30015 * @cfg {String} indicatorpos (left|right) default left
30018 * Create a new FieldLabel
30019 * @param {Object} config The config object
30022 Roo.bootstrap.FieldLabel = function(config){
30023 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30028 * Fires after the field has been marked as invalid.
30029 * @param {Roo.form.FieldLabel} this
30030 * @param {String} msg The validation message
30035 * Fires after the field has been validated with no errors.
30036 * @param {Roo.form.FieldLabel} this
30042 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30049 invalidClass : 'has-warning',
30050 validClass : 'has-success',
30051 iconTooltip : 'This field is required',
30052 indicatorpos : 'left',
30054 getAutoCreate : function(){
30057 if (!this.allowBlank) {
30063 cls : 'roo-bootstrap-field-label ' + this.cls,
30068 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30069 tooltip : this.iconTooltip
30078 if(this.indicatorpos == 'right'){
30081 cls : 'roo-bootstrap-field-label ' + this.cls,
30090 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30091 tooltip : this.iconTooltip
30100 initEvents: function()
30102 Roo.bootstrap.Element.superclass.initEvents.call(this);
30104 this.indicator = this.indicatorEl();
30106 if(this.indicator){
30107 this.indicator.removeClass('visible');
30108 this.indicator.addClass('invisible');
30111 Roo.bootstrap.FieldLabel.register(this);
30114 indicatorEl : function()
30116 var indicator = this.el.select('i.roo-required-indicator',true).first();
30127 * Mark this field as valid
30129 markValid : function()
30131 if(this.indicator){
30132 this.indicator.removeClass('visible');
30133 this.indicator.addClass('invisible');
30136 this.el.removeClass(this.invalidClass);
30138 this.el.addClass(this.validClass);
30140 this.fireEvent('valid', this);
30144 * Mark this field as invalid
30145 * @param {String} msg The validation message
30147 markInvalid : function(msg)
30149 if(this.indicator){
30150 this.indicator.removeClass('invisible');
30151 this.indicator.addClass('visible');
30154 this.el.removeClass(this.validClass);
30156 this.el.addClass(this.invalidClass);
30158 this.fireEvent('invalid', this, msg);
30164 Roo.apply(Roo.bootstrap.FieldLabel, {
30169 * register a FieldLabel Group
30170 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30172 register : function(label)
30174 if(this.groups.hasOwnProperty(label.target)){
30178 this.groups[label.target] = label;
30182 * fetch a FieldLabel Group based on the target
30183 * @param {string} target
30184 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30186 get: function(target) {
30187 if (typeof(this.groups[target]) == 'undefined') {
30191 return this.groups[target] ;
30200 * page DateSplitField.
30206 * @class Roo.bootstrap.DateSplitField
30207 * @extends Roo.bootstrap.Component
30208 * Bootstrap DateSplitField class
30209 * @cfg {string} fieldLabel - the label associated
30210 * @cfg {Number} labelWidth set the width of label (0-12)
30211 * @cfg {String} labelAlign (top|left)
30212 * @cfg {Boolean} dayAllowBlank (true|false) default false
30213 * @cfg {Boolean} monthAllowBlank (true|false) default false
30214 * @cfg {Boolean} yearAllowBlank (true|false) default false
30215 * @cfg {string} dayPlaceholder
30216 * @cfg {string} monthPlaceholder
30217 * @cfg {string} yearPlaceholder
30218 * @cfg {string} dayFormat default 'd'
30219 * @cfg {string} monthFormat default 'm'
30220 * @cfg {string} yearFormat default 'Y'
30221 * @cfg {Number} labellg set the width of label (1-12)
30222 * @cfg {Number} labelmd set the width of label (1-12)
30223 * @cfg {Number} labelsm set the width of label (1-12)
30224 * @cfg {Number} labelxs set the width of label (1-12)
30228 * Create a new DateSplitField
30229 * @param {Object} config The config object
30232 Roo.bootstrap.DateSplitField = function(config){
30233 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30239 * getting the data of years
30240 * @param {Roo.bootstrap.DateSplitField} this
30241 * @param {Object} years
30246 * getting the data of days
30247 * @param {Roo.bootstrap.DateSplitField} this
30248 * @param {Object} days
30253 * Fires after the field has been marked as invalid.
30254 * @param {Roo.form.Field} this
30255 * @param {String} msg The validation message
30260 * Fires after the field has been validated with no errors.
30261 * @param {Roo.form.Field} this
30267 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30270 labelAlign : 'top',
30272 dayAllowBlank : false,
30273 monthAllowBlank : false,
30274 yearAllowBlank : false,
30275 dayPlaceholder : '',
30276 monthPlaceholder : '',
30277 yearPlaceholder : '',
30281 isFormField : true,
30287 getAutoCreate : function()
30291 cls : 'row roo-date-split-field-group',
30296 cls : 'form-hidden-field roo-date-split-field-group-value',
30302 var labelCls = 'col-md-12';
30303 var contentCls = 'col-md-4';
30305 if(this.fieldLabel){
30309 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30313 html : this.fieldLabel
30318 if(this.labelAlign == 'left'){
30320 if(this.labelWidth > 12){
30321 label.style = "width: " + this.labelWidth + 'px';
30324 if(this.labelWidth < 13 && this.labelmd == 0){
30325 this.labelmd = this.labelWidth;
30328 if(this.labellg > 0){
30329 labelCls = ' col-lg-' + this.labellg;
30330 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30333 if(this.labelmd > 0){
30334 labelCls = ' col-md-' + this.labelmd;
30335 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30338 if(this.labelsm > 0){
30339 labelCls = ' col-sm-' + this.labelsm;
30340 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30343 if(this.labelxs > 0){
30344 labelCls = ' col-xs-' + this.labelxs;
30345 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30349 label.cls += ' ' + labelCls;
30351 cfg.cn.push(label);
30354 Roo.each(['day', 'month', 'year'], function(t){
30357 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30364 inputEl: function ()
30366 return this.el.select('.roo-date-split-field-group-value', true).first();
30369 onRender : function(ct, position)
30373 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30375 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30377 this.dayField = new Roo.bootstrap.ComboBox({
30378 allowBlank : this.dayAllowBlank,
30379 alwaysQuery : true,
30380 displayField : 'value',
30383 forceSelection : true,
30385 placeholder : this.dayPlaceholder,
30386 selectOnFocus : true,
30387 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30388 triggerAction : 'all',
30390 valueField : 'value',
30391 store : new Roo.data.SimpleStore({
30392 data : (function() {
30394 _this.fireEvent('days', _this, days);
30397 fields : [ 'value' ]
30400 select : function (_self, record, index)
30402 _this.setValue(_this.getValue());
30407 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30409 this.monthField = new Roo.bootstrap.MonthField({
30410 after : '<i class=\"fa fa-calendar\"></i>',
30411 allowBlank : this.monthAllowBlank,
30412 placeholder : this.monthPlaceholder,
30415 render : function (_self)
30417 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30418 e.preventDefault();
30422 select : function (_self, oldvalue, newvalue)
30424 _this.setValue(_this.getValue());
30429 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30431 this.yearField = new Roo.bootstrap.ComboBox({
30432 allowBlank : this.yearAllowBlank,
30433 alwaysQuery : true,
30434 displayField : 'value',
30437 forceSelection : true,
30439 placeholder : this.yearPlaceholder,
30440 selectOnFocus : true,
30441 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30442 triggerAction : 'all',
30444 valueField : 'value',
30445 store : new Roo.data.SimpleStore({
30446 data : (function() {
30448 _this.fireEvent('years', _this, years);
30451 fields : [ 'value' ]
30454 select : function (_self, record, index)
30456 _this.setValue(_this.getValue());
30461 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30464 setValue : function(v, format)
30466 this.inputEl.dom.value = v;
30468 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30470 var d = Date.parseDate(v, f);
30477 this.setDay(d.format(this.dayFormat));
30478 this.setMonth(d.format(this.monthFormat));
30479 this.setYear(d.format(this.yearFormat));
30486 setDay : function(v)
30488 this.dayField.setValue(v);
30489 this.inputEl.dom.value = this.getValue();
30494 setMonth : function(v)
30496 this.monthField.setValue(v, true);
30497 this.inputEl.dom.value = this.getValue();
30502 setYear : function(v)
30504 this.yearField.setValue(v);
30505 this.inputEl.dom.value = this.getValue();
30510 getDay : function()
30512 return this.dayField.getValue();
30515 getMonth : function()
30517 return this.monthField.getValue();
30520 getYear : function()
30522 return this.yearField.getValue();
30525 getValue : function()
30527 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30529 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30539 this.inputEl.dom.value = '';
30544 validate : function()
30546 var d = this.dayField.validate();
30547 var m = this.monthField.validate();
30548 var y = this.yearField.validate();
30553 (!this.dayAllowBlank && !d) ||
30554 (!this.monthAllowBlank && !m) ||
30555 (!this.yearAllowBlank && !y)
30560 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30569 this.markInvalid();
30574 markValid : function()
30577 var label = this.el.select('label', true).first();
30578 var icon = this.el.select('i.fa-star', true).first();
30584 this.fireEvent('valid', this);
30588 * Mark this field as invalid
30589 * @param {String} msg The validation message
30591 markInvalid : function(msg)
30594 var label = this.el.select('label', true).first();
30595 var icon = this.el.select('i.fa-star', true).first();
30597 if(label && !icon){
30598 this.el.select('.roo-date-split-field-label', true).createChild({
30600 cls : 'text-danger fa fa-lg fa-star',
30601 tooltip : 'This field is required',
30602 style : 'margin-right:5px;'
30606 this.fireEvent('invalid', this, msg);
30609 clearInvalid : function()
30611 var label = this.el.select('label', true).first();
30612 var icon = this.el.select('i.fa-star', true).first();
30618 this.fireEvent('valid', this);
30621 getName: function()
30631 * http://masonry.desandro.com
30633 * The idea is to render all the bricks based on vertical width...
30635 * The original code extends 'outlayer' - we might need to use that....
30641 * @class Roo.bootstrap.LayoutMasonry
30642 * @extends Roo.bootstrap.Component
30643 * Bootstrap Layout Masonry class
30646 * Create a new Element
30647 * @param {Object} config The config object
30650 Roo.bootstrap.LayoutMasonry = function(config){
30652 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30656 Roo.bootstrap.LayoutMasonry.register(this);
30662 * Fire after layout the items
30663 * @param {Roo.bootstrap.LayoutMasonry} this
30664 * @param {Roo.EventObject} e
30671 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30674 * @cfg {Boolean} isLayoutInstant = no animation?
30676 isLayoutInstant : false, // needed?
30679 * @cfg {Number} boxWidth width of the columns
30684 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30689 * @cfg {Number} padWidth padding below box..
30694 * @cfg {Number} gutter gutter width..
30699 * @cfg {Number} maxCols maximum number of columns
30705 * @cfg {Boolean} isAutoInitial defalut true
30707 isAutoInitial : true,
30712 * @cfg {Boolean} isHorizontal defalut false
30714 isHorizontal : false,
30716 currentSize : null,
30722 bricks: null, //CompositeElement
30726 _isLayoutInited : false,
30728 // isAlternative : false, // only use for vertical layout...
30731 * @cfg {Number} alternativePadWidth padding below box..
30733 alternativePadWidth : 50,
30735 selectedBrick : [],
30737 getAutoCreate : function(){
30739 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30743 cls: 'blog-masonary-wrapper ' + this.cls,
30745 cls : 'mas-boxes masonary'
30752 getChildContainer: function( )
30754 if (this.boxesEl) {
30755 return this.boxesEl;
30758 this.boxesEl = this.el.select('.mas-boxes').first();
30760 return this.boxesEl;
30764 initEvents : function()
30768 if(this.isAutoInitial){
30769 Roo.log('hook children rendered');
30770 this.on('childrenrendered', function() {
30771 Roo.log('children rendered');
30777 initial : function()
30779 this.selectedBrick = [];
30781 this.currentSize = this.el.getBox(true);
30783 Roo.EventManager.onWindowResize(this.resize, this);
30785 if(!this.isAutoInitial){
30793 //this.layout.defer(500,this);
30797 resize : function()
30799 var cs = this.el.getBox(true);
30802 this.currentSize.width == cs.width &&
30803 this.currentSize.x == cs.x &&
30804 this.currentSize.height == cs.height &&
30805 this.currentSize.y == cs.y
30807 Roo.log("no change in with or X or Y");
30811 this.currentSize = cs;
30817 layout : function()
30819 this._resetLayout();
30821 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30823 this.layoutItems( isInstant );
30825 this._isLayoutInited = true;
30827 this.fireEvent('layout', this);
30831 _resetLayout : function()
30833 if(this.isHorizontal){
30834 this.horizontalMeasureColumns();
30838 this.verticalMeasureColumns();
30842 verticalMeasureColumns : function()
30844 this.getContainerWidth();
30846 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30847 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30851 var boxWidth = this.boxWidth + this.padWidth;
30853 if(this.containerWidth < this.boxWidth){
30854 boxWidth = this.containerWidth
30857 var containerWidth = this.containerWidth;
30859 var cols = Math.floor(containerWidth / boxWidth);
30861 this.cols = Math.max( cols, 1 );
30863 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30865 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30867 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30869 this.colWidth = boxWidth + avail - this.padWidth;
30871 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30872 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30875 horizontalMeasureColumns : function()
30877 this.getContainerWidth();
30879 var boxWidth = this.boxWidth;
30881 if(this.containerWidth < boxWidth){
30882 boxWidth = this.containerWidth;
30885 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30887 this.el.setHeight(boxWidth);
30891 getContainerWidth : function()
30893 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30896 layoutItems : function( isInstant )
30898 Roo.log(this.bricks);
30900 var items = Roo.apply([], this.bricks);
30902 if(this.isHorizontal){
30903 this._horizontalLayoutItems( items , isInstant );
30907 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30908 // this._verticalAlternativeLayoutItems( items , isInstant );
30912 this._verticalLayoutItems( items , isInstant );
30916 _verticalLayoutItems : function ( items , isInstant)
30918 if ( !items || !items.length ) {
30923 ['xs', 'xs', 'xs', 'tall'],
30924 ['xs', 'xs', 'tall'],
30925 ['xs', 'xs', 'sm'],
30926 ['xs', 'xs', 'xs'],
30932 ['sm', 'xs', 'xs'],
30936 ['tall', 'xs', 'xs', 'xs'],
30937 ['tall', 'xs', 'xs'],
30949 Roo.each(items, function(item, k){
30951 switch (item.size) {
30952 // these layouts take up a full box,
30963 boxes.push([item]);
30986 var filterPattern = function(box, length)
30994 var pattern = box.slice(0, length);
30998 Roo.each(pattern, function(i){
30999 format.push(i.size);
31002 Roo.each(standard, function(s){
31004 if(String(s) != String(format)){
31013 if(!match && length == 1){
31018 filterPattern(box, length - 1);
31022 queue.push(pattern);
31024 box = box.slice(length, box.length);
31026 filterPattern(box, 4);
31032 Roo.each(boxes, function(box, k){
31038 if(box.length == 1){
31043 filterPattern(box, 4);
31047 this._processVerticalLayoutQueue( queue, isInstant );
31051 // _verticalAlternativeLayoutItems : function( items , isInstant )
31053 // if ( !items || !items.length ) {
31057 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31061 _horizontalLayoutItems : function ( items , isInstant)
31063 if ( !items || !items.length || items.length < 3) {
31069 var eItems = items.slice(0, 3);
31071 items = items.slice(3, items.length);
31074 ['xs', 'xs', 'xs', 'wide'],
31075 ['xs', 'xs', 'wide'],
31076 ['xs', 'xs', 'sm'],
31077 ['xs', 'xs', 'xs'],
31083 ['sm', 'xs', 'xs'],
31087 ['wide', 'xs', 'xs', 'xs'],
31088 ['wide', 'xs', 'xs'],
31101 Roo.each(items, function(item, k){
31103 switch (item.size) {
31114 boxes.push([item]);
31138 var filterPattern = function(box, length)
31146 var pattern = box.slice(0, length);
31150 Roo.each(pattern, function(i){
31151 format.push(i.size);
31154 Roo.each(standard, function(s){
31156 if(String(s) != String(format)){
31165 if(!match && length == 1){
31170 filterPattern(box, length - 1);
31174 queue.push(pattern);
31176 box = box.slice(length, box.length);
31178 filterPattern(box, 4);
31184 Roo.each(boxes, function(box, k){
31190 if(box.length == 1){
31195 filterPattern(box, 4);
31202 var pos = this.el.getBox(true);
31206 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31208 var hit_end = false;
31210 Roo.each(queue, function(box){
31214 Roo.each(box, function(b){
31216 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31226 Roo.each(box, function(b){
31228 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31231 mx = Math.max(mx, b.x);
31235 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31239 Roo.each(box, function(b){
31241 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31255 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31258 /** Sets position of item in DOM
31259 * @param {Element} item
31260 * @param {Number} x - horizontal position
31261 * @param {Number} y - vertical position
31262 * @param {Boolean} isInstant - disables transitions
31264 _processVerticalLayoutQueue : function( queue, isInstant )
31266 var pos = this.el.getBox(true);
31271 for (var i = 0; i < this.cols; i++){
31275 Roo.each(queue, function(box, k){
31277 var col = k % this.cols;
31279 Roo.each(box, function(b,kk){
31281 b.el.position('absolute');
31283 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31284 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31286 if(b.size == 'md-left' || b.size == 'md-right'){
31287 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31288 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31291 b.el.setWidth(width);
31292 b.el.setHeight(height);
31294 b.el.select('iframe',true).setSize(width,height);
31298 for (var i = 0; i < this.cols; i++){
31300 if(maxY[i] < maxY[col]){
31305 col = Math.min(col, i);
31309 x = pos.x + col * (this.colWidth + this.padWidth);
31313 var positions = [];
31315 switch (box.length){
31317 positions = this.getVerticalOneBoxColPositions(x, y, box);
31320 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31323 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31326 positions = this.getVerticalFourBoxColPositions(x, y, box);
31332 Roo.each(box, function(b,kk){
31334 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31336 var sz = b.el.getSize();
31338 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31346 for (var i = 0; i < this.cols; i++){
31347 mY = Math.max(mY, maxY[i]);
31350 this.el.setHeight(mY - pos.y);
31354 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31356 // var pos = this.el.getBox(true);
31359 // var maxX = pos.right;
31361 // var maxHeight = 0;
31363 // Roo.each(items, function(item, k){
31367 // item.el.position('absolute');
31369 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31371 // item.el.setWidth(width);
31373 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31375 // item.el.setHeight(height);
31378 // item.el.setXY([x, y], isInstant ? false : true);
31380 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31383 // y = y + height + this.alternativePadWidth;
31385 // maxHeight = maxHeight + height + this.alternativePadWidth;
31389 // this.el.setHeight(maxHeight);
31393 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31395 var pos = this.el.getBox(true);
31400 var maxX = pos.right;
31402 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31404 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31406 Roo.each(queue, function(box, k){
31408 Roo.each(box, function(b, kk){
31410 b.el.position('absolute');
31412 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31413 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31415 if(b.size == 'md-left' || b.size == 'md-right'){
31416 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31417 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31420 b.el.setWidth(width);
31421 b.el.setHeight(height);
31429 var positions = [];
31431 switch (box.length){
31433 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31436 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31439 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31442 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31448 Roo.each(box, function(b,kk){
31450 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31452 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31460 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31462 Roo.each(eItems, function(b,k){
31464 b.size = (k == 0) ? 'sm' : 'xs';
31465 b.x = (k == 0) ? 2 : 1;
31466 b.y = (k == 0) ? 2 : 1;
31468 b.el.position('absolute');
31470 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31472 b.el.setWidth(width);
31474 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31476 b.el.setHeight(height);
31480 var positions = [];
31483 x : maxX - this.unitWidth * 2 - this.gutter,
31488 x : maxX - this.unitWidth,
31489 y : minY + (this.unitWidth + this.gutter) * 2
31493 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31497 Roo.each(eItems, function(b,k){
31499 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31505 getVerticalOneBoxColPositions : function(x, y, box)
31509 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31511 if(box[0].size == 'md-left'){
31515 if(box[0].size == 'md-right'){
31520 x : x + (this.unitWidth + this.gutter) * rand,
31527 getVerticalTwoBoxColPositions : function(x, y, box)
31531 if(box[0].size == 'xs'){
31535 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31539 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31553 x : x + (this.unitWidth + this.gutter) * 2,
31554 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31561 getVerticalThreeBoxColPositions : function(x, y, box)
31565 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31573 x : x + (this.unitWidth + this.gutter) * 1,
31578 x : x + (this.unitWidth + this.gutter) * 2,
31586 if(box[0].size == 'xs' && box[1].size == 'xs'){
31595 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31599 x : x + (this.unitWidth + this.gutter) * 1,
31613 x : x + (this.unitWidth + this.gutter) * 2,
31618 x : x + (this.unitWidth + this.gutter) * 2,
31619 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31626 getVerticalFourBoxColPositions : function(x, y, box)
31630 if(box[0].size == 'xs'){
31639 y : y + (this.unitHeight + this.gutter) * 1
31644 y : y + (this.unitHeight + this.gutter) * 2
31648 x : x + (this.unitWidth + this.gutter) * 1,
31662 x : x + (this.unitWidth + this.gutter) * 2,
31667 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31668 y : y + (this.unitHeight + this.gutter) * 1
31672 x : x + (this.unitWidth + this.gutter) * 2,
31673 y : y + (this.unitWidth + this.gutter) * 2
31680 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31684 if(box[0].size == 'md-left'){
31686 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31693 if(box[0].size == 'md-right'){
31695 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31696 y : minY + (this.unitWidth + this.gutter) * 1
31702 var rand = Math.floor(Math.random() * (4 - box[0].y));
31705 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31706 y : minY + (this.unitWidth + this.gutter) * rand
31713 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31717 if(box[0].size == 'xs'){
31720 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31725 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31726 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31734 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31739 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31740 y : minY + (this.unitWidth + this.gutter) * 2
31747 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31751 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31754 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31759 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31760 y : minY + (this.unitWidth + this.gutter) * 1
31764 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31765 y : minY + (this.unitWidth + this.gutter) * 2
31772 if(box[0].size == 'xs' && box[1].size == 'xs'){
31775 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31780 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31785 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31786 y : minY + (this.unitWidth + this.gutter) * 1
31794 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31799 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31800 y : minY + (this.unitWidth + this.gutter) * 2
31804 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31805 y : minY + (this.unitWidth + this.gutter) * 2
31812 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31816 if(box[0].size == 'xs'){
31819 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31824 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31829 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),
31834 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31835 y : minY + (this.unitWidth + this.gutter) * 1
31843 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31848 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31849 y : minY + (this.unitWidth + this.gutter) * 2
31853 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31854 y : minY + (this.unitWidth + this.gutter) * 2
31858 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),
31859 y : minY + (this.unitWidth + this.gutter) * 2
31867 * remove a Masonry Brick
31868 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31870 removeBrick : function(brick_id)
31876 for (var i = 0; i<this.bricks.length; i++) {
31877 if (this.bricks[i].id == brick_id) {
31878 this.bricks.splice(i,1);
31879 this.el.dom.removeChild(Roo.get(brick_id).dom);
31886 * adds a Masonry Brick
31887 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31889 addBrick : function(cfg)
31891 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31892 //this.register(cn);
31893 cn.parentId = this.id;
31894 cn.onRender(this.el, null);
31899 * register a Masonry Brick
31900 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31903 register : function(brick)
31905 this.bricks.push(brick);
31906 brick.masonryId = this.id;
31910 * clear all the Masonry Brick
31912 clearAll : function()
31915 //this.getChildContainer().dom.innerHTML = "";
31916 this.el.dom.innerHTML = '';
31919 getSelected : function()
31921 if (!this.selectedBrick) {
31925 return this.selectedBrick;
31929 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31933 * register a Masonry Layout
31934 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31937 register : function(layout)
31939 this.groups[layout.id] = layout;
31942 * fetch a Masonry Layout based on the masonry layout ID
31943 * @param {string} the masonry layout to add
31944 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31947 get: function(layout_id) {
31948 if (typeof(this.groups[layout_id]) == 'undefined') {
31951 return this.groups[layout_id] ;
31963 * http://masonry.desandro.com
31965 * The idea is to render all the bricks based on vertical width...
31967 * The original code extends 'outlayer' - we might need to use that....
31973 * @class Roo.bootstrap.LayoutMasonryAuto
31974 * @extends Roo.bootstrap.Component
31975 * Bootstrap Layout Masonry class
31978 * Create a new Element
31979 * @param {Object} config The config object
31982 Roo.bootstrap.LayoutMasonryAuto = function(config){
31983 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31986 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31989 * @cfg {Boolean} isFitWidth - resize the width..
31991 isFitWidth : false, // options..
31993 * @cfg {Boolean} isOriginLeft = left align?
31995 isOriginLeft : true,
31997 * @cfg {Boolean} isOriginTop = top align?
31999 isOriginTop : false,
32001 * @cfg {Boolean} isLayoutInstant = no animation?
32003 isLayoutInstant : false, // needed?
32005 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32007 isResizingContainer : true,
32009 * @cfg {Number} columnWidth width of the columns
32015 * @cfg {Number} maxCols maximum number of columns
32020 * @cfg {Number} padHeight padding below box..
32026 * @cfg {Boolean} isAutoInitial defalut true
32029 isAutoInitial : true,
32035 initialColumnWidth : 0,
32036 currentSize : null,
32038 colYs : null, // array.
32045 bricks: null, //CompositeElement
32046 cols : 0, // array?
32047 // element : null, // wrapped now this.el
32048 _isLayoutInited : null,
32051 getAutoCreate : function(){
32055 cls: 'blog-masonary-wrapper ' + this.cls,
32057 cls : 'mas-boxes masonary'
32064 getChildContainer: function( )
32066 if (this.boxesEl) {
32067 return this.boxesEl;
32070 this.boxesEl = this.el.select('.mas-boxes').first();
32072 return this.boxesEl;
32076 initEvents : function()
32080 if(this.isAutoInitial){
32081 Roo.log('hook children rendered');
32082 this.on('childrenrendered', function() {
32083 Roo.log('children rendered');
32090 initial : function()
32092 this.reloadItems();
32094 this.currentSize = this.el.getBox(true);
32096 /// was window resize... - let's see if this works..
32097 Roo.EventManager.onWindowResize(this.resize, this);
32099 if(!this.isAutoInitial){
32104 this.layout.defer(500,this);
32107 reloadItems: function()
32109 this.bricks = this.el.select('.masonry-brick', true);
32111 this.bricks.each(function(b) {
32112 //Roo.log(b.getSize());
32113 if (!b.attr('originalwidth')) {
32114 b.attr('originalwidth', b.getSize().width);
32119 Roo.log(this.bricks.elements.length);
32122 resize : function()
32125 var cs = this.el.getBox(true);
32127 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32128 Roo.log("no change in with or X");
32131 this.currentSize = cs;
32135 layout : function()
32138 this._resetLayout();
32139 //this._manageStamps();
32141 // don't animate first layout
32142 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32143 this.layoutItems( isInstant );
32145 // flag for initalized
32146 this._isLayoutInited = true;
32149 layoutItems : function( isInstant )
32151 //var items = this._getItemsForLayout( this.items );
32152 // original code supports filtering layout items.. we just ignore it..
32154 this._layoutItems( this.bricks , isInstant );
32156 this._postLayout();
32158 _layoutItems : function ( items , isInstant)
32160 //this.fireEvent( 'layout', this, items );
32163 if ( !items || !items.elements.length ) {
32164 // no items, emit event with empty array
32169 items.each(function(item) {
32170 Roo.log("layout item");
32172 // get x/y object from method
32173 var position = this._getItemLayoutPosition( item );
32175 position.item = item;
32176 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32177 queue.push( position );
32180 this._processLayoutQueue( queue );
32182 /** Sets position of item in DOM
32183 * @param {Element} item
32184 * @param {Number} x - horizontal position
32185 * @param {Number} y - vertical position
32186 * @param {Boolean} isInstant - disables transitions
32188 _processLayoutQueue : function( queue )
32190 for ( var i=0, len = queue.length; i < len; i++ ) {
32191 var obj = queue[i];
32192 obj.item.position('absolute');
32193 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32199 * Any logic you want to do after each layout,
32200 * i.e. size the container
32202 _postLayout : function()
32204 this.resizeContainer();
32207 resizeContainer : function()
32209 if ( !this.isResizingContainer ) {
32212 var size = this._getContainerSize();
32214 this.el.setSize(size.width,size.height);
32215 this.boxesEl.setSize(size.width,size.height);
32221 _resetLayout : function()
32223 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32224 this.colWidth = this.el.getWidth();
32225 //this.gutter = this.el.getWidth();
32227 this.measureColumns();
32233 this.colYs.push( 0 );
32239 measureColumns : function()
32241 this.getContainerWidth();
32242 // if columnWidth is 0, default to outerWidth of first item
32243 if ( !this.columnWidth ) {
32244 var firstItem = this.bricks.first();
32245 Roo.log(firstItem);
32246 this.columnWidth = this.containerWidth;
32247 if (firstItem && firstItem.attr('originalwidth') ) {
32248 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32250 // columnWidth fall back to item of first element
32251 Roo.log("set column width?");
32252 this.initialColumnWidth = this.columnWidth ;
32254 // if first elem has no width, default to size of container
32259 if (this.initialColumnWidth) {
32260 this.columnWidth = this.initialColumnWidth;
32265 // column width is fixed at the top - however if container width get's smaller we should
32268 // this bit calcs how man columns..
32270 var columnWidth = this.columnWidth += this.gutter;
32272 // calculate columns
32273 var containerWidth = this.containerWidth + this.gutter;
32275 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32276 // fix rounding errors, typically with gutters
32277 var excess = columnWidth - containerWidth % columnWidth;
32280 // if overshoot is less than a pixel, round up, otherwise floor it
32281 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32282 cols = Math[ mathMethod ]( cols );
32283 this.cols = Math.max( cols, 1 );
32284 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32286 // padding positioning..
32287 var totalColWidth = this.cols * this.columnWidth;
32288 var padavail = this.containerWidth - totalColWidth;
32289 // so for 2 columns - we need 3 'pads'
32291 var padNeeded = (1+this.cols) * this.padWidth;
32293 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32295 this.columnWidth += padExtra
32296 //this.padWidth = Math.floor(padavail / ( this.cols));
32298 // adjust colum width so that padding is fixed??
32300 // we have 3 columns ... total = width * 3
32301 // we have X left over... that should be used by
32303 //if (this.expandC) {
32311 getContainerWidth : function()
32313 /* // container is parent if fit width
32314 var container = this.isFitWidth ? this.element.parentNode : this.element;
32315 // check that this.size and size are there
32316 // IE8 triggers resize on body size change, so they might not be
32318 var size = getSize( container ); //FIXME
32319 this.containerWidth = size && size.innerWidth; //FIXME
32322 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32326 _getItemLayoutPosition : function( item ) // what is item?
32328 // we resize the item to our columnWidth..
32330 item.setWidth(this.columnWidth);
32331 item.autoBoxAdjust = false;
32333 var sz = item.getSize();
32335 // how many columns does this brick span
32336 var remainder = this.containerWidth % this.columnWidth;
32338 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32339 // round if off by 1 pixel, otherwise use ceil
32340 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32341 colSpan = Math.min( colSpan, this.cols );
32343 // normally this should be '1' as we dont' currently allow multi width columns..
32345 var colGroup = this._getColGroup( colSpan );
32346 // get the minimum Y value from the columns
32347 var minimumY = Math.min.apply( Math, colGroup );
32348 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32350 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32352 // position the brick
32354 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32355 y: this.currentSize.y + minimumY + this.padHeight
32359 // apply setHeight to necessary columns
32360 var setHeight = minimumY + sz.height + this.padHeight;
32361 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32363 var setSpan = this.cols + 1 - colGroup.length;
32364 for ( var i = 0; i < setSpan; i++ ) {
32365 this.colYs[ shortColIndex + i ] = setHeight ;
32372 * @param {Number} colSpan - number of columns the element spans
32373 * @returns {Array} colGroup
32375 _getColGroup : function( colSpan )
32377 if ( colSpan < 2 ) {
32378 // if brick spans only one column, use all the column Ys
32383 // how many different places could this brick fit horizontally
32384 var groupCount = this.cols + 1 - colSpan;
32385 // for each group potential horizontal position
32386 for ( var i = 0; i < groupCount; i++ ) {
32387 // make an array of colY values for that one group
32388 var groupColYs = this.colYs.slice( i, i + colSpan );
32389 // and get the max value of the array
32390 colGroup[i] = Math.max.apply( Math, groupColYs );
32395 _manageStamp : function( stamp )
32397 var stampSize = stamp.getSize();
32398 var offset = stamp.getBox();
32399 // get the columns that this stamp affects
32400 var firstX = this.isOriginLeft ? offset.x : offset.right;
32401 var lastX = firstX + stampSize.width;
32402 var firstCol = Math.floor( firstX / this.columnWidth );
32403 firstCol = Math.max( 0, firstCol );
32405 var lastCol = Math.floor( lastX / this.columnWidth );
32406 // lastCol should not go over if multiple of columnWidth #425
32407 lastCol -= lastX % this.columnWidth ? 0 : 1;
32408 lastCol = Math.min( this.cols - 1, lastCol );
32410 // set colYs to bottom of the stamp
32411 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32414 for ( var i = firstCol; i <= lastCol; i++ ) {
32415 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32420 _getContainerSize : function()
32422 this.maxY = Math.max.apply( Math, this.colYs );
32427 if ( this.isFitWidth ) {
32428 size.width = this._getContainerFitWidth();
32434 _getContainerFitWidth : function()
32436 var unusedCols = 0;
32437 // count unused columns
32440 if ( this.colYs[i] !== 0 ) {
32445 // fit container to columns that have been used
32446 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32449 needsResizeLayout : function()
32451 var previousWidth = this.containerWidth;
32452 this.getContainerWidth();
32453 return previousWidth !== this.containerWidth;
32468 * @class Roo.bootstrap.MasonryBrick
32469 * @extends Roo.bootstrap.Component
32470 * Bootstrap MasonryBrick class
32473 * Create a new MasonryBrick
32474 * @param {Object} config The config object
32477 Roo.bootstrap.MasonryBrick = function(config){
32479 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32481 Roo.bootstrap.MasonryBrick.register(this);
32487 * When a MasonryBrick is clcik
32488 * @param {Roo.bootstrap.MasonryBrick} this
32489 * @param {Roo.EventObject} e
32495 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32498 * @cfg {String} title
32502 * @cfg {String} html
32506 * @cfg {String} bgimage
32510 * @cfg {String} videourl
32514 * @cfg {String} cls
32518 * @cfg {String} href
32522 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32527 * @cfg {String} placetitle (center|bottom)
32532 * @cfg {Boolean} isFitContainer defalut true
32534 isFitContainer : true,
32537 * @cfg {Boolean} preventDefault defalut false
32539 preventDefault : false,
32542 * @cfg {Boolean} inverse defalut false
32544 maskInverse : false,
32546 getAutoCreate : function()
32548 if(!this.isFitContainer){
32549 return this.getSplitAutoCreate();
32552 var cls = 'masonry-brick masonry-brick-full';
32554 if(this.href.length){
32555 cls += ' masonry-brick-link';
32558 if(this.bgimage.length){
32559 cls += ' masonry-brick-image';
32562 if(this.maskInverse){
32563 cls += ' mask-inverse';
32566 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32567 cls += ' enable-mask';
32571 cls += ' masonry-' + this.size + '-brick';
32574 if(this.placetitle.length){
32576 switch (this.placetitle) {
32578 cls += ' masonry-center-title';
32581 cls += ' masonry-bottom-title';
32588 if(!this.html.length && !this.bgimage.length){
32589 cls += ' masonry-center-title';
32592 if(!this.html.length && this.bgimage.length){
32593 cls += ' masonry-bottom-title';
32598 cls += ' ' + this.cls;
32602 tag: (this.href.length) ? 'a' : 'div',
32607 cls: 'masonry-brick-mask'
32611 cls: 'masonry-brick-paragraph',
32617 if(this.href.length){
32618 cfg.href = this.href;
32621 var cn = cfg.cn[1].cn;
32623 if(this.title.length){
32626 cls: 'masonry-brick-title',
32631 if(this.html.length){
32634 cls: 'masonry-brick-text',
32639 if (!this.title.length && !this.html.length) {
32640 cfg.cn[1].cls += ' hide';
32643 if(this.bgimage.length){
32646 cls: 'masonry-brick-image-view',
32651 if(this.videourl.length){
32652 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32653 // youtube support only?
32656 cls: 'masonry-brick-image-view',
32659 allowfullscreen : true
32667 getSplitAutoCreate : function()
32669 var cls = 'masonry-brick masonry-brick-split';
32671 if(this.href.length){
32672 cls += ' masonry-brick-link';
32675 if(this.bgimage.length){
32676 cls += ' masonry-brick-image';
32680 cls += ' masonry-' + this.size + '-brick';
32683 switch (this.placetitle) {
32685 cls += ' masonry-center-title';
32688 cls += ' masonry-bottom-title';
32691 if(!this.bgimage.length){
32692 cls += ' masonry-center-title';
32695 if(this.bgimage.length){
32696 cls += ' masonry-bottom-title';
32702 cls += ' ' + this.cls;
32706 tag: (this.href.length) ? 'a' : 'div',
32711 cls: 'masonry-brick-split-head',
32715 cls: 'masonry-brick-paragraph',
32722 cls: 'masonry-brick-split-body',
32728 if(this.href.length){
32729 cfg.href = this.href;
32732 if(this.title.length){
32733 cfg.cn[0].cn[0].cn.push({
32735 cls: 'masonry-brick-title',
32740 if(this.html.length){
32741 cfg.cn[1].cn.push({
32743 cls: 'masonry-brick-text',
32748 if(this.bgimage.length){
32749 cfg.cn[0].cn.push({
32751 cls: 'masonry-brick-image-view',
32756 if(this.videourl.length){
32757 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32758 // youtube support only?
32759 cfg.cn[0].cn.cn.push({
32761 cls: 'masonry-brick-image-view',
32764 allowfullscreen : true
32771 initEvents: function()
32773 switch (this.size) {
32806 this.el.on('touchstart', this.onTouchStart, this);
32807 this.el.on('touchmove', this.onTouchMove, this);
32808 this.el.on('touchend', this.onTouchEnd, this);
32809 this.el.on('contextmenu', this.onContextMenu, this);
32811 this.el.on('mouseenter' ,this.enter, this);
32812 this.el.on('mouseleave', this.leave, this);
32813 this.el.on('click', this.onClick, this);
32816 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32817 this.parent().bricks.push(this);
32822 onClick: function(e, el)
32824 var time = this.endTimer - this.startTimer;
32825 // Roo.log(e.preventDefault());
32828 e.preventDefault();
32833 if(!this.preventDefault){
32837 e.preventDefault();
32839 if (this.activeClass != '') {
32840 this.selectBrick();
32843 this.fireEvent('click', this, e);
32846 enter: function(e, el)
32848 e.preventDefault();
32850 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32854 if(this.bgimage.length && this.html.length){
32855 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32859 leave: function(e, el)
32861 e.preventDefault();
32863 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32867 if(this.bgimage.length && this.html.length){
32868 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32872 onTouchStart: function(e, el)
32874 // e.preventDefault();
32876 this.touchmoved = false;
32878 if(!this.isFitContainer){
32882 if(!this.bgimage.length || !this.html.length){
32886 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32888 this.timer = new Date().getTime();
32892 onTouchMove: function(e, el)
32894 this.touchmoved = true;
32897 onContextMenu : function(e,el)
32899 e.preventDefault();
32900 e.stopPropagation();
32904 onTouchEnd: function(e, el)
32906 // e.preventDefault();
32908 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32915 if(!this.bgimage.length || !this.html.length){
32917 if(this.href.length){
32918 window.location.href = this.href;
32924 if(!this.isFitContainer){
32928 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32930 window.location.href = this.href;
32933 //selection on single brick only
32934 selectBrick : function() {
32936 if (!this.parentId) {
32940 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32941 var index = m.selectedBrick.indexOf(this.id);
32944 m.selectedBrick.splice(index,1);
32945 this.el.removeClass(this.activeClass);
32949 for(var i = 0; i < m.selectedBrick.length; i++) {
32950 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32951 b.el.removeClass(b.activeClass);
32954 m.selectedBrick = [];
32956 m.selectedBrick.push(this.id);
32957 this.el.addClass(this.activeClass);
32961 isSelected : function(){
32962 return this.el.hasClass(this.activeClass);
32967 Roo.apply(Roo.bootstrap.MasonryBrick, {
32970 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32972 * register a Masonry Brick
32973 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32976 register : function(brick)
32978 //this.groups[brick.id] = brick;
32979 this.groups.add(brick.id, brick);
32982 * fetch a masonry brick based on the masonry brick ID
32983 * @param {string} the masonry brick to add
32984 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32987 get: function(brick_id)
32989 // if (typeof(this.groups[brick_id]) == 'undefined') {
32992 // return this.groups[brick_id] ;
32994 if(this.groups.key(brick_id)) {
32995 return this.groups.key(brick_id);
33013 * @class Roo.bootstrap.Brick
33014 * @extends Roo.bootstrap.Component
33015 * Bootstrap Brick class
33018 * Create a new Brick
33019 * @param {Object} config The config object
33022 Roo.bootstrap.Brick = function(config){
33023 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33029 * When a Brick is click
33030 * @param {Roo.bootstrap.Brick} this
33031 * @param {Roo.EventObject} e
33037 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33040 * @cfg {String} title
33044 * @cfg {String} html
33048 * @cfg {String} bgimage
33052 * @cfg {String} cls
33056 * @cfg {String} href
33060 * @cfg {String} video
33064 * @cfg {Boolean} square
33068 getAutoCreate : function()
33070 var cls = 'roo-brick';
33072 if(this.href.length){
33073 cls += ' roo-brick-link';
33076 if(this.bgimage.length){
33077 cls += ' roo-brick-image';
33080 if(!this.html.length && !this.bgimage.length){
33081 cls += ' roo-brick-center-title';
33084 if(!this.html.length && this.bgimage.length){
33085 cls += ' roo-brick-bottom-title';
33089 cls += ' ' + this.cls;
33093 tag: (this.href.length) ? 'a' : 'div',
33098 cls: 'roo-brick-paragraph',
33104 if(this.href.length){
33105 cfg.href = this.href;
33108 var cn = cfg.cn[0].cn;
33110 if(this.title.length){
33113 cls: 'roo-brick-title',
33118 if(this.html.length){
33121 cls: 'roo-brick-text',
33128 if(this.bgimage.length){
33131 cls: 'roo-brick-image-view',
33139 initEvents: function()
33141 if(this.title.length || this.html.length){
33142 this.el.on('mouseenter' ,this.enter, this);
33143 this.el.on('mouseleave', this.leave, this);
33146 Roo.EventManager.onWindowResize(this.resize, this);
33148 if(this.bgimage.length){
33149 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33150 this.imageEl.on('load', this.onImageLoad, this);
33157 onImageLoad : function()
33162 resize : function()
33164 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33166 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33168 if(this.bgimage.length){
33169 var image = this.el.select('.roo-brick-image-view', true).first();
33171 image.setWidth(paragraph.getWidth());
33174 image.setHeight(paragraph.getWidth());
33177 this.el.setHeight(image.getHeight());
33178 paragraph.setHeight(image.getHeight());
33184 enter: function(e, el)
33186 e.preventDefault();
33188 if(this.bgimage.length){
33189 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33190 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33194 leave: function(e, el)
33196 e.preventDefault();
33198 if(this.bgimage.length){
33199 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33200 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33215 * @class Roo.bootstrap.NumberField
33216 * @extends Roo.bootstrap.Input
33217 * Bootstrap NumberField class
33223 * Create a new NumberField
33224 * @param {Object} config The config object
33227 Roo.bootstrap.NumberField = function(config){
33228 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33231 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33234 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33236 allowDecimals : true,
33238 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33240 decimalSeparator : ".",
33242 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33244 decimalPrecision : 2,
33246 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33248 allowNegative : true,
33251 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33255 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33257 minValue : Number.NEGATIVE_INFINITY,
33259 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33261 maxValue : Number.MAX_VALUE,
33263 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33265 minText : "The minimum value for this field is {0}",
33267 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33269 maxText : "The maximum value for this field is {0}",
33271 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33272 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33274 nanText : "{0} is not a valid number",
33276 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33278 thousandsDelimiter : false,
33280 * @cfg {String} valueAlign alignment of value
33282 valueAlign : "left",
33284 getAutoCreate : function()
33286 var hiddenInput = {
33290 cls: 'hidden-number-input'
33294 hiddenInput.name = this.name;
33299 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33301 this.name = hiddenInput.name;
33303 if(cfg.cn.length > 0) {
33304 cfg.cn.push(hiddenInput);
33311 initEvents : function()
33313 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33315 var allowed = "0123456789";
33317 if(this.allowDecimals){
33318 allowed += this.decimalSeparator;
33321 if(this.allowNegative){
33325 if(this.thousandsDelimiter) {
33329 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33331 var keyPress = function(e){
33333 var k = e.getKey();
33335 var c = e.getCharCode();
33338 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33339 allowed.indexOf(String.fromCharCode(c)) === -1
33345 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33349 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33354 this.el.on("keypress", keyPress, this);
33357 validateValue : function(value)
33360 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33364 var num = this.parseValue(value);
33367 this.markInvalid(String.format(this.nanText, value));
33371 if(num < this.minValue){
33372 this.markInvalid(String.format(this.minText, this.minValue));
33376 if(num > this.maxValue){
33377 this.markInvalid(String.format(this.maxText, this.maxValue));
33384 getValue : function()
33386 var v = this.hiddenEl().getValue();
33388 return this.fixPrecision(this.parseValue(v));
33391 parseValue : function(value)
33393 if(this.thousandsDelimiter) {
33395 r = new RegExp(",", "g");
33396 value = value.replace(r, "");
33399 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33400 return isNaN(value) ? '' : value;
33403 fixPrecision : function(value)
33405 if(this.thousandsDelimiter) {
33407 r = new RegExp(",", "g");
33408 value = value.replace(r, "");
33411 var nan = isNaN(value);
33413 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33414 return nan ? '' : value;
33416 return parseFloat(value).toFixed(this.decimalPrecision);
33419 setValue : function(v)
33421 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33427 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33429 this.inputEl().dom.value = (v == '') ? '' :
33430 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33432 if(!this.allowZero && v === '0') {
33433 this.hiddenEl().dom.value = '';
33434 this.inputEl().dom.value = '';
33441 decimalPrecisionFcn : function(v)
33443 return Math.floor(v);
33446 beforeBlur : function()
33448 var v = this.parseValue(this.getRawValue());
33450 if(v || v === 0 || v === ''){
33455 hiddenEl : function()
33457 return this.el.select('input.hidden-number-input',true).first();
33469 * @class Roo.bootstrap.DocumentSlider
33470 * @extends Roo.bootstrap.Component
33471 * Bootstrap DocumentSlider class
33474 * Create a new DocumentViewer
33475 * @param {Object} config The config object
33478 Roo.bootstrap.DocumentSlider = function(config){
33479 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33486 * Fire after initEvent
33487 * @param {Roo.bootstrap.DocumentSlider} this
33492 * Fire after update
33493 * @param {Roo.bootstrap.DocumentSlider} this
33499 * @param {Roo.bootstrap.DocumentSlider} this
33505 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33511 getAutoCreate : function()
33515 cls : 'roo-document-slider',
33519 cls : 'roo-document-slider-header',
33523 cls : 'roo-document-slider-header-title'
33529 cls : 'roo-document-slider-body',
33533 cls : 'roo-document-slider-prev',
33537 cls : 'fa fa-chevron-left'
33543 cls : 'roo-document-slider-thumb',
33547 cls : 'roo-document-slider-image'
33553 cls : 'roo-document-slider-next',
33557 cls : 'fa fa-chevron-right'
33569 initEvents : function()
33571 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33572 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33574 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33575 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33577 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33578 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33580 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33581 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33583 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33584 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33586 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33587 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33589 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33590 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33592 this.thumbEl.on('click', this.onClick, this);
33594 this.prevIndicator.on('click', this.prev, this);
33596 this.nextIndicator.on('click', this.next, this);
33600 initial : function()
33602 if(this.files.length){
33603 this.indicator = 1;
33607 this.fireEvent('initial', this);
33610 update : function()
33612 this.imageEl.attr('src', this.files[this.indicator - 1]);
33614 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33616 this.prevIndicator.show();
33618 if(this.indicator == 1){
33619 this.prevIndicator.hide();
33622 this.nextIndicator.show();
33624 if(this.indicator == this.files.length){
33625 this.nextIndicator.hide();
33628 this.thumbEl.scrollTo('top');
33630 this.fireEvent('update', this);
33633 onClick : function(e)
33635 e.preventDefault();
33637 this.fireEvent('click', this);
33642 e.preventDefault();
33644 this.indicator = Math.max(1, this.indicator - 1);
33651 e.preventDefault();
33653 this.indicator = Math.min(this.files.length, this.indicator + 1);
33667 * @class Roo.bootstrap.RadioSet
33668 * @extends Roo.bootstrap.Input
33669 * Bootstrap RadioSet class
33670 * @cfg {String} indicatorpos (left|right) default left
33671 * @cfg {Boolean} inline (true|false) inline the element (default true)
33672 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33674 * Create a new RadioSet
33675 * @param {Object} config The config object
33678 Roo.bootstrap.RadioSet = function(config){
33680 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33684 Roo.bootstrap.RadioSet.register(this);
33689 * Fires when the element is checked or unchecked.
33690 * @param {Roo.bootstrap.RadioSet} this This radio
33691 * @param {Roo.bootstrap.Radio} item The checked item
33696 * Fires when the element is click.
33697 * @param {Roo.bootstrap.RadioSet} this This radio set
33698 * @param {Roo.bootstrap.Radio} item The checked item
33699 * @param {Roo.EventObject} e The event object
33706 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33714 indicatorpos : 'left',
33716 getAutoCreate : function()
33720 cls : 'roo-radio-set-label',
33724 html : this.fieldLabel
33729 if(this.indicatorpos == 'left'){
33732 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33733 tooltip : 'This field is required'
33738 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33739 tooltip : 'This field is required'
33745 cls : 'roo-radio-set-items'
33748 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33750 if (align === 'left' && this.fieldLabel.length) {
33753 cls : "roo-radio-set-right",
33759 if(this.labelWidth > 12){
33760 label.style = "width: " + this.labelWidth + 'px';
33763 if(this.labelWidth < 13 && this.labelmd == 0){
33764 this.labelmd = this.labelWidth;
33767 if(this.labellg > 0){
33768 label.cls += ' col-lg-' + this.labellg;
33769 items.cls += ' col-lg-' + (12 - this.labellg);
33772 if(this.labelmd > 0){
33773 label.cls += ' col-md-' + this.labelmd;
33774 items.cls += ' col-md-' + (12 - this.labelmd);
33777 if(this.labelsm > 0){
33778 label.cls += ' col-sm-' + this.labelsm;
33779 items.cls += ' col-sm-' + (12 - this.labelsm);
33782 if(this.labelxs > 0){
33783 label.cls += ' col-xs-' + this.labelxs;
33784 items.cls += ' col-xs-' + (12 - this.labelxs);
33790 cls : 'roo-radio-set',
33794 cls : 'roo-radio-set-input',
33797 value : this.value ? this.value : ''
33804 if(this.weight.length){
33805 cfg.cls += ' roo-radio-' + this.weight;
33809 cfg.cls += ' roo-radio-set-inline';
33813 ['xs','sm','md','lg'].map(function(size){
33814 if (settings[size]) {
33815 cfg.cls += ' col-' + size + '-' + settings[size];
33823 initEvents : function()
33825 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33826 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33828 if(!this.fieldLabel.length){
33829 this.labelEl.hide();
33832 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33833 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33835 this.indicator = this.indicatorEl();
33837 if(this.indicator){
33838 this.indicator.addClass('invisible');
33841 this.originalValue = this.getValue();
33845 inputEl: function ()
33847 return this.el.select('.roo-radio-set-input', true).first();
33850 getChildContainer : function()
33852 return this.itemsEl;
33855 register : function(item)
33857 this.radioes.push(item);
33861 validate : function()
33863 if(this.getVisibilityEl().hasClass('hidden')){
33869 Roo.each(this.radioes, function(i){
33878 if(this.allowBlank) {
33882 if(this.disabled || valid){
33887 this.markInvalid();
33892 markValid : function()
33894 if(this.labelEl.isVisible(true)){
33895 this.indicatorEl().removeClass('visible');
33896 this.indicatorEl().addClass('invisible');
33899 this.el.removeClass([this.invalidClass, this.validClass]);
33900 this.el.addClass(this.validClass);
33902 this.fireEvent('valid', this);
33905 markInvalid : function(msg)
33907 if(this.allowBlank || this.disabled){
33911 if(this.labelEl.isVisible(true)){
33912 this.indicatorEl().removeClass('invisible');
33913 this.indicatorEl().addClass('visible');
33916 this.el.removeClass([this.invalidClass, this.validClass]);
33917 this.el.addClass(this.invalidClass);
33919 this.fireEvent('invalid', this, msg);
33923 setValue : function(v, suppressEvent)
33925 if(this.value === v){
33932 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33935 Roo.each(this.radioes, function(i){
33937 i.el.removeClass('checked');
33940 Roo.each(this.radioes, function(i){
33942 if(i.value === v || i.value.toString() === v.toString()){
33944 i.el.addClass('checked');
33946 if(suppressEvent !== true){
33947 this.fireEvent('check', this, i);
33958 clearInvalid : function(){
33960 if(!this.el || this.preventMark){
33964 this.el.removeClass([this.invalidClass]);
33966 this.fireEvent('valid', this);
33971 Roo.apply(Roo.bootstrap.RadioSet, {
33975 register : function(set)
33977 this.groups[set.name] = set;
33980 get: function(name)
33982 if (typeof(this.groups[name]) == 'undefined') {
33986 return this.groups[name] ;
33992 * Ext JS Library 1.1.1
33993 * Copyright(c) 2006-2007, Ext JS, LLC.
33995 * Originally Released Under LGPL - original licence link has changed is not relivant.
33998 * <script type="text/javascript">
34003 * @class Roo.bootstrap.SplitBar
34004 * @extends Roo.util.Observable
34005 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34009 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34010 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34011 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34012 split.minSize = 100;
34013 split.maxSize = 600;
34014 split.animate = true;
34015 split.on('moved', splitterMoved);
34018 * Create a new SplitBar
34019 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34020 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34021 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34022 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34023 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34024 position of the SplitBar).
34026 Roo.bootstrap.SplitBar = function(cfg){
34031 // dragElement : elm
34032 // resizingElement: el,
34034 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34035 // placement : Roo.bootstrap.SplitBar.LEFT ,
34036 // existingProxy ???
34039 this.el = Roo.get(cfg.dragElement, true);
34040 this.el.dom.unselectable = "on";
34042 this.resizingEl = Roo.get(cfg.resizingElement, true);
34046 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34047 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34050 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34053 * The minimum size of the resizing element. (Defaults to 0)
34059 * The maximum size of the resizing element. (Defaults to 2000)
34062 this.maxSize = 2000;
34065 * Whether to animate the transition to the new size
34068 this.animate = false;
34071 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34074 this.useShim = false;
34079 if(!cfg.existingProxy){
34081 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34083 this.proxy = Roo.get(cfg.existingProxy).dom;
34086 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34089 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34092 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34095 this.dragSpecs = {};
34098 * @private The adapter to use to positon and resize elements
34100 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34101 this.adapter.init(this);
34103 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34105 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34106 this.el.addClass("roo-splitbar-h");
34109 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34110 this.el.addClass("roo-splitbar-v");
34116 * Fires when the splitter is moved (alias for {@link #event-moved})
34117 * @param {Roo.bootstrap.SplitBar} this
34118 * @param {Number} newSize the new width or height
34123 * Fires when the splitter is moved
34124 * @param {Roo.bootstrap.SplitBar} this
34125 * @param {Number} newSize the new width or height
34129 * @event beforeresize
34130 * Fires before the splitter is dragged
34131 * @param {Roo.bootstrap.SplitBar} this
34133 "beforeresize" : true,
34135 "beforeapply" : true
34138 Roo.util.Observable.call(this);
34141 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34142 onStartProxyDrag : function(x, y){
34143 this.fireEvent("beforeresize", this);
34145 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34147 o.enableDisplayMode("block");
34148 // all splitbars share the same overlay
34149 Roo.bootstrap.SplitBar.prototype.overlay = o;
34151 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34152 this.overlay.show();
34153 Roo.get(this.proxy).setDisplayed("block");
34154 var size = this.adapter.getElementSize(this);
34155 this.activeMinSize = this.getMinimumSize();;
34156 this.activeMaxSize = this.getMaximumSize();;
34157 var c1 = size - this.activeMinSize;
34158 var c2 = Math.max(this.activeMaxSize - size, 0);
34159 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34160 this.dd.resetConstraints();
34161 this.dd.setXConstraint(
34162 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34163 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34165 this.dd.setYConstraint(0, 0);
34167 this.dd.resetConstraints();
34168 this.dd.setXConstraint(0, 0);
34169 this.dd.setYConstraint(
34170 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34171 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34174 this.dragSpecs.startSize = size;
34175 this.dragSpecs.startPoint = [x, y];
34176 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34180 * @private Called after the drag operation by the DDProxy
34182 onEndProxyDrag : function(e){
34183 Roo.get(this.proxy).setDisplayed(false);
34184 var endPoint = Roo.lib.Event.getXY(e);
34186 this.overlay.hide();
34189 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34190 newSize = this.dragSpecs.startSize +
34191 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34192 endPoint[0] - this.dragSpecs.startPoint[0] :
34193 this.dragSpecs.startPoint[0] - endPoint[0]
34196 newSize = this.dragSpecs.startSize +
34197 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34198 endPoint[1] - this.dragSpecs.startPoint[1] :
34199 this.dragSpecs.startPoint[1] - endPoint[1]
34202 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34203 if(newSize != this.dragSpecs.startSize){
34204 if(this.fireEvent('beforeapply', this, newSize) !== false){
34205 this.adapter.setElementSize(this, newSize);
34206 this.fireEvent("moved", this, newSize);
34207 this.fireEvent("resize", this, newSize);
34213 * Get the adapter this SplitBar uses
34214 * @return The adapter object
34216 getAdapter : function(){
34217 return this.adapter;
34221 * Set the adapter this SplitBar uses
34222 * @param {Object} adapter A SplitBar adapter object
34224 setAdapter : function(adapter){
34225 this.adapter = adapter;
34226 this.adapter.init(this);
34230 * Gets the minimum size for the resizing element
34231 * @return {Number} The minimum size
34233 getMinimumSize : function(){
34234 return this.minSize;
34238 * Sets the minimum size for the resizing element
34239 * @param {Number} minSize The minimum size
34241 setMinimumSize : function(minSize){
34242 this.minSize = minSize;
34246 * Gets the maximum size for the resizing element
34247 * @return {Number} The maximum size
34249 getMaximumSize : function(){
34250 return this.maxSize;
34254 * Sets the maximum size for the resizing element
34255 * @param {Number} maxSize The maximum size
34257 setMaximumSize : function(maxSize){
34258 this.maxSize = maxSize;
34262 * Sets the initialize size for the resizing element
34263 * @param {Number} size The initial size
34265 setCurrentSize : function(size){
34266 var oldAnimate = this.animate;
34267 this.animate = false;
34268 this.adapter.setElementSize(this, size);
34269 this.animate = oldAnimate;
34273 * Destroy this splitbar.
34274 * @param {Boolean} removeEl True to remove the element
34276 destroy : function(removeEl){
34278 this.shim.remove();
34281 this.proxy.parentNode.removeChild(this.proxy);
34289 * @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.
34291 Roo.bootstrap.SplitBar.createProxy = function(dir){
34292 var proxy = new Roo.Element(document.createElement("div"));
34293 proxy.unselectable();
34294 var cls = 'roo-splitbar-proxy';
34295 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34296 document.body.appendChild(proxy.dom);
34301 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34302 * Default Adapter. It assumes the splitter and resizing element are not positioned
34303 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34305 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34308 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34309 // do nothing for now
34310 init : function(s){
34314 * Called before drag operations to get the current size of the resizing element.
34315 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34317 getElementSize : function(s){
34318 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34319 return s.resizingEl.getWidth();
34321 return s.resizingEl.getHeight();
34326 * Called after drag operations to set the size of the resizing element.
34327 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34328 * @param {Number} newSize The new size to set
34329 * @param {Function} onComplete A function to be invoked when resizing is complete
34331 setElementSize : function(s, newSize, onComplete){
34332 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34334 s.resizingEl.setWidth(newSize);
34336 onComplete(s, newSize);
34339 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34344 s.resizingEl.setHeight(newSize);
34346 onComplete(s, newSize);
34349 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34356 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34357 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34358 * Adapter that moves the splitter element to align with the resized sizing element.
34359 * Used with an absolute positioned SplitBar.
34360 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34361 * document.body, make sure you assign an id to the body element.
34363 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34364 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34365 this.container = Roo.get(container);
34368 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34369 init : function(s){
34370 this.basic.init(s);
34373 getElementSize : function(s){
34374 return this.basic.getElementSize(s);
34377 setElementSize : function(s, newSize, onComplete){
34378 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34381 moveSplitter : function(s){
34382 var yes = Roo.bootstrap.SplitBar;
34383 switch(s.placement){
34385 s.el.setX(s.resizingEl.getRight());
34388 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34391 s.el.setY(s.resizingEl.getBottom());
34394 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34401 * Orientation constant - Create a vertical SplitBar
34405 Roo.bootstrap.SplitBar.VERTICAL = 1;
34408 * Orientation constant - Create a horizontal SplitBar
34412 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34415 * Placement constant - The resizing element is to the left of the splitter element
34419 Roo.bootstrap.SplitBar.LEFT = 1;
34422 * Placement constant - The resizing element is to the right of the splitter element
34426 Roo.bootstrap.SplitBar.RIGHT = 2;
34429 * Placement constant - The resizing element is positioned above the splitter element
34433 Roo.bootstrap.SplitBar.TOP = 3;
34436 * Placement constant - The resizing element is positioned under splitter element
34440 Roo.bootstrap.SplitBar.BOTTOM = 4;
34441 Roo.namespace("Roo.bootstrap.layout");/*
34443 * Ext JS Library 1.1.1
34444 * Copyright(c) 2006-2007, Ext JS, LLC.
34446 * Originally Released Under LGPL - original licence link has changed is not relivant.
34449 * <script type="text/javascript">
34453 * @class Roo.bootstrap.layout.Manager
34454 * @extends Roo.bootstrap.Component
34455 * Base class for layout managers.
34457 Roo.bootstrap.layout.Manager = function(config)
34459 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34465 /** false to disable window resize monitoring @type Boolean */
34466 this.monitorWindowResize = true;
34471 * Fires when a layout is performed.
34472 * @param {Roo.LayoutManager} this
34476 * @event regionresized
34477 * Fires when the user resizes a region.
34478 * @param {Roo.LayoutRegion} region The resized region
34479 * @param {Number} newSize The new size (width for east/west, height for north/south)
34481 "regionresized" : true,
34483 * @event regioncollapsed
34484 * Fires when a region is collapsed.
34485 * @param {Roo.LayoutRegion} region The collapsed region
34487 "regioncollapsed" : true,
34489 * @event regionexpanded
34490 * Fires when a region is expanded.
34491 * @param {Roo.LayoutRegion} region The expanded region
34493 "regionexpanded" : true
34495 this.updating = false;
34498 this.el = Roo.get(config.el);
34504 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34509 monitorWindowResize : true,
34515 onRender : function(ct, position)
34518 this.el = Roo.get(ct);
34521 //this.fireEvent('render',this);
34525 initEvents: function()
34529 // ie scrollbar fix
34530 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34531 document.body.scroll = "no";
34532 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34533 this.el.position('relative');
34535 this.id = this.el.id;
34536 this.el.addClass("roo-layout-container");
34537 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34538 if(this.el.dom != document.body ) {
34539 this.el.on('resize', this.layout,this);
34540 this.el.on('show', this.layout,this);
34546 * Returns true if this layout is currently being updated
34547 * @return {Boolean}
34549 isUpdating : function(){
34550 return this.updating;
34554 * Suspend the LayoutManager from doing auto-layouts while
34555 * making multiple add or remove calls
34557 beginUpdate : function(){
34558 this.updating = true;
34562 * Restore auto-layouts and optionally disable the manager from performing a layout
34563 * @param {Boolean} noLayout true to disable a layout update
34565 endUpdate : function(noLayout){
34566 this.updating = false;
34572 layout: function(){
34576 onRegionResized : function(region, newSize){
34577 this.fireEvent("regionresized", region, newSize);
34581 onRegionCollapsed : function(region){
34582 this.fireEvent("regioncollapsed", region);
34585 onRegionExpanded : function(region){
34586 this.fireEvent("regionexpanded", region);
34590 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34591 * performs box-model adjustments.
34592 * @return {Object} The size as an object {width: (the width), height: (the height)}
34594 getViewSize : function()
34597 if(this.el.dom != document.body){
34598 size = this.el.getSize();
34600 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34602 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34603 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34608 * Returns the Element this layout is bound to.
34609 * @return {Roo.Element}
34611 getEl : function(){
34616 * Returns the specified region.
34617 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34618 * @return {Roo.LayoutRegion}
34620 getRegion : function(target){
34621 return this.regions[target.toLowerCase()];
34624 onWindowResize : function(){
34625 if(this.monitorWindowResize){
34632 * Ext JS Library 1.1.1
34633 * Copyright(c) 2006-2007, Ext JS, LLC.
34635 * Originally Released Under LGPL - original licence link has changed is not relivant.
34638 * <script type="text/javascript">
34641 * @class Roo.bootstrap.layout.Border
34642 * @extends Roo.bootstrap.layout.Manager
34643 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34644 * please see: examples/bootstrap/nested.html<br><br>
34646 <b>The container the layout is rendered into can be either the body element or any other element.
34647 If it is not the body element, the container needs to either be an absolute positioned element,
34648 or you will need to add "position:relative" to the css of the container. You will also need to specify
34649 the container size if it is not the body element.</b>
34652 * Create a new Border
34653 * @param {Object} config Configuration options
34655 Roo.bootstrap.layout.Border = function(config){
34656 config = config || {};
34657 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34661 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34662 if(config[region]){
34663 config[region].region = region;
34664 this.addRegion(config[region]);
34670 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34672 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34674 * Creates and adds a new region if it doesn't already exist.
34675 * @param {String} target The target region key (north, south, east, west or center).
34676 * @param {Object} config The regions config object
34677 * @return {BorderLayoutRegion} The new region
34679 addRegion : function(config)
34681 if(!this.regions[config.region]){
34682 var r = this.factory(config);
34683 this.bindRegion(r);
34685 return this.regions[config.region];
34689 bindRegion : function(r){
34690 this.regions[r.config.region] = r;
34692 r.on("visibilitychange", this.layout, this);
34693 r.on("paneladded", this.layout, this);
34694 r.on("panelremoved", this.layout, this);
34695 r.on("invalidated", this.layout, this);
34696 r.on("resized", this.onRegionResized, this);
34697 r.on("collapsed", this.onRegionCollapsed, this);
34698 r.on("expanded", this.onRegionExpanded, this);
34702 * Performs a layout update.
34704 layout : function()
34706 if(this.updating) {
34710 // render all the rebions if they have not been done alreayd?
34711 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34712 if(this.regions[region] && !this.regions[region].bodyEl){
34713 this.regions[region].onRender(this.el)
34717 var size = this.getViewSize();
34718 var w = size.width;
34719 var h = size.height;
34724 //var x = 0, y = 0;
34726 var rs = this.regions;
34727 var north = rs["north"];
34728 var south = rs["south"];
34729 var west = rs["west"];
34730 var east = rs["east"];
34731 var center = rs["center"];
34732 //if(this.hideOnLayout){ // not supported anymore
34733 //c.el.setStyle("display", "none");
34735 if(north && north.isVisible()){
34736 var b = north.getBox();
34737 var m = north.getMargins();
34738 b.width = w - (m.left+m.right);
34741 centerY = b.height + b.y + m.bottom;
34742 centerH -= centerY;
34743 north.updateBox(this.safeBox(b));
34745 if(south && south.isVisible()){
34746 var b = south.getBox();
34747 var m = south.getMargins();
34748 b.width = w - (m.left+m.right);
34750 var totalHeight = (b.height + m.top + m.bottom);
34751 b.y = h - totalHeight + m.top;
34752 centerH -= totalHeight;
34753 south.updateBox(this.safeBox(b));
34755 if(west && west.isVisible()){
34756 var b = west.getBox();
34757 var m = west.getMargins();
34758 b.height = centerH - (m.top+m.bottom);
34760 b.y = centerY + m.top;
34761 var totalWidth = (b.width + m.left + m.right);
34762 centerX += totalWidth;
34763 centerW -= totalWidth;
34764 west.updateBox(this.safeBox(b));
34766 if(east && east.isVisible()){
34767 var b = east.getBox();
34768 var m = east.getMargins();
34769 b.height = centerH - (m.top+m.bottom);
34770 var totalWidth = (b.width + m.left + m.right);
34771 b.x = w - totalWidth + m.left;
34772 b.y = centerY + m.top;
34773 centerW -= totalWidth;
34774 east.updateBox(this.safeBox(b));
34777 var m = center.getMargins();
34779 x: centerX + m.left,
34780 y: centerY + m.top,
34781 width: centerW - (m.left+m.right),
34782 height: centerH - (m.top+m.bottom)
34784 //if(this.hideOnLayout){
34785 //center.el.setStyle("display", "block");
34787 center.updateBox(this.safeBox(centerBox));
34790 this.fireEvent("layout", this);
34794 safeBox : function(box){
34795 box.width = Math.max(0, box.width);
34796 box.height = Math.max(0, box.height);
34801 * Adds a ContentPanel (or subclass) to this layout.
34802 * @param {String} target The target region key (north, south, east, west or center).
34803 * @param {Roo.ContentPanel} panel The panel to add
34804 * @return {Roo.ContentPanel} The added panel
34806 add : function(target, panel){
34808 target = target.toLowerCase();
34809 return this.regions[target].add(panel);
34813 * Remove a ContentPanel (or subclass) to this layout.
34814 * @param {String} target The target region key (north, south, east, west or center).
34815 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34816 * @return {Roo.ContentPanel} The removed panel
34818 remove : function(target, panel){
34819 target = target.toLowerCase();
34820 return this.regions[target].remove(panel);
34824 * Searches all regions for a panel with the specified id
34825 * @param {String} panelId
34826 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34828 findPanel : function(panelId){
34829 var rs = this.regions;
34830 for(var target in rs){
34831 if(typeof rs[target] != "function"){
34832 var p = rs[target].getPanel(panelId);
34842 * Searches all regions for a panel with the specified id and activates (shows) it.
34843 * @param {String/ContentPanel} panelId The panels id or the panel itself
34844 * @return {Roo.ContentPanel} The shown panel or null
34846 showPanel : function(panelId) {
34847 var rs = this.regions;
34848 for(var target in rs){
34849 var r = rs[target];
34850 if(typeof r != "function"){
34851 if(r.hasPanel(panelId)){
34852 return r.showPanel(panelId);
34860 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34861 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34864 restoreState : function(provider){
34866 provider = Roo.state.Manager;
34868 var sm = new Roo.LayoutStateManager();
34869 sm.init(this, provider);
34875 * Adds a xtype elements to the layout.
34879 xtype : 'ContentPanel',
34886 xtype : 'NestedLayoutPanel',
34892 items : [ ... list of content panels or nested layout panels.. ]
34896 * @param {Object} cfg Xtype definition of item to add.
34898 addxtype : function(cfg)
34900 // basically accepts a pannel...
34901 // can accept a layout region..!?!?
34902 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34905 // theory? children can only be panels??
34907 //if (!cfg.xtype.match(/Panel$/)) {
34912 if (typeof(cfg.region) == 'undefined') {
34913 Roo.log("Failed to add Panel, region was not set");
34917 var region = cfg.region;
34923 xitems = cfg.items;
34930 case 'Content': // ContentPanel (el, cfg)
34931 case 'Scroll': // ContentPanel (el, cfg)
34933 cfg.autoCreate = true;
34934 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34936 // var el = this.el.createChild();
34937 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34940 this.add(region, ret);
34944 case 'TreePanel': // our new panel!
34945 cfg.el = this.el.createChild();
34946 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34947 this.add(region, ret);
34952 // create a new Layout (which is a Border Layout...
34954 var clayout = cfg.layout;
34955 clayout.el = this.el.createChild();
34956 clayout.items = clayout.items || [];
34960 // replace this exitems with the clayout ones..
34961 xitems = clayout.items;
34963 // force background off if it's in center...
34964 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34965 cfg.background = false;
34967 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34970 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34971 //console.log('adding nested layout panel ' + cfg.toSource());
34972 this.add(region, ret);
34973 nb = {}; /// find first...
34978 // needs grid and region
34980 //var el = this.getRegion(region).el.createChild();
34982 *var el = this.el.createChild();
34983 // create the grid first...
34984 cfg.grid.container = el;
34985 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34988 if (region == 'center' && this.active ) {
34989 cfg.background = false;
34992 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34994 this.add(region, ret);
34996 if (cfg.background) {
34997 // render grid on panel activation (if panel background)
34998 ret.on('activate', function(gp) {
34999 if (!gp.grid.rendered) {
35000 // gp.grid.render(el);
35004 // cfg.grid.render(el);
35010 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35011 // it was the old xcomponent building that caused this before.
35012 // espeically if border is the top element in the tree.
35022 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35024 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35025 this.add(region, ret);
35029 throw "Can not add '" + cfg.xtype + "' to Border";
35035 this.beginUpdate();
35039 Roo.each(xitems, function(i) {
35040 region = nb && i.region ? i.region : false;
35042 var add = ret.addxtype(i);
35045 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35046 if (!i.background) {
35047 abn[region] = nb[region] ;
35054 // make the last non-background panel active..
35055 //if (nb) { Roo.log(abn); }
35058 for(var r in abn) {
35059 region = this.getRegion(r);
35061 // tried using nb[r], but it does not work..
35063 region.showPanel(abn[r]);
35074 factory : function(cfg)
35077 var validRegions = Roo.bootstrap.layout.Border.regions;
35079 var target = cfg.region;
35082 var r = Roo.bootstrap.layout;
35086 return new r.North(cfg);
35088 return new r.South(cfg);
35090 return new r.East(cfg);
35092 return new r.West(cfg);
35094 return new r.Center(cfg);
35096 throw 'Layout region "'+target+'" not supported.';
35103 * Ext JS Library 1.1.1
35104 * Copyright(c) 2006-2007, Ext JS, LLC.
35106 * Originally Released Under LGPL - original licence link has changed is not relivant.
35109 * <script type="text/javascript">
35113 * @class Roo.bootstrap.layout.Basic
35114 * @extends Roo.util.Observable
35115 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35116 * and does not have a titlebar, tabs or any other features. All it does is size and position
35117 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35118 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35119 * @cfg {string} region the region that it inhabits..
35120 * @cfg {bool} skipConfig skip config?
35124 Roo.bootstrap.layout.Basic = function(config){
35126 this.mgr = config.mgr;
35128 this.position = config.region;
35130 var skipConfig = config.skipConfig;
35134 * @scope Roo.BasicLayoutRegion
35138 * @event beforeremove
35139 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35140 * @param {Roo.LayoutRegion} this
35141 * @param {Roo.ContentPanel} panel The panel
35142 * @param {Object} e The cancel event object
35144 "beforeremove" : true,
35146 * @event invalidated
35147 * Fires when the layout for this region is changed.
35148 * @param {Roo.LayoutRegion} this
35150 "invalidated" : true,
35152 * @event visibilitychange
35153 * Fires when this region is shown or hidden
35154 * @param {Roo.LayoutRegion} this
35155 * @param {Boolean} visibility true or false
35157 "visibilitychange" : true,
35159 * @event paneladded
35160 * Fires when a panel is added.
35161 * @param {Roo.LayoutRegion} this
35162 * @param {Roo.ContentPanel} panel The panel
35164 "paneladded" : true,
35166 * @event panelremoved
35167 * Fires when a panel is removed.
35168 * @param {Roo.LayoutRegion} this
35169 * @param {Roo.ContentPanel} panel The panel
35171 "panelremoved" : true,
35173 * @event beforecollapse
35174 * Fires when this region before collapse.
35175 * @param {Roo.LayoutRegion} this
35177 "beforecollapse" : true,
35180 * Fires when this region is collapsed.
35181 * @param {Roo.LayoutRegion} this
35183 "collapsed" : true,
35186 * Fires when this region is expanded.
35187 * @param {Roo.LayoutRegion} this
35192 * Fires when this region is slid into view.
35193 * @param {Roo.LayoutRegion} this
35195 "slideshow" : true,
35198 * Fires when this region slides out of view.
35199 * @param {Roo.LayoutRegion} this
35201 "slidehide" : true,
35203 * @event panelactivated
35204 * Fires when a panel is activated.
35205 * @param {Roo.LayoutRegion} this
35206 * @param {Roo.ContentPanel} panel The activated panel
35208 "panelactivated" : true,
35211 * Fires when the user resizes this region.
35212 * @param {Roo.LayoutRegion} this
35213 * @param {Number} newSize The new size (width for east/west, height for north/south)
35217 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35218 this.panels = new Roo.util.MixedCollection();
35219 this.panels.getKey = this.getPanelId.createDelegate(this);
35221 this.activePanel = null;
35222 // ensure listeners are added...
35224 if (config.listeners || config.events) {
35225 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35226 listeners : config.listeners || {},
35227 events : config.events || {}
35231 if(skipConfig !== true){
35232 this.applyConfig(config);
35236 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35238 getPanelId : function(p){
35242 applyConfig : function(config){
35243 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35244 this.config = config;
35249 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35250 * the width, for horizontal (north, south) the height.
35251 * @param {Number} newSize The new width or height
35253 resizeTo : function(newSize){
35254 var el = this.el ? this.el :
35255 (this.activePanel ? this.activePanel.getEl() : null);
35257 switch(this.position){
35260 el.setWidth(newSize);
35261 this.fireEvent("resized", this, newSize);
35265 el.setHeight(newSize);
35266 this.fireEvent("resized", this, newSize);
35272 getBox : function(){
35273 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35276 getMargins : function(){
35277 return this.margins;
35280 updateBox : function(box){
35282 var el = this.activePanel.getEl();
35283 el.dom.style.left = box.x + "px";
35284 el.dom.style.top = box.y + "px";
35285 this.activePanel.setSize(box.width, box.height);
35289 * Returns the container element for this region.
35290 * @return {Roo.Element}
35292 getEl : function(){
35293 return this.activePanel;
35297 * Returns true if this region is currently visible.
35298 * @return {Boolean}
35300 isVisible : function(){
35301 return this.activePanel ? true : false;
35304 setActivePanel : function(panel){
35305 panel = this.getPanel(panel);
35306 if(this.activePanel && this.activePanel != panel){
35307 this.activePanel.setActiveState(false);
35308 this.activePanel.getEl().setLeftTop(-10000,-10000);
35310 this.activePanel = panel;
35311 panel.setActiveState(true);
35313 panel.setSize(this.box.width, this.box.height);
35315 this.fireEvent("panelactivated", this, panel);
35316 this.fireEvent("invalidated");
35320 * Show the specified panel.
35321 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35322 * @return {Roo.ContentPanel} The shown panel or null
35324 showPanel : function(panel){
35325 panel = this.getPanel(panel);
35327 this.setActivePanel(panel);
35333 * Get the active panel for this region.
35334 * @return {Roo.ContentPanel} The active panel or null
35336 getActivePanel : function(){
35337 return this.activePanel;
35341 * Add the passed ContentPanel(s)
35342 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35343 * @return {Roo.ContentPanel} The panel added (if only one was added)
35345 add : function(panel){
35346 if(arguments.length > 1){
35347 for(var i = 0, len = arguments.length; i < len; i++) {
35348 this.add(arguments[i]);
35352 if(this.hasPanel(panel)){
35353 this.showPanel(panel);
35356 var el = panel.getEl();
35357 if(el.dom.parentNode != this.mgr.el.dom){
35358 this.mgr.el.dom.appendChild(el.dom);
35360 if(panel.setRegion){
35361 panel.setRegion(this);
35363 this.panels.add(panel);
35364 el.setStyle("position", "absolute");
35365 if(!panel.background){
35366 this.setActivePanel(panel);
35367 if(this.config.initialSize && this.panels.getCount()==1){
35368 this.resizeTo(this.config.initialSize);
35371 this.fireEvent("paneladded", this, panel);
35376 * Returns true if the panel is in this region.
35377 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35378 * @return {Boolean}
35380 hasPanel : function(panel){
35381 if(typeof panel == "object"){ // must be panel obj
35382 panel = panel.getId();
35384 return this.getPanel(panel) ? true : false;
35388 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35389 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35390 * @param {Boolean} preservePanel Overrides the config preservePanel option
35391 * @return {Roo.ContentPanel} The panel that was removed
35393 remove : function(panel, preservePanel){
35394 panel = this.getPanel(panel);
35399 this.fireEvent("beforeremove", this, panel, e);
35400 if(e.cancel === true){
35403 var panelId = panel.getId();
35404 this.panels.removeKey(panelId);
35409 * Returns the panel specified or null if it's not in this region.
35410 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35411 * @return {Roo.ContentPanel}
35413 getPanel : function(id){
35414 if(typeof id == "object"){ // must be panel obj
35417 return this.panels.get(id);
35421 * Returns this regions position (north/south/east/west/center).
35424 getPosition: function(){
35425 return this.position;
35429 * Ext JS Library 1.1.1
35430 * Copyright(c) 2006-2007, Ext JS, LLC.
35432 * Originally Released Under LGPL - original licence link has changed is not relivant.
35435 * <script type="text/javascript">
35439 * @class Roo.bootstrap.layout.Region
35440 * @extends Roo.bootstrap.layout.Basic
35441 * This class represents a region in a layout manager.
35443 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35444 * @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})
35445 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35446 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35447 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35448 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35449 * @cfg {String} title The title for the region (overrides panel titles)
35450 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35451 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35452 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35453 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35454 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35455 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35456 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35457 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35458 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35459 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35461 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35462 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35463 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35464 * @cfg {Number} width For East/West panels
35465 * @cfg {Number} height For North/South panels
35466 * @cfg {Boolean} split To show the splitter
35467 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35469 * @cfg {string} cls Extra CSS classes to add to region
35471 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35472 * @cfg {string} region the region that it inhabits..
35475 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35476 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35478 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35479 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35480 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35482 Roo.bootstrap.layout.Region = function(config)
35484 this.applyConfig(config);
35486 var mgr = config.mgr;
35487 var pos = config.region;
35488 config.skipConfig = true;
35489 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35492 this.onRender(mgr.el);
35495 this.visible = true;
35496 this.collapsed = false;
35497 this.unrendered_panels = [];
35500 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35502 position: '', // set by wrapper (eg. north/south etc..)
35503 unrendered_panels : null, // unrendered panels.
35504 createBody : function(){
35505 /** This region's body element
35506 * @type Roo.Element */
35507 this.bodyEl = this.el.createChild({
35509 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35513 onRender: function(ctr, pos)
35515 var dh = Roo.DomHelper;
35516 /** This region's container element
35517 * @type Roo.Element */
35518 this.el = dh.append(ctr.dom, {
35520 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35522 /** This region's title element
35523 * @type Roo.Element */
35525 this.titleEl = dh.append(this.el.dom,
35528 unselectable: "on",
35529 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35531 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35532 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35535 this.titleEl.enableDisplayMode();
35536 /** This region's title text element
35537 * @type HTMLElement */
35538 this.titleTextEl = this.titleEl.dom.firstChild;
35539 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35541 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35542 this.closeBtn.enableDisplayMode();
35543 this.closeBtn.on("click", this.closeClicked, this);
35544 this.closeBtn.hide();
35546 this.createBody(this.config);
35547 if(this.config.hideWhenEmpty){
35549 this.on("paneladded", this.validateVisibility, this);
35550 this.on("panelremoved", this.validateVisibility, this);
35552 if(this.autoScroll){
35553 this.bodyEl.setStyle("overflow", "auto");
35555 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35557 //if(c.titlebar !== false){
35558 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35559 this.titleEl.hide();
35561 this.titleEl.show();
35562 if(this.config.title){
35563 this.titleTextEl.innerHTML = this.config.title;
35567 if(this.config.collapsed){
35568 this.collapse(true);
35570 if(this.config.hidden){
35574 if (this.unrendered_panels && this.unrendered_panels.length) {
35575 for (var i =0;i< this.unrendered_panels.length; i++) {
35576 this.add(this.unrendered_panels[i]);
35578 this.unrendered_panels = null;
35584 applyConfig : function(c)
35587 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35588 var dh = Roo.DomHelper;
35589 if(c.titlebar !== false){
35590 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35591 this.collapseBtn.on("click", this.collapse, this);
35592 this.collapseBtn.enableDisplayMode();
35594 if(c.showPin === true || this.showPin){
35595 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35596 this.stickBtn.enableDisplayMode();
35597 this.stickBtn.on("click", this.expand, this);
35598 this.stickBtn.hide();
35603 /** This region's collapsed element
35604 * @type Roo.Element */
35607 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35608 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35611 if(c.floatable !== false){
35612 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35613 this.collapsedEl.on("click", this.collapseClick, this);
35616 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35617 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35618 id: "message", unselectable: "on", style:{"float":"left"}});
35619 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35621 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35622 this.expandBtn.on("click", this.expand, this);
35626 if(this.collapseBtn){
35627 this.collapseBtn.setVisible(c.collapsible == true);
35630 this.cmargins = c.cmargins || this.cmargins ||
35631 (this.position == "west" || this.position == "east" ?
35632 {top: 0, left: 2, right:2, bottom: 0} :
35633 {top: 2, left: 0, right:0, bottom: 2});
35635 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35638 this.bottomTabs = c.tabPosition != "top";
35640 this.autoScroll = c.autoScroll || false;
35645 this.duration = c.duration || .30;
35646 this.slideDuration = c.slideDuration || .45;
35651 * Returns true if this region is currently visible.
35652 * @return {Boolean}
35654 isVisible : function(){
35655 return this.visible;
35659 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35660 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35662 //setCollapsedTitle : function(title){
35663 // title = title || " ";
35664 // if(this.collapsedTitleTextEl){
35665 // this.collapsedTitleTextEl.innerHTML = title;
35669 getBox : function(){
35671 // if(!this.collapsed){
35672 b = this.el.getBox(false, true);
35674 // b = this.collapsedEl.getBox(false, true);
35679 getMargins : function(){
35680 return this.margins;
35681 //return this.collapsed ? this.cmargins : this.margins;
35684 highlight : function(){
35685 this.el.addClass("x-layout-panel-dragover");
35688 unhighlight : function(){
35689 this.el.removeClass("x-layout-panel-dragover");
35692 updateBox : function(box)
35694 if (!this.bodyEl) {
35695 return; // not rendered yet..
35699 if(!this.collapsed){
35700 this.el.dom.style.left = box.x + "px";
35701 this.el.dom.style.top = box.y + "px";
35702 this.updateBody(box.width, box.height);
35704 this.collapsedEl.dom.style.left = box.x + "px";
35705 this.collapsedEl.dom.style.top = box.y + "px";
35706 this.collapsedEl.setSize(box.width, box.height);
35709 this.tabs.autoSizeTabs();
35713 updateBody : function(w, h)
35716 this.el.setWidth(w);
35717 w -= this.el.getBorderWidth("rl");
35718 if(this.config.adjustments){
35719 w += this.config.adjustments[0];
35722 if(h !== null && h > 0){
35723 this.el.setHeight(h);
35724 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35725 h -= this.el.getBorderWidth("tb");
35726 if(this.config.adjustments){
35727 h += this.config.adjustments[1];
35729 this.bodyEl.setHeight(h);
35731 h = this.tabs.syncHeight(h);
35734 if(this.panelSize){
35735 w = w !== null ? w : this.panelSize.width;
35736 h = h !== null ? h : this.panelSize.height;
35738 if(this.activePanel){
35739 var el = this.activePanel.getEl();
35740 w = w !== null ? w : el.getWidth();
35741 h = h !== null ? h : el.getHeight();
35742 this.panelSize = {width: w, height: h};
35743 this.activePanel.setSize(w, h);
35745 if(Roo.isIE && this.tabs){
35746 this.tabs.el.repaint();
35751 * Returns the container element for this region.
35752 * @return {Roo.Element}
35754 getEl : function(){
35759 * Hides this region.
35762 //if(!this.collapsed){
35763 this.el.dom.style.left = "-2000px";
35766 // this.collapsedEl.dom.style.left = "-2000px";
35767 // this.collapsedEl.hide();
35769 this.visible = false;
35770 this.fireEvent("visibilitychange", this, false);
35774 * Shows this region if it was previously hidden.
35777 //if(!this.collapsed){
35780 // this.collapsedEl.show();
35782 this.visible = true;
35783 this.fireEvent("visibilitychange", this, true);
35786 closeClicked : function(){
35787 if(this.activePanel){
35788 this.remove(this.activePanel);
35792 collapseClick : function(e){
35794 e.stopPropagation();
35797 e.stopPropagation();
35803 * Collapses this region.
35804 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35807 collapse : function(skipAnim, skipCheck = false){
35808 if(this.collapsed) {
35812 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35814 this.collapsed = true;
35816 this.split.el.hide();
35818 if(this.config.animate && skipAnim !== true){
35819 this.fireEvent("invalidated", this);
35820 this.animateCollapse();
35822 this.el.setLocation(-20000,-20000);
35824 this.collapsedEl.show();
35825 this.fireEvent("collapsed", this);
35826 this.fireEvent("invalidated", this);
35832 animateCollapse : function(){
35837 * Expands this region if it was previously collapsed.
35838 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35839 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35842 expand : function(e, skipAnim){
35844 e.stopPropagation();
35846 if(!this.collapsed || this.el.hasActiveFx()) {
35850 this.afterSlideIn();
35853 this.collapsed = false;
35854 if(this.config.animate && skipAnim !== true){
35855 this.animateExpand();
35859 this.split.el.show();
35861 this.collapsedEl.setLocation(-2000,-2000);
35862 this.collapsedEl.hide();
35863 this.fireEvent("invalidated", this);
35864 this.fireEvent("expanded", this);
35868 animateExpand : function(){
35872 initTabs : function()
35874 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35876 var ts = new Roo.bootstrap.panel.Tabs({
35877 el: this.bodyEl.dom,
35878 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35879 disableTooltips: this.config.disableTabTips,
35880 toolbar : this.config.toolbar
35883 if(this.config.hideTabs){
35884 ts.stripWrap.setDisplayed(false);
35887 ts.resizeTabs = this.config.resizeTabs === true;
35888 ts.minTabWidth = this.config.minTabWidth || 40;
35889 ts.maxTabWidth = this.config.maxTabWidth || 250;
35890 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35891 ts.monitorResize = false;
35892 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35893 ts.bodyEl.addClass('roo-layout-tabs-body');
35894 this.panels.each(this.initPanelAsTab, this);
35897 initPanelAsTab : function(panel){
35898 var ti = this.tabs.addTab(
35902 this.config.closeOnTab && panel.isClosable(),
35905 if(panel.tabTip !== undefined){
35906 ti.setTooltip(panel.tabTip);
35908 ti.on("activate", function(){
35909 this.setActivePanel(panel);
35912 if(this.config.closeOnTab){
35913 ti.on("beforeclose", function(t, e){
35915 this.remove(panel);
35919 panel.tabItem = ti;
35924 updatePanelTitle : function(panel, title)
35926 if(this.activePanel == panel){
35927 this.updateTitle(title);
35930 var ti = this.tabs.getTab(panel.getEl().id);
35932 if(panel.tabTip !== undefined){
35933 ti.setTooltip(panel.tabTip);
35938 updateTitle : function(title){
35939 if(this.titleTextEl && !this.config.title){
35940 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35944 setActivePanel : function(panel)
35946 panel = this.getPanel(panel);
35947 if(this.activePanel && this.activePanel != panel){
35948 if(this.activePanel.setActiveState(false) === false){
35952 this.activePanel = panel;
35953 panel.setActiveState(true);
35954 if(this.panelSize){
35955 panel.setSize(this.panelSize.width, this.panelSize.height);
35958 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35960 this.updateTitle(panel.getTitle());
35962 this.fireEvent("invalidated", this);
35964 this.fireEvent("panelactivated", this, panel);
35968 * Shows the specified panel.
35969 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35970 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35972 showPanel : function(panel)
35974 panel = this.getPanel(panel);
35977 var tab = this.tabs.getTab(panel.getEl().id);
35978 if(tab.isHidden()){
35979 this.tabs.unhideTab(tab.id);
35983 this.setActivePanel(panel);
35990 * Get the active panel for this region.
35991 * @return {Roo.ContentPanel} The active panel or null
35993 getActivePanel : function(){
35994 return this.activePanel;
35997 validateVisibility : function(){
35998 if(this.panels.getCount() < 1){
35999 this.updateTitle(" ");
36000 this.closeBtn.hide();
36003 if(!this.isVisible()){
36010 * Adds the passed ContentPanel(s) to this region.
36011 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36012 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36014 add : function(panel)
36016 if(arguments.length > 1){
36017 for(var i = 0, len = arguments.length; i < len; i++) {
36018 this.add(arguments[i]);
36023 // if we have not been rendered yet, then we can not really do much of this..
36024 if (!this.bodyEl) {
36025 this.unrendered_panels.push(panel);
36032 if(this.hasPanel(panel)){
36033 this.showPanel(panel);
36036 panel.setRegion(this);
36037 this.panels.add(panel);
36038 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36039 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36040 // and hide them... ???
36041 this.bodyEl.dom.appendChild(panel.getEl().dom);
36042 if(panel.background !== true){
36043 this.setActivePanel(panel);
36045 this.fireEvent("paneladded", this, panel);
36052 this.initPanelAsTab(panel);
36056 if(panel.background !== true){
36057 this.tabs.activate(panel.getEl().id);
36059 this.fireEvent("paneladded", this, panel);
36064 * Hides the tab for the specified panel.
36065 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36067 hidePanel : function(panel){
36068 if(this.tabs && (panel = this.getPanel(panel))){
36069 this.tabs.hideTab(panel.getEl().id);
36074 * Unhides the tab for a previously hidden panel.
36075 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36077 unhidePanel : function(panel){
36078 if(this.tabs && (panel = this.getPanel(panel))){
36079 this.tabs.unhideTab(panel.getEl().id);
36083 clearPanels : function(){
36084 while(this.panels.getCount() > 0){
36085 this.remove(this.panels.first());
36090 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36091 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36092 * @param {Boolean} preservePanel Overrides the config preservePanel option
36093 * @return {Roo.ContentPanel} The panel that was removed
36095 remove : function(panel, preservePanel)
36097 panel = this.getPanel(panel);
36102 this.fireEvent("beforeremove", this, panel, e);
36103 if(e.cancel === true){
36106 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36107 var panelId = panel.getId();
36108 this.panels.removeKey(panelId);
36110 document.body.appendChild(panel.getEl().dom);
36113 this.tabs.removeTab(panel.getEl().id);
36114 }else if (!preservePanel){
36115 this.bodyEl.dom.removeChild(panel.getEl().dom);
36117 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36118 var p = this.panels.first();
36119 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36120 tempEl.appendChild(p.getEl().dom);
36121 this.bodyEl.update("");
36122 this.bodyEl.dom.appendChild(p.getEl().dom);
36124 this.updateTitle(p.getTitle());
36126 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36127 this.setActivePanel(p);
36129 panel.setRegion(null);
36130 if(this.activePanel == panel){
36131 this.activePanel = null;
36133 if(this.config.autoDestroy !== false && preservePanel !== true){
36134 try{panel.destroy();}catch(e){}
36136 this.fireEvent("panelremoved", this, panel);
36141 * Returns the TabPanel component used by this region
36142 * @return {Roo.TabPanel}
36144 getTabs : function(){
36148 createTool : function(parentEl, className){
36149 var btn = Roo.DomHelper.append(parentEl, {
36151 cls: "x-layout-tools-button",
36154 cls: "roo-layout-tools-button-inner " + className,
36158 btn.addClassOnOver("roo-layout-tools-button-over");
36163 * Ext JS Library 1.1.1
36164 * Copyright(c) 2006-2007, Ext JS, LLC.
36166 * Originally Released Under LGPL - original licence link has changed is not relivant.
36169 * <script type="text/javascript">
36175 * @class Roo.SplitLayoutRegion
36176 * @extends Roo.LayoutRegion
36177 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36179 Roo.bootstrap.layout.Split = function(config){
36180 this.cursor = config.cursor;
36181 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36184 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36186 splitTip : "Drag to resize.",
36187 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36188 useSplitTips : false,
36190 applyConfig : function(config){
36191 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36194 onRender : function(ctr,pos) {
36196 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36197 if(!this.config.split){
36202 var splitEl = Roo.DomHelper.append(ctr.dom, {
36204 id: this.el.id + "-split",
36205 cls: "roo-layout-split roo-layout-split-"+this.position,
36208 /** The SplitBar for this region
36209 * @type Roo.SplitBar */
36210 // does not exist yet...
36211 Roo.log([this.position, this.orientation]);
36213 this.split = new Roo.bootstrap.SplitBar({
36214 dragElement : splitEl,
36215 resizingElement: this.el,
36216 orientation : this.orientation
36219 this.split.on("moved", this.onSplitMove, this);
36220 this.split.useShim = this.config.useShim === true;
36221 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36222 if(this.useSplitTips){
36223 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36225 //if(config.collapsible){
36226 // this.split.el.on("dblclick", this.collapse, this);
36229 if(typeof this.config.minSize != "undefined"){
36230 this.split.minSize = this.config.minSize;
36232 if(typeof this.config.maxSize != "undefined"){
36233 this.split.maxSize = this.config.maxSize;
36235 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36236 this.hideSplitter();
36241 getHMaxSize : function(){
36242 var cmax = this.config.maxSize || 10000;
36243 var center = this.mgr.getRegion("center");
36244 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36247 getVMaxSize : function(){
36248 var cmax = this.config.maxSize || 10000;
36249 var center = this.mgr.getRegion("center");
36250 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36253 onSplitMove : function(split, newSize){
36254 this.fireEvent("resized", this, newSize);
36258 * Returns the {@link Roo.SplitBar} for this region.
36259 * @return {Roo.SplitBar}
36261 getSplitBar : function(){
36266 this.hideSplitter();
36267 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36270 hideSplitter : function(){
36272 this.split.el.setLocation(-2000,-2000);
36273 this.split.el.hide();
36279 this.split.el.show();
36281 Roo.bootstrap.layout.Split.superclass.show.call(this);
36284 beforeSlide: function(){
36285 if(Roo.isGecko){// firefox overflow auto bug workaround
36286 this.bodyEl.clip();
36288 this.tabs.bodyEl.clip();
36290 if(this.activePanel){
36291 this.activePanel.getEl().clip();
36293 if(this.activePanel.beforeSlide){
36294 this.activePanel.beforeSlide();
36300 afterSlide : function(){
36301 if(Roo.isGecko){// firefox overflow auto bug workaround
36302 this.bodyEl.unclip();
36304 this.tabs.bodyEl.unclip();
36306 if(this.activePanel){
36307 this.activePanel.getEl().unclip();
36308 if(this.activePanel.afterSlide){
36309 this.activePanel.afterSlide();
36315 initAutoHide : function(){
36316 if(this.autoHide !== false){
36317 if(!this.autoHideHd){
36318 var st = new Roo.util.DelayedTask(this.slideIn, this);
36319 this.autoHideHd = {
36320 "mouseout": function(e){
36321 if(!e.within(this.el, true)){
36325 "mouseover" : function(e){
36331 this.el.on(this.autoHideHd);
36335 clearAutoHide : function(){
36336 if(this.autoHide !== false){
36337 this.el.un("mouseout", this.autoHideHd.mouseout);
36338 this.el.un("mouseover", this.autoHideHd.mouseover);
36342 clearMonitor : function(){
36343 Roo.get(document).un("click", this.slideInIf, this);
36346 // these names are backwards but not changed for compat
36347 slideOut : function(){
36348 if(this.isSlid || this.el.hasActiveFx()){
36351 this.isSlid = true;
36352 if(this.collapseBtn){
36353 this.collapseBtn.hide();
36355 this.closeBtnState = this.closeBtn.getStyle('display');
36356 this.closeBtn.hide();
36358 this.stickBtn.show();
36361 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36362 this.beforeSlide();
36363 this.el.setStyle("z-index", 10001);
36364 this.el.slideIn(this.getSlideAnchor(), {
36365 callback: function(){
36367 this.initAutoHide();
36368 Roo.get(document).on("click", this.slideInIf, this);
36369 this.fireEvent("slideshow", this);
36376 afterSlideIn : function(){
36377 this.clearAutoHide();
36378 this.isSlid = false;
36379 this.clearMonitor();
36380 this.el.setStyle("z-index", "");
36381 if(this.collapseBtn){
36382 this.collapseBtn.show();
36384 this.closeBtn.setStyle('display', this.closeBtnState);
36386 this.stickBtn.hide();
36388 this.fireEvent("slidehide", this);
36391 slideIn : function(cb){
36392 if(!this.isSlid || this.el.hasActiveFx()){
36396 this.isSlid = false;
36397 this.beforeSlide();
36398 this.el.slideOut(this.getSlideAnchor(), {
36399 callback: function(){
36400 this.el.setLeftTop(-10000, -10000);
36402 this.afterSlideIn();
36410 slideInIf : function(e){
36411 if(!e.within(this.el)){
36416 animateCollapse : function(){
36417 this.beforeSlide();
36418 this.el.setStyle("z-index", 20000);
36419 var anchor = this.getSlideAnchor();
36420 this.el.slideOut(anchor, {
36421 callback : function(){
36422 this.el.setStyle("z-index", "");
36423 this.collapsedEl.slideIn(anchor, {duration:.3});
36425 this.el.setLocation(-10000,-10000);
36427 this.fireEvent("collapsed", this);
36434 animateExpand : function(){
36435 this.beforeSlide();
36436 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36437 this.el.setStyle("z-index", 20000);
36438 this.collapsedEl.hide({
36441 this.el.slideIn(this.getSlideAnchor(), {
36442 callback : function(){
36443 this.el.setStyle("z-index", "");
36446 this.split.el.show();
36448 this.fireEvent("invalidated", this);
36449 this.fireEvent("expanded", this);
36477 getAnchor : function(){
36478 return this.anchors[this.position];
36481 getCollapseAnchor : function(){
36482 return this.canchors[this.position];
36485 getSlideAnchor : function(){
36486 return this.sanchors[this.position];
36489 getAlignAdj : function(){
36490 var cm = this.cmargins;
36491 switch(this.position){
36507 getExpandAdj : function(){
36508 var c = this.collapsedEl, cm = this.cmargins;
36509 switch(this.position){
36511 return [-(cm.right+c.getWidth()+cm.left), 0];
36514 return [cm.right+c.getWidth()+cm.left, 0];
36517 return [0, -(cm.top+cm.bottom+c.getHeight())];
36520 return [0, cm.top+cm.bottom+c.getHeight()];
36526 * Ext JS Library 1.1.1
36527 * Copyright(c) 2006-2007, Ext JS, LLC.
36529 * Originally Released Under LGPL - original licence link has changed is not relivant.
36532 * <script type="text/javascript">
36535 * These classes are private internal classes
36537 Roo.bootstrap.layout.Center = function(config){
36538 config.region = "center";
36539 Roo.bootstrap.layout.Region.call(this, config);
36540 this.visible = true;
36541 this.minWidth = config.minWidth || 20;
36542 this.minHeight = config.minHeight || 20;
36545 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36547 // center panel can't be hidden
36551 // center panel can't be hidden
36554 getMinWidth: function(){
36555 return this.minWidth;
36558 getMinHeight: function(){
36559 return this.minHeight;
36572 Roo.bootstrap.layout.North = function(config)
36574 config.region = 'north';
36575 config.cursor = 'n-resize';
36577 Roo.bootstrap.layout.Split.call(this, config);
36581 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36582 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36583 this.split.el.addClass("roo-layout-split-v");
36585 var size = config.initialSize || config.height;
36586 if(typeof size != "undefined"){
36587 this.el.setHeight(size);
36590 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36592 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36596 getBox : function(){
36597 if(this.collapsed){
36598 return this.collapsedEl.getBox();
36600 var box = this.el.getBox();
36602 box.height += this.split.el.getHeight();
36607 updateBox : function(box){
36608 if(this.split && !this.collapsed){
36609 box.height -= this.split.el.getHeight();
36610 this.split.el.setLeft(box.x);
36611 this.split.el.setTop(box.y+box.height);
36612 this.split.el.setWidth(box.width);
36614 if(this.collapsed){
36615 this.updateBody(box.width, null);
36617 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36625 Roo.bootstrap.layout.South = function(config){
36626 config.region = 'south';
36627 config.cursor = 's-resize';
36628 Roo.bootstrap.layout.Split.call(this, config);
36630 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36631 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36632 this.split.el.addClass("roo-layout-split-v");
36634 var size = config.initialSize || config.height;
36635 if(typeof size != "undefined"){
36636 this.el.setHeight(size);
36640 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36641 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36642 getBox : function(){
36643 if(this.collapsed){
36644 return this.collapsedEl.getBox();
36646 var box = this.el.getBox();
36648 var sh = this.split.el.getHeight();
36655 updateBox : function(box){
36656 if(this.split && !this.collapsed){
36657 var sh = this.split.el.getHeight();
36660 this.split.el.setLeft(box.x);
36661 this.split.el.setTop(box.y-sh);
36662 this.split.el.setWidth(box.width);
36664 if(this.collapsed){
36665 this.updateBody(box.width, null);
36667 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36671 Roo.bootstrap.layout.East = function(config){
36672 config.region = "east";
36673 config.cursor = "e-resize";
36674 Roo.bootstrap.layout.Split.call(this, config);
36676 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36677 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36678 this.split.el.addClass("roo-layout-split-h");
36680 var size = config.initialSize || config.width;
36681 if(typeof size != "undefined"){
36682 this.el.setWidth(size);
36685 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36686 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36687 getBox : function(){
36688 if(this.collapsed){
36689 return this.collapsedEl.getBox();
36691 var box = this.el.getBox();
36693 var sw = this.split.el.getWidth();
36700 updateBox : function(box){
36701 if(this.split && !this.collapsed){
36702 var sw = this.split.el.getWidth();
36704 this.split.el.setLeft(box.x);
36705 this.split.el.setTop(box.y);
36706 this.split.el.setHeight(box.height);
36709 if(this.collapsed){
36710 this.updateBody(null, box.height);
36712 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36716 Roo.bootstrap.layout.West = function(config){
36717 config.region = "west";
36718 config.cursor = "w-resize";
36720 Roo.bootstrap.layout.Split.call(this, config);
36722 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36723 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36724 this.split.el.addClass("roo-layout-split-h");
36728 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36729 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36731 onRender: function(ctr, pos)
36733 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36734 var size = this.config.initialSize || this.config.width;
36735 if(typeof size != "undefined"){
36736 this.el.setWidth(size);
36740 getBox : function(){
36741 if(this.collapsed){
36742 return this.collapsedEl.getBox();
36744 var box = this.el.getBox();
36746 box.width += this.split.el.getWidth();
36751 updateBox : function(box){
36752 if(this.split && !this.collapsed){
36753 var sw = this.split.el.getWidth();
36755 this.split.el.setLeft(box.x+box.width);
36756 this.split.el.setTop(box.y);
36757 this.split.el.setHeight(box.height);
36759 if(this.collapsed){
36760 this.updateBody(null, box.height);
36762 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36765 Roo.namespace("Roo.bootstrap.panel");/*
36767 * Ext JS Library 1.1.1
36768 * Copyright(c) 2006-2007, Ext JS, LLC.
36770 * Originally Released Under LGPL - original licence link has changed is not relivant.
36773 * <script type="text/javascript">
36776 * @class Roo.ContentPanel
36777 * @extends Roo.util.Observable
36778 * A basic ContentPanel element.
36779 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36780 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36781 * @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
36782 * @cfg {Boolean} closable True if the panel can be closed/removed
36783 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36784 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36785 * @cfg {Toolbar} toolbar A toolbar for this panel
36786 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36787 * @cfg {String} title The title for this panel
36788 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36789 * @cfg {String} url Calls {@link #setUrl} with this value
36790 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36791 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36792 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36793 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36794 * @cfg {Boolean} badges render the badges
36797 * Create a new ContentPanel.
36798 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36799 * @param {String/Object} config A string to set only the title or a config object
36800 * @param {String} content (optional) Set the HTML content for this panel
36801 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36803 Roo.bootstrap.panel.Content = function( config){
36805 this.tpl = config.tpl || false;
36807 var el = config.el;
36808 var content = config.content;
36810 if(config.autoCreate){ // xtype is available if this is called from factory
36813 this.el = Roo.get(el);
36814 if(!this.el && config && config.autoCreate){
36815 if(typeof config.autoCreate == "object"){
36816 if(!config.autoCreate.id){
36817 config.autoCreate.id = config.id||el;
36819 this.el = Roo.DomHelper.append(document.body,
36820 config.autoCreate, true);
36822 var elcfg = { tag: "div",
36823 cls: "roo-layout-inactive-content",
36827 elcfg.html = config.html;
36831 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36834 this.closable = false;
36835 this.loaded = false;
36836 this.active = false;
36839 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36841 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36843 this.wrapEl = this.el; //this.el.wrap();
36845 if (config.toolbar.items) {
36846 ti = config.toolbar.items ;
36847 delete config.toolbar.items ;
36851 this.toolbar.render(this.wrapEl, 'before');
36852 for(var i =0;i < ti.length;i++) {
36853 // Roo.log(['add child', items[i]]);
36854 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36856 this.toolbar.items = nitems;
36857 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36858 delete config.toolbar;
36862 // xtype created footer. - not sure if will work as we normally have to render first..
36863 if (this.footer && !this.footer.el && this.footer.xtype) {
36864 if (!this.wrapEl) {
36865 this.wrapEl = this.el.wrap();
36868 this.footer.container = this.wrapEl.createChild();
36870 this.footer = Roo.factory(this.footer, Roo);
36875 if(typeof config == "string"){
36876 this.title = config;
36878 Roo.apply(this, config);
36882 this.resizeEl = Roo.get(this.resizeEl, true);
36884 this.resizeEl = this.el;
36886 // handle view.xtype
36894 * Fires when this panel is activated.
36895 * @param {Roo.ContentPanel} this
36899 * @event deactivate
36900 * Fires when this panel is activated.
36901 * @param {Roo.ContentPanel} this
36903 "deactivate" : true,
36907 * Fires when this panel is resized if fitToFrame is true.
36908 * @param {Roo.ContentPanel} this
36909 * @param {Number} width The width after any component adjustments
36910 * @param {Number} height The height after any component adjustments
36916 * Fires when this tab is created
36917 * @param {Roo.ContentPanel} this
36928 if(this.autoScroll){
36929 this.resizeEl.setStyle("overflow", "auto");
36931 // fix randome scrolling
36932 //this.el.on('scroll', function() {
36933 // Roo.log('fix random scolling');
36934 // this.scrollTo('top',0);
36937 content = content || this.content;
36939 this.setContent(content);
36941 if(config && config.url){
36942 this.setUrl(this.url, this.params, this.loadOnce);
36947 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36949 if (this.view && typeof(this.view.xtype) != 'undefined') {
36950 this.view.el = this.el.appendChild(document.createElement("div"));
36951 this.view = Roo.factory(this.view);
36952 this.view.render && this.view.render(false, '');
36956 this.fireEvent('render', this);
36959 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36963 setRegion : function(region){
36964 this.region = region;
36965 this.setActiveClass(region && !this.background);
36969 setActiveClass: function(state)
36972 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36973 this.el.setStyle('position','relative');
36975 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36976 this.el.setStyle('position', 'absolute');
36981 * Returns the toolbar for this Panel if one was configured.
36982 * @return {Roo.Toolbar}
36984 getToolbar : function(){
36985 return this.toolbar;
36988 setActiveState : function(active)
36990 this.active = active;
36991 this.setActiveClass(active);
36993 if(this.fireEvent("deactivate", this) === false){
36998 this.fireEvent("activate", this);
37002 * Updates this panel's element
37003 * @param {String} content The new content
37004 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37006 setContent : function(content, loadScripts){
37007 this.el.update(content, loadScripts);
37010 ignoreResize : function(w, h){
37011 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37014 this.lastSize = {width: w, height: h};
37019 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37020 * @return {Roo.UpdateManager} The UpdateManager
37022 getUpdateManager : function(){
37023 return this.el.getUpdateManager();
37026 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37027 * @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:
37030 url: "your-url.php",
37031 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37032 callback: yourFunction,
37033 scope: yourObject, //(optional scope)
37036 text: "Loading...",
37041 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37042 * 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.
37043 * @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}
37044 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37045 * @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.
37046 * @return {Roo.ContentPanel} this
37049 var um = this.el.getUpdateManager();
37050 um.update.apply(um, arguments);
37056 * 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.
37057 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37058 * @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)
37059 * @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)
37060 * @return {Roo.UpdateManager} The UpdateManager
37062 setUrl : function(url, params, loadOnce){
37063 if(this.refreshDelegate){
37064 this.removeListener("activate", this.refreshDelegate);
37066 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37067 this.on("activate", this.refreshDelegate);
37068 return this.el.getUpdateManager();
37071 _handleRefresh : function(url, params, loadOnce){
37072 if(!loadOnce || !this.loaded){
37073 var updater = this.el.getUpdateManager();
37074 updater.update(url, params, this._setLoaded.createDelegate(this));
37078 _setLoaded : function(){
37079 this.loaded = true;
37083 * Returns this panel's id
37086 getId : function(){
37091 * Returns this panel's element - used by regiosn to add.
37092 * @return {Roo.Element}
37094 getEl : function(){
37095 return this.wrapEl || this.el;
37100 adjustForComponents : function(width, height)
37102 //Roo.log('adjustForComponents ');
37103 if(this.resizeEl != this.el){
37104 width -= this.el.getFrameWidth('lr');
37105 height -= this.el.getFrameWidth('tb');
37108 var te = this.toolbar.getEl();
37109 te.setWidth(width);
37110 height -= te.getHeight();
37113 var te = this.footer.getEl();
37114 te.setWidth(width);
37115 height -= te.getHeight();
37119 if(this.adjustments){
37120 width += this.adjustments[0];
37121 height += this.adjustments[1];
37123 return {"width": width, "height": height};
37126 setSize : function(width, height){
37127 if(this.fitToFrame && !this.ignoreResize(width, height)){
37128 if(this.fitContainer && this.resizeEl != this.el){
37129 this.el.setSize(width, height);
37131 var size = this.adjustForComponents(width, height);
37132 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37133 this.fireEvent('resize', this, size.width, size.height);
37138 * Returns this panel's title
37141 getTitle : function(){
37143 if (typeof(this.title) != 'object') {
37148 for (var k in this.title) {
37149 if (!this.title.hasOwnProperty(k)) {
37153 if (k.indexOf('-') >= 0) {
37154 var s = k.split('-');
37155 for (var i = 0; i<s.length; i++) {
37156 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37159 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37166 * Set this panel's title
37167 * @param {String} title
37169 setTitle : function(title){
37170 this.title = title;
37172 this.region.updatePanelTitle(this, title);
37177 * Returns true is this panel was configured to be closable
37178 * @return {Boolean}
37180 isClosable : function(){
37181 return this.closable;
37184 beforeSlide : function(){
37186 this.resizeEl.clip();
37189 afterSlide : function(){
37191 this.resizeEl.unclip();
37195 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37196 * Will fail silently if the {@link #setUrl} method has not been called.
37197 * This does not activate the panel, just updates its content.
37199 refresh : function(){
37200 if(this.refreshDelegate){
37201 this.loaded = false;
37202 this.refreshDelegate();
37207 * Destroys this panel
37209 destroy : function(){
37210 this.el.removeAllListeners();
37211 var tempEl = document.createElement("span");
37212 tempEl.appendChild(this.el.dom);
37213 tempEl.innerHTML = "";
37219 * form - if the content panel contains a form - this is a reference to it.
37220 * @type {Roo.form.Form}
37224 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37225 * This contains a reference to it.
37231 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37241 * @param {Object} cfg Xtype definition of item to add.
37245 getChildContainer: function () {
37246 return this.getEl();
37251 var ret = new Roo.factory(cfg);
37256 if (cfg.xtype.match(/^Form$/)) {
37259 //if (this.footer) {
37260 // el = this.footer.container.insertSibling(false, 'before');
37262 el = this.el.createChild();
37265 this.form = new Roo.form.Form(cfg);
37268 if ( this.form.allItems.length) {
37269 this.form.render(el.dom);
37273 // should only have one of theses..
37274 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37275 // views.. should not be just added - used named prop 'view''
37277 cfg.el = this.el.appendChild(document.createElement("div"));
37280 var ret = new Roo.factory(cfg);
37282 ret.render && ret.render(false, ''); // render blank..
37292 * @class Roo.bootstrap.panel.Grid
37293 * @extends Roo.bootstrap.panel.Content
37295 * Create a new GridPanel.
37296 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37297 * @param {Object} config A the config object
37303 Roo.bootstrap.panel.Grid = function(config)
37307 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37308 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37310 config.el = this.wrapper;
37311 //this.el = this.wrapper;
37313 if (config.container) {
37314 // ctor'ed from a Border/panel.grid
37317 this.wrapper.setStyle("overflow", "hidden");
37318 this.wrapper.addClass('roo-grid-container');
37323 if(config.toolbar){
37324 var tool_el = this.wrapper.createChild();
37325 this.toolbar = Roo.factory(config.toolbar);
37327 if (config.toolbar.items) {
37328 ti = config.toolbar.items ;
37329 delete config.toolbar.items ;
37333 this.toolbar.render(tool_el);
37334 for(var i =0;i < ti.length;i++) {
37335 // Roo.log(['add child', items[i]]);
37336 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37338 this.toolbar.items = nitems;
37340 delete config.toolbar;
37343 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37344 config.grid.scrollBody = true;;
37345 config.grid.monitorWindowResize = false; // turn off autosizing
37346 config.grid.autoHeight = false;
37347 config.grid.autoWidth = false;
37349 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37351 if (config.background) {
37352 // render grid on panel activation (if panel background)
37353 this.on('activate', function(gp) {
37354 if (!gp.grid.rendered) {
37355 gp.grid.render(this.wrapper);
37356 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37361 this.grid.render(this.wrapper);
37362 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37365 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37366 // ??? needed ??? config.el = this.wrapper;
37371 // xtype created footer. - not sure if will work as we normally have to render first..
37372 if (this.footer && !this.footer.el && this.footer.xtype) {
37374 var ctr = this.grid.getView().getFooterPanel(true);
37375 this.footer.dataSource = this.grid.dataSource;
37376 this.footer = Roo.factory(this.footer, Roo);
37377 this.footer.render(ctr);
37387 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37388 getId : function(){
37389 return this.grid.id;
37393 * Returns the grid for this panel
37394 * @return {Roo.bootstrap.Table}
37396 getGrid : function(){
37400 setSize : function(width, height){
37401 if(!this.ignoreResize(width, height)){
37402 var grid = this.grid;
37403 var size = this.adjustForComponents(width, height);
37404 var gridel = grid.getGridEl();
37405 gridel.setSize(size.width, size.height);
37407 var thd = grid.getGridEl().select('thead',true).first();
37408 var tbd = grid.getGridEl().select('tbody', true).first();
37410 tbd.setSize(width, height - thd.getHeight());
37419 beforeSlide : function(){
37420 this.grid.getView().scroller.clip();
37423 afterSlide : function(){
37424 this.grid.getView().scroller.unclip();
37427 destroy : function(){
37428 this.grid.destroy();
37430 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37435 * @class Roo.bootstrap.panel.Nest
37436 * @extends Roo.bootstrap.panel.Content
37438 * Create a new Panel, that can contain a layout.Border.
37441 * @param {Roo.BorderLayout} layout The layout for this panel
37442 * @param {String/Object} config A string to set only the title or a config object
37444 Roo.bootstrap.panel.Nest = function(config)
37446 // construct with only one argument..
37447 /* FIXME - implement nicer consturctors
37448 if (layout.layout) {
37450 layout = config.layout;
37451 delete config.layout;
37453 if (layout.xtype && !layout.getEl) {
37454 // then layout needs constructing..
37455 layout = Roo.factory(layout, Roo);
37459 config.el = config.layout.getEl();
37461 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37463 config.layout.monitorWindowResize = false; // turn off autosizing
37464 this.layout = config.layout;
37465 this.layout.getEl().addClass("roo-layout-nested-layout");
37472 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37474 setSize : function(width, height){
37475 if(!this.ignoreResize(width, height)){
37476 var size = this.adjustForComponents(width, height);
37477 var el = this.layout.getEl();
37478 if (size.height < 1) {
37479 el.setWidth(size.width);
37481 el.setSize(size.width, size.height);
37483 var touch = el.dom.offsetWidth;
37484 this.layout.layout();
37485 // ie requires a double layout on the first pass
37486 if(Roo.isIE && !this.initialized){
37487 this.initialized = true;
37488 this.layout.layout();
37493 // activate all subpanels if not currently active..
37495 setActiveState : function(active){
37496 this.active = active;
37497 this.setActiveClass(active);
37500 this.fireEvent("deactivate", this);
37504 this.fireEvent("activate", this);
37505 // not sure if this should happen before or after..
37506 if (!this.layout) {
37507 return; // should not happen..
37510 for (var r in this.layout.regions) {
37511 reg = this.layout.getRegion(r);
37512 if (reg.getActivePanel()) {
37513 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37514 reg.setActivePanel(reg.getActivePanel());
37517 if (!reg.panels.length) {
37520 reg.showPanel(reg.getPanel(0));
37529 * Returns the nested BorderLayout for this panel
37530 * @return {Roo.BorderLayout}
37532 getLayout : function(){
37533 return this.layout;
37537 * Adds a xtype elements to the layout of the nested panel
37541 xtype : 'ContentPanel',
37548 xtype : 'NestedLayoutPanel',
37554 items : [ ... list of content panels or nested layout panels.. ]
37558 * @param {Object} cfg Xtype definition of item to add.
37560 addxtype : function(cfg) {
37561 return this.layout.addxtype(cfg);
37566 * Ext JS Library 1.1.1
37567 * Copyright(c) 2006-2007, Ext JS, LLC.
37569 * Originally Released Under LGPL - original licence link has changed is not relivant.
37572 * <script type="text/javascript">
37575 * @class Roo.TabPanel
37576 * @extends Roo.util.Observable
37577 * A lightweight tab container.
37581 // basic tabs 1, built from existing content
37582 var tabs = new Roo.TabPanel("tabs1");
37583 tabs.addTab("script", "View Script");
37584 tabs.addTab("markup", "View Markup");
37585 tabs.activate("script");
37587 // more advanced tabs, built from javascript
37588 var jtabs = new Roo.TabPanel("jtabs");
37589 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37591 // set up the UpdateManager
37592 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37593 var updater = tab2.getUpdateManager();
37594 updater.setDefaultUrl("ajax1.htm");
37595 tab2.on('activate', updater.refresh, updater, true);
37597 // Use setUrl for Ajax loading
37598 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37599 tab3.setUrl("ajax2.htm", null, true);
37602 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37605 jtabs.activate("jtabs-1");
37608 * Create a new TabPanel.
37609 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37610 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37612 Roo.bootstrap.panel.Tabs = function(config){
37614 * The container element for this TabPanel.
37615 * @type Roo.Element
37617 this.el = Roo.get(config.el);
37620 if(typeof config == "boolean"){
37621 this.tabPosition = config ? "bottom" : "top";
37623 Roo.apply(this, config);
37627 if(this.tabPosition == "bottom"){
37628 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37629 this.el.addClass("roo-tabs-bottom");
37631 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37632 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37633 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37635 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37637 if(this.tabPosition != "bottom"){
37638 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37639 * @type Roo.Element
37641 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37642 this.el.addClass("roo-tabs-top");
37646 this.bodyEl.setStyle("position", "relative");
37648 this.active = null;
37649 this.activateDelegate = this.activate.createDelegate(this);
37654 * Fires when the active tab changes
37655 * @param {Roo.TabPanel} this
37656 * @param {Roo.TabPanelItem} activePanel The new active tab
37660 * @event beforetabchange
37661 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37662 * @param {Roo.TabPanel} this
37663 * @param {Object} e Set cancel to true on this object to cancel the tab change
37664 * @param {Roo.TabPanelItem} tab The tab being changed to
37666 "beforetabchange" : true
37669 Roo.EventManager.onWindowResize(this.onResize, this);
37670 this.cpad = this.el.getPadding("lr");
37671 this.hiddenCount = 0;
37674 // toolbar on the tabbar support...
37675 if (this.toolbar) {
37676 alert("no toolbar support yet");
37677 this.toolbar = false;
37679 var tcfg = this.toolbar;
37680 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37681 this.toolbar = new Roo.Toolbar(tcfg);
37682 if (Roo.isSafari) {
37683 var tbl = tcfg.container.child('table', true);
37684 tbl.setAttribute('width', '100%');
37692 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37695 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37697 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37699 tabPosition : "top",
37701 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37703 currentTabWidth : 0,
37705 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37709 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37713 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37715 preferredTabWidth : 175,
37717 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37719 resizeTabs : false,
37721 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37723 monitorResize : true,
37725 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37730 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37731 * @param {String} id The id of the div to use <b>or create</b>
37732 * @param {String} text The text for the tab
37733 * @param {String} content (optional) Content to put in the TabPanelItem body
37734 * @param {Boolean} closable (optional) True to create a close icon on the tab
37735 * @return {Roo.TabPanelItem} The created TabPanelItem
37737 addTab : function(id, text, content, closable, tpl)
37739 var item = new Roo.bootstrap.panel.TabItem({
37743 closable : closable,
37746 this.addTabItem(item);
37748 item.setContent(content);
37754 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37755 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37756 * @return {Roo.TabPanelItem}
37758 getTab : function(id){
37759 return this.items[id];
37763 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37764 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37766 hideTab : function(id){
37767 var t = this.items[id];
37770 this.hiddenCount++;
37771 this.autoSizeTabs();
37776 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37777 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37779 unhideTab : function(id){
37780 var t = this.items[id];
37782 t.setHidden(false);
37783 this.hiddenCount--;
37784 this.autoSizeTabs();
37789 * Adds an existing {@link Roo.TabPanelItem}.
37790 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37792 addTabItem : function(item){
37793 this.items[item.id] = item;
37794 this.items.push(item);
37795 // if(this.resizeTabs){
37796 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37797 // this.autoSizeTabs();
37799 // item.autoSize();
37804 * Removes a {@link Roo.TabPanelItem}.
37805 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37807 removeTab : function(id){
37808 var items = this.items;
37809 var tab = items[id];
37810 if(!tab) { return; }
37811 var index = items.indexOf(tab);
37812 if(this.active == tab && items.length > 1){
37813 var newTab = this.getNextAvailable(index);
37818 this.stripEl.dom.removeChild(tab.pnode.dom);
37819 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37820 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37822 items.splice(index, 1);
37823 delete this.items[tab.id];
37824 tab.fireEvent("close", tab);
37825 tab.purgeListeners();
37826 this.autoSizeTabs();
37829 getNextAvailable : function(start){
37830 var items = this.items;
37832 // look for a next tab that will slide over to
37833 // replace the one being removed
37834 while(index < items.length){
37835 var item = items[++index];
37836 if(item && !item.isHidden()){
37840 // if one isn't found select the previous tab (on the left)
37843 var item = items[--index];
37844 if(item && !item.isHidden()){
37852 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37853 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37855 disableTab : function(id){
37856 var tab = this.items[id];
37857 if(tab && this.active != tab){
37863 * Enables a {@link Roo.TabPanelItem} that is disabled.
37864 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37866 enableTab : function(id){
37867 var tab = this.items[id];
37872 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37873 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37874 * @return {Roo.TabPanelItem} The TabPanelItem.
37876 activate : function(id){
37877 var tab = this.items[id];
37881 if(tab == this.active || tab.disabled){
37885 this.fireEvent("beforetabchange", this, e, tab);
37886 if(e.cancel !== true && !tab.disabled){
37888 this.active.hide();
37890 this.active = this.items[id];
37891 this.active.show();
37892 this.fireEvent("tabchange", this, this.active);
37898 * Gets the active {@link Roo.TabPanelItem}.
37899 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37901 getActiveTab : function(){
37902 return this.active;
37906 * Updates the tab body element to fit the height of the container element
37907 * for overflow scrolling
37908 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37910 syncHeight : function(targetHeight){
37911 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37912 var bm = this.bodyEl.getMargins();
37913 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37914 this.bodyEl.setHeight(newHeight);
37918 onResize : function(){
37919 if(this.monitorResize){
37920 this.autoSizeTabs();
37925 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37927 beginUpdate : function(){
37928 this.updating = true;
37932 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37934 endUpdate : function(){
37935 this.updating = false;
37936 this.autoSizeTabs();
37940 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37942 autoSizeTabs : function(){
37943 var count = this.items.length;
37944 var vcount = count - this.hiddenCount;
37945 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37948 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37949 var availWidth = Math.floor(w / vcount);
37950 var b = this.stripBody;
37951 if(b.getWidth() > w){
37952 var tabs = this.items;
37953 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37954 if(availWidth < this.minTabWidth){
37955 /*if(!this.sleft){ // incomplete scrolling code
37956 this.createScrollButtons();
37959 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37962 if(this.currentTabWidth < this.preferredTabWidth){
37963 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37969 * Returns the number of tabs in this TabPanel.
37972 getCount : function(){
37973 return this.items.length;
37977 * Resizes all the tabs to the passed width
37978 * @param {Number} The new width
37980 setTabWidth : function(width){
37981 this.currentTabWidth = width;
37982 for(var i = 0, len = this.items.length; i < len; i++) {
37983 if(!this.items[i].isHidden()) {
37984 this.items[i].setWidth(width);
37990 * Destroys this TabPanel
37991 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37993 destroy : function(removeEl){
37994 Roo.EventManager.removeResizeListener(this.onResize, this);
37995 for(var i = 0, len = this.items.length; i < len; i++){
37996 this.items[i].purgeListeners();
37998 if(removeEl === true){
37999 this.el.update("");
38004 createStrip : function(container)
38006 var strip = document.createElement("nav");
38007 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38008 container.appendChild(strip);
38012 createStripList : function(strip)
38014 // div wrapper for retard IE
38015 // returns the "tr" element.
38016 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38017 //'<div class="x-tabs-strip-wrap">'+
38018 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38019 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38020 return strip.firstChild; //.firstChild.firstChild.firstChild;
38022 createBody : function(container)
38024 var body = document.createElement("div");
38025 Roo.id(body, "tab-body");
38026 //Roo.fly(body).addClass("x-tabs-body");
38027 Roo.fly(body).addClass("tab-content");
38028 container.appendChild(body);
38031 createItemBody :function(bodyEl, id){
38032 var body = Roo.getDom(id);
38034 body = document.createElement("div");
38037 //Roo.fly(body).addClass("x-tabs-item-body");
38038 Roo.fly(body).addClass("tab-pane");
38039 bodyEl.insertBefore(body, bodyEl.firstChild);
38043 createStripElements : function(stripEl, text, closable, tpl)
38045 var td = document.createElement("li"); // was td..
38048 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38051 stripEl.appendChild(td);
38053 td.className = "x-tabs-closable";
38054 if(!this.closeTpl){
38055 this.closeTpl = new Roo.Template(
38056 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38057 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38058 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38061 var el = this.closeTpl.overwrite(td, {"text": text});
38062 var close = el.getElementsByTagName("div")[0];
38063 var inner = el.getElementsByTagName("em")[0];
38064 return {"el": el, "close": close, "inner": inner};
38067 // not sure what this is..
38068 // if(!this.tabTpl){
38069 //this.tabTpl = new Roo.Template(
38070 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38071 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38073 // this.tabTpl = new Roo.Template(
38074 // '<a href="#">' +
38075 // '<span unselectable="on"' +
38076 // (this.disableTooltips ? '' : ' title="{text}"') +
38077 // ' >{text}</span></a>'
38083 var template = tpl || this.tabTpl || false;
38087 template = new Roo.Template(
38089 '<span unselectable="on"' +
38090 (this.disableTooltips ? '' : ' title="{text}"') +
38091 ' >{text}</span></a>'
38095 switch (typeof(template)) {
38099 template = new Roo.Template(template);
38105 var el = template.overwrite(td, {"text": text});
38107 var inner = el.getElementsByTagName("span")[0];
38109 return {"el": el, "inner": inner};
38117 * @class Roo.TabPanelItem
38118 * @extends Roo.util.Observable
38119 * Represents an individual item (tab plus body) in a TabPanel.
38120 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38121 * @param {String} id The id of this TabPanelItem
38122 * @param {String} text The text for the tab of this TabPanelItem
38123 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38125 Roo.bootstrap.panel.TabItem = function(config){
38127 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38128 * @type Roo.TabPanel
38130 this.tabPanel = config.panel;
38132 * The id for this TabPanelItem
38135 this.id = config.id;
38137 this.disabled = false;
38139 this.text = config.text;
38141 this.loaded = false;
38142 this.closable = config.closable;
38145 * The body element for this TabPanelItem.
38146 * @type Roo.Element
38148 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38149 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38150 this.bodyEl.setStyle("display", "block");
38151 this.bodyEl.setStyle("zoom", "1");
38152 //this.hideAction();
38154 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38156 this.el = Roo.get(els.el);
38157 this.inner = Roo.get(els.inner, true);
38158 this.textEl = Roo.get(this.el.dom.firstChild, true);
38159 this.pnode = Roo.get(els.el.parentNode, true);
38160 // this.el.on("mousedown", this.onTabMouseDown, this);
38161 this.el.on("click", this.onTabClick, this);
38163 if(config.closable){
38164 var c = Roo.get(els.close, true);
38165 c.dom.title = this.closeText;
38166 c.addClassOnOver("close-over");
38167 c.on("click", this.closeClick, this);
38173 * Fires when this tab becomes the active tab.
38174 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38175 * @param {Roo.TabPanelItem} this
38179 * @event beforeclose
38180 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38181 * @param {Roo.TabPanelItem} this
38182 * @param {Object} e Set cancel to true on this object to cancel the close.
38184 "beforeclose": true,
38187 * Fires when this tab is closed.
38188 * @param {Roo.TabPanelItem} this
38192 * @event deactivate
38193 * Fires when this tab is no longer the active tab.
38194 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38195 * @param {Roo.TabPanelItem} this
38197 "deactivate" : true
38199 this.hidden = false;
38201 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38204 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38206 purgeListeners : function(){
38207 Roo.util.Observable.prototype.purgeListeners.call(this);
38208 this.el.removeAllListeners();
38211 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38214 this.pnode.addClass("active");
38217 this.tabPanel.stripWrap.repaint();
38219 this.fireEvent("activate", this.tabPanel, this);
38223 * Returns true if this tab is the active tab.
38224 * @return {Boolean}
38226 isActive : function(){
38227 return this.tabPanel.getActiveTab() == this;
38231 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38234 this.pnode.removeClass("active");
38236 this.fireEvent("deactivate", this.tabPanel, this);
38239 hideAction : function(){
38240 this.bodyEl.hide();
38241 this.bodyEl.setStyle("position", "absolute");
38242 this.bodyEl.setLeft("-20000px");
38243 this.bodyEl.setTop("-20000px");
38246 showAction : function(){
38247 this.bodyEl.setStyle("position", "relative");
38248 this.bodyEl.setTop("");
38249 this.bodyEl.setLeft("");
38250 this.bodyEl.show();
38254 * Set the tooltip for the tab.
38255 * @param {String} tooltip The tab's tooltip
38257 setTooltip : function(text){
38258 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38259 this.textEl.dom.qtip = text;
38260 this.textEl.dom.removeAttribute('title');
38262 this.textEl.dom.title = text;
38266 onTabClick : function(e){
38267 e.preventDefault();
38268 this.tabPanel.activate(this.id);
38271 onTabMouseDown : function(e){
38272 e.preventDefault();
38273 this.tabPanel.activate(this.id);
38276 getWidth : function(){
38277 return this.inner.getWidth();
38280 setWidth : function(width){
38281 var iwidth = width - this.pnode.getPadding("lr");
38282 this.inner.setWidth(iwidth);
38283 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38284 this.pnode.setWidth(width);
38288 * Show or hide the tab
38289 * @param {Boolean} hidden True to hide or false to show.
38291 setHidden : function(hidden){
38292 this.hidden = hidden;
38293 this.pnode.setStyle("display", hidden ? "none" : "");
38297 * Returns true if this tab is "hidden"
38298 * @return {Boolean}
38300 isHidden : function(){
38301 return this.hidden;
38305 * Returns the text for this tab
38308 getText : function(){
38312 autoSize : function(){
38313 //this.el.beginMeasure();
38314 this.textEl.setWidth(1);
38316 * #2804 [new] Tabs in Roojs
38317 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38319 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38320 //this.el.endMeasure();
38324 * Sets the text for the tab (Note: this also sets the tooltip text)
38325 * @param {String} text The tab's text and tooltip
38327 setText : function(text){
38329 this.textEl.update(text);
38330 this.setTooltip(text);
38331 //if(!this.tabPanel.resizeTabs){
38332 // this.autoSize();
38336 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38338 activate : function(){
38339 this.tabPanel.activate(this.id);
38343 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38345 disable : function(){
38346 if(this.tabPanel.active != this){
38347 this.disabled = true;
38348 this.pnode.addClass("disabled");
38353 * Enables this TabPanelItem if it was previously disabled.
38355 enable : function(){
38356 this.disabled = false;
38357 this.pnode.removeClass("disabled");
38361 * Sets the content for this TabPanelItem.
38362 * @param {String} content The content
38363 * @param {Boolean} loadScripts true to look for and load scripts
38365 setContent : function(content, loadScripts){
38366 this.bodyEl.update(content, loadScripts);
38370 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38371 * @return {Roo.UpdateManager} The UpdateManager
38373 getUpdateManager : function(){
38374 return this.bodyEl.getUpdateManager();
38378 * Set a URL to be used to load the content for this TabPanelItem.
38379 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38380 * @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)
38381 * @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)
38382 * @return {Roo.UpdateManager} The UpdateManager
38384 setUrl : function(url, params, loadOnce){
38385 if(this.refreshDelegate){
38386 this.un('activate', this.refreshDelegate);
38388 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38389 this.on("activate", this.refreshDelegate);
38390 return this.bodyEl.getUpdateManager();
38394 _handleRefresh : function(url, params, loadOnce){
38395 if(!loadOnce || !this.loaded){
38396 var updater = this.bodyEl.getUpdateManager();
38397 updater.update(url, params, this._setLoaded.createDelegate(this));
38402 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38403 * Will fail silently if the setUrl method has not been called.
38404 * This does not activate the panel, just updates its content.
38406 refresh : function(){
38407 if(this.refreshDelegate){
38408 this.loaded = false;
38409 this.refreshDelegate();
38414 _setLoaded : function(){
38415 this.loaded = true;
38419 closeClick : function(e){
38422 this.fireEvent("beforeclose", this, o);
38423 if(o.cancel !== true){
38424 this.tabPanel.removeTab(this.id);
38428 * The text displayed in the tooltip for the close icon.
38431 closeText : "Close this tab"
38434 * This script refer to:
38435 * Title: International Telephone Input
38436 * Author: Jack O'Connor
38437 * Code version: v12.1.12
38438 * Availability: https://github.com/jackocnr/intl-tel-input.git
38441 Roo.bootstrap.PhoneInputData = function() {
38444 "Afghanistan (افغانستان)",
38449 "Albania (Shqipëri)",
38454 "Algeria (الجزائر)",
38479 "Antigua and Barbuda",
38489 "Armenia (Հայաստան)",
38505 "Austria (Österreich)",
38510 "Azerbaijan (Azərbaycan)",
38520 "Bahrain (البحرين)",
38525 "Bangladesh (বাংলাদেশ)",
38535 "Belarus (Беларусь)",
38540 "Belgium (België)",
38570 "Bosnia and Herzegovina (Босна и Херцеговина)",
38585 "British Indian Ocean Territory",
38590 "British Virgin Islands",
38600 "Bulgaria (България)",
38610 "Burundi (Uburundi)",
38615 "Cambodia (កម្ពុជា)",
38620 "Cameroon (Cameroun)",
38629 ["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"]
38632 "Cape Verde (Kabu Verdi)",
38637 "Caribbean Netherlands",
38648 "Central African Republic (République centrafricaine)",
38668 "Christmas Island",
38674 "Cocos (Keeling) Islands",
38685 "Comoros (جزر القمر)",
38690 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38695 "Congo (Republic) (Congo-Brazzaville)",
38715 "Croatia (Hrvatska)",
38736 "Czech Republic (Česká republika)",
38741 "Denmark (Danmark)",
38756 "Dominican Republic (República Dominicana)",
38760 ["809", "829", "849"]
38778 "Equatorial Guinea (Guinea Ecuatorial)",
38798 "Falkland Islands (Islas Malvinas)",
38803 "Faroe Islands (Føroyar)",
38824 "French Guiana (Guyane française)",
38829 "French Polynesia (Polynésie française)",
38844 "Georgia (საქართველო)",
38849 "Germany (Deutschland)",
38869 "Greenland (Kalaallit Nunaat)",
38906 "Guinea-Bissau (Guiné Bissau)",
38931 "Hungary (Magyarország)",
38936 "Iceland (Ísland)",
38956 "Iraq (العراق)",
38972 "Israel (ישראל)",
38999 "Jordan (الأردن)",
39004 "Kazakhstan (Казахстан)",
39025 "Kuwait (الكويت)",
39030 "Kyrgyzstan (Кыргызстан)",
39040 "Latvia (Latvija)",
39045 "Lebanon (لبنان)",
39060 "Libya (ليبيا)",
39070 "Lithuania (Lietuva)",
39085 "Macedonia (FYROM) (Македонија)",
39090 "Madagascar (Madagasikara)",
39120 "Marshall Islands",
39130 "Mauritania (موريتانيا)",
39135 "Mauritius (Moris)",
39156 "Moldova (Republica Moldova)",
39166 "Mongolia (Монгол)",
39171 "Montenegro (Crna Gora)",
39181 "Morocco (المغرب)",
39187 "Mozambique (Moçambique)",
39192 "Myanmar (Burma) (မြန်မာ)",
39197 "Namibia (Namibië)",
39212 "Netherlands (Nederland)",
39217 "New Caledonia (Nouvelle-Calédonie)",
39252 "North Korea (조선 민주주의 인민 공화국)",
39257 "Northern Mariana Islands",
39273 "Pakistan (پاکستان)",
39283 "Palestine (فلسطين)",
39293 "Papua New Guinea",
39335 "Réunion (La Réunion)",
39341 "Romania (România)",
39357 "Saint Barthélemy",
39368 "Saint Kitts and Nevis",
39378 "Saint Martin (Saint-Martin (partie française))",
39384 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39389 "Saint Vincent and the Grenadines",
39404 "São Tomé and Príncipe (São Tomé e Príncipe)",
39409 "Saudi Arabia (المملكة العربية السعودية)",
39414 "Senegal (Sénégal)",
39444 "Slovakia (Slovensko)",
39449 "Slovenia (Slovenija)",
39459 "Somalia (Soomaaliya)",
39469 "South Korea (대한민국)",
39474 "South Sudan (جنوب السودان)",
39484 "Sri Lanka (ශ්රී ලංකාව)",
39489 "Sudan (السودان)",
39499 "Svalbard and Jan Mayen",
39510 "Sweden (Sverige)",
39515 "Switzerland (Schweiz)",
39520 "Syria (سوريا)",
39565 "Trinidad and Tobago",
39570 "Tunisia (تونس)",
39575 "Turkey (Türkiye)",
39585 "Turks and Caicos Islands",
39595 "U.S. Virgin Islands",
39605 "Ukraine (Україна)",
39610 "United Arab Emirates (الإمارات العربية المتحدة)",
39632 "Uzbekistan (Oʻzbekiston)",
39642 "Vatican City (Città del Vaticano)",
39653 "Vietnam (Việt Nam)",
39658 "Wallis and Futuna (Wallis-et-Futuna)",
39663 "Western Sahara (الصحراء الغربية)",
39669 "Yemen (اليمن)",
39693 * This script refer to:
39694 * Title: International Telephone Input
39695 * Author: Jack O'Connor
39696 * Code version: v12.1.12
39697 * Availability: https://github.com/jackocnr/intl-tel-input.git
39701 * @class Roo.bootstrap.PhoneInput
39702 * @extends Roo.bootstrap.TriggerField
39703 * An input with International dial-code selection
39705 * @cfg {String} defaultDialCode default '+852'
39706 * @cfg {Array} preferedCountries default []
39709 * Create a new PhoneInput.
39710 * @param {Object} config Configuration options
39713 Roo.bootstrap.PhoneInput = function(config) {
39714 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39717 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39719 listWidth: undefined,
39721 selectedClass: 'active',
39723 invalidClass : "has-warning",
39725 validClass: 'has-success',
39727 allowed: '0123456789',
39730 * @cfg {String} defaultDialCode The default dial code when initializing the input
39732 defaultDialCode: '+852',
39735 * @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
39737 preferedCountries: false,
39739 getAutoCreate : function()
39741 var data = Roo.bootstrap.PhoneInputData();
39742 var align = this.labelAlign || this.parentLabelAlign();
39745 this.allCountries = [];
39746 this.dialCodeMapping = [];
39748 for (var i = 0; i < data.length; i++) {
39750 this.allCountries[i] = {
39754 priority: c[3] || 0,
39755 areaCodes: c[4] || null
39757 this.dialCodeMapping[c[2]] = {
39760 priority: c[3] || 0,
39761 areaCodes: c[4] || null
39773 cls : 'form-control tel-input',
39774 autocomplete: 'new-password'
39777 var hiddenInput = {
39780 cls: 'hidden-tel-input'
39784 hiddenInput.name = this.name;
39787 if (this.disabled) {
39788 input.disabled = true;
39791 var flag_container = {
39808 cls: this.hasFeedback ? 'has-feedback' : '',
39814 cls: 'dial-code-holder',
39821 cls: 'roo-select2-container input-group',
39828 if (this.fieldLabel.length) {
39831 tooltip: 'This field is required'
39837 cls: 'control-label',
39843 html: this.fieldLabel
39846 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39852 if(this.indicatorpos == 'right') {
39853 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39860 if(align == 'left') {
39868 if(this.labelWidth > 12){
39869 label.style = "width: " + this.labelWidth + 'px';
39871 if(this.labelWidth < 13 && this.labelmd == 0){
39872 this.labelmd = this.labelWidth;
39874 if(this.labellg > 0){
39875 label.cls += ' col-lg-' + this.labellg;
39876 input.cls += ' col-lg-' + (12 - this.labellg);
39878 if(this.labelmd > 0){
39879 label.cls += ' col-md-' + this.labelmd;
39880 container.cls += ' col-md-' + (12 - this.labelmd);
39882 if(this.labelsm > 0){
39883 label.cls += ' col-sm-' + this.labelsm;
39884 container.cls += ' col-sm-' + (12 - this.labelsm);
39886 if(this.labelxs > 0){
39887 label.cls += ' col-xs-' + this.labelxs;
39888 container.cls += ' col-xs-' + (12 - this.labelxs);
39898 var settings = this;
39900 ['xs','sm','md','lg'].map(function(size){
39901 if (settings[size]) {
39902 cfg.cls += ' col-' + size + '-' + settings[size];
39906 this.store = new Roo.data.Store({
39907 proxy : new Roo.data.MemoryProxy({}),
39908 reader : new Roo.data.JsonReader({
39919 'name' : 'dialCode',
39923 'name' : 'priority',
39927 'name' : 'areaCodes',
39934 if(!this.preferedCountries) {
39935 this.preferedCountries = [
39942 var p = this.preferedCountries.reverse();
39945 for (var i = 0; i < p.length; i++) {
39946 for (var j = 0; j < this.allCountries.length; j++) {
39947 if(this.allCountries[j].iso2 == p[i]) {
39948 var t = this.allCountries[j];
39949 this.allCountries.splice(j,1);
39950 this.allCountries.unshift(t);
39956 this.store.proxy.data = {
39958 data: this.allCountries
39964 initEvents : function()
39967 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39969 this.indicator = this.indicatorEl();
39970 this.flag = this.flagEl();
39971 this.dialCodeHolder = this.dialCodeHolderEl();
39973 this.trigger = this.el.select('div.flag-box',true).first();
39974 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39979 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39980 _this.list.setWidth(lw);
39983 this.list.on('mouseover', this.onViewOver, this);
39984 this.list.on('mousemove', this.onViewMove, this);
39985 this.inputEl().on("keyup", this.onKeyUp, this);
39987 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39989 this.view = new Roo.View(this.list, this.tpl, {
39990 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39993 this.view.on('click', this.onViewClick, this);
39994 this.setValue(this.defaultDialCode);
39997 onTriggerClick : function(e)
39999 Roo.log('trigger click');
40004 if(this.isExpanded()){
40006 this.hasFocus = false;
40008 this.store.load({});
40009 this.hasFocus = true;
40014 isExpanded : function()
40016 return this.list.isVisible();
40019 collapse : function()
40021 if(!this.isExpanded()){
40025 Roo.get(document).un('mousedown', this.collapseIf, this);
40026 Roo.get(document).un('mousewheel', this.collapseIf, this);
40027 this.fireEvent('collapse', this);
40031 expand : function()
40035 if(this.isExpanded() || !this.hasFocus){
40039 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40040 this.list.setWidth(lw);
40043 this.restrictHeight();
40045 Roo.get(document).on('mousedown', this.collapseIf, this);
40046 Roo.get(document).on('mousewheel', this.collapseIf, this);
40048 this.fireEvent('expand', this);
40051 restrictHeight : function()
40053 this.list.alignTo(this.inputEl(), this.listAlign);
40054 this.list.alignTo(this.inputEl(), this.listAlign);
40057 onViewOver : function(e, t)
40059 if(this.inKeyMode){
40062 var item = this.view.findItemFromChild(t);
40065 var index = this.view.indexOf(item);
40066 this.select(index, false);
40071 onViewClick : function(view, doFocus, el, e)
40073 var index = this.view.getSelectedIndexes()[0];
40075 var r = this.store.getAt(index);
40078 this.onSelect(r, index);
40080 if(doFocus !== false && !this.blockFocus){
40081 this.inputEl().focus();
40085 onViewMove : function(e, t)
40087 this.inKeyMode = false;
40090 select : function(index, scrollIntoView)
40092 this.selectedIndex = index;
40093 this.view.select(index);
40094 if(scrollIntoView !== false){
40095 var el = this.view.getNode(index);
40097 this.list.scrollChildIntoView(el, false);
40102 createList : function()
40104 this.list = Roo.get(document.body).createChild({
40106 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40107 style: 'display:none'
40110 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40113 collapseIf : function(e)
40115 var in_combo = e.within(this.el);
40116 var in_list = e.within(this.list);
40117 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40119 if (in_combo || in_list || is_list) {
40125 onSelect : function(record, index)
40127 if(this.fireEvent('beforeselect', this, record, index) !== false){
40129 this.setFlagClass(record.data.iso2);
40130 this.setDialCode(record.data.dialCode);
40131 this.hasFocus = false;
40133 this.fireEvent('select', this, record, index);
40137 flagEl : function()
40139 var flag = this.el.select('div.flag',true).first();
40146 dialCodeHolderEl : function()
40148 var d = this.el.select('input.dial-code-holder',true).first();
40155 setDialCode : function(v)
40157 this.dialCodeHolder.dom.value = '+'+v;
40160 setFlagClass : function(n)
40162 this.flag.dom.className = 'flag '+n;
40165 getValue : function()
40167 var v = this.inputEl().getValue();
40168 if(this.dialCodeHolder) {
40169 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40174 setValue : function(v)
40176 var d = this.getDialCode(v);
40178 //invalid dial code
40179 if(v.length == 0 || !d || d.length == 0) {
40181 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40182 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40188 this.setFlagClass(this.dialCodeMapping[d].iso2);
40189 this.setDialCode(d);
40190 this.inputEl().dom.value = v.replace('+'+d,'');
40191 this.hiddenEl().dom.value = this.getValue();
40196 getDialCode : function(v)
40200 if (v.length == 0) {
40201 return this.dialCodeHolder.dom.value;
40205 if (v.charAt(0) != "+") {
40208 var numericChars = "";
40209 for (var i = 1; i < v.length; i++) {
40210 var c = v.charAt(i);
40213 if (this.dialCodeMapping[numericChars]) {
40214 dialCode = v.substr(1, i);
40216 if (numericChars.length == 4) {
40226 this.setValue(this.defaultDialCode);
40230 hiddenEl : function()
40232 return this.el.select('input.hidden-tel-input',true).first();
40235 onKeyUp : function(e){
40237 var k = e.getKey();
40238 var c = e.getCharCode();
40241 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40242 this.allowed.indexOf(String.fromCharCode(c)) === -1
40247 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40250 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40254 this.setValue(this.getValue());
40259 * @class Roo.bootstrap.MoneyField
40260 * @extends Roo.bootstrap.ComboBox
40261 * Bootstrap MoneyField class
40264 * Create a new MoneyField.
40265 * @param {Object} config Configuration options
40268 Roo.bootstrap.MoneyField = function(config) {
40270 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40274 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40277 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40279 allowDecimals : true,
40281 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40283 decimalSeparator : ".",
40285 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40287 decimalPrecision : 0,
40289 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40291 allowNegative : true,
40293 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40297 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40299 minValue : Number.NEGATIVE_INFINITY,
40301 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40303 maxValue : Number.MAX_VALUE,
40305 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40307 minText : "The minimum value for this field is {0}",
40309 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40311 maxText : "The maximum value for this field is {0}",
40313 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40314 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40316 nanText : "{0} is not a valid number",
40318 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40322 * @cfg {String} defaults currency of the MoneyField
40323 * value should be in lkey
40325 defaultCurrency : false,
40327 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40329 thousandsDelimiter : false,
40339 getAutoCreate : function()
40341 var align = this.labelAlign || this.parentLabelAlign();
40353 cls : 'form-control roo-money-amount-input',
40354 autocomplete: 'new-password'
40357 var hiddenInput = {
40361 cls: 'hidden-number-input'
40365 hiddenInput.name = this.name;
40368 if (this.disabled) {
40369 input.disabled = true;
40372 var clg = 12 - this.inputlg;
40373 var cmd = 12 - this.inputmd;
40374 var csm = 12 - this.inputsm;
40375 var cxs = 12 - this.inputxs;
40379 cls : 'row roo-money-field',
40383 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40387 cls: 'roo-select2-container input-group',
40391 cls : 'form-control roo-money-currency-input',
40392 autocomplete: 'new-password',
40394 name : this.currencyName
40398 cls : 'input-group-addon',
40412 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40416 cls: this.hasFeedback ? 'has-feedback' : '',
40427 if (this.fieldLabel.length) {
40430 tooltip: 'This field is required'
40436 cls: 'control-label',
40442 html: this.fieldLabel
40445 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40451 if(this.indicatorpos == 'right') {
40452 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40459 if(align == 'left') {
40467 if(this.labelWidth > 12){
40468 label.style = "width: " + this.labelWidth + 'px';
40470 if(this.labelWidth < 13 && this.labelmd == 0){
40471 this.labelmd = this.labelWidth;
40473 if(this.labellg > 0){
40474 label.cls += ' col-lg-' + this.labellg;
40475 input.cls += ' col-lg-' + (12 - this.labellg);
40477 if(this.labelmd > 0){
40478 label.cls += ' col-md-' + this.labelmd;
40479 container.cls += ' col-md-' + (12 - this.labelmd);
40481 if(this.labelsm > 0){
40482 label.cls += ' col-sm-' + this.labelsm;
40483 container.cls += ' col-sm-' + (12 - this.labelsm);
40485 if(this.labelxs > 0){
40486 label.cls += ' col-xs-' + this.labelxs;
40487 container.cls += ' col-xs-' + (12 - this.labelxs);
40498 var settings = this;
40500 ['xs','sm','md','lg'].map(function(size){
40501 if (settings[size]) {
40502 cfg.cls += ' col-' + size + '-' + settings[size];
40509 initEvents : function()
40511 this.indicator = this.indicatorEl();
40513 this.initCurrencyEvent();
40515 this.initNumberEvent();
40518 initCurrencyEvent : function()
40521 throw "can not find store for combo";
40524 this.store = Roo.factory(this.store, Roo.data);
40525 this.store.parent = this;
40529 this.triggerEl = this.el.select('.input-group-addon', true).first();
40531 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40536 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40537 _this.list.setWidth(lw);
40540 this.list.on('mouseover', this.onViewOver, this);
40541 this.list.on('mousemove', this.onViewMove, this);
40542 this.list.on('scroll', this.onViewScroll, this);
40545 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40548 this.view = new Roo.View(this.list, this.tpl, {
40549 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40552 this.view.on('click', this.onViewClick, this);
40554 this.store.on('beforeload', this.onBeforeLoad, this);
40555 this.store.on('load', this.onLoad, this);
40556 this.store.on('loadexception', this.onLoadException, this);
40558 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40559 "up" : function(e){
40560 this.inKeyMode = true;
40564 "down" : function(e){
40565 if(!this.isExpanded()){
40566 this.onTriggerClick();
40568 this.inKeyMode = true;
40573 "enter" : function(e){
40576 if(this.fireEvent("specialkey", this, e)){
40577 this.onViewClick(false);
40583 "esc" : function(e){
40587 "tab" : function(e){
40590 if(this.fireEvent("specialkey", this, e)){
40591 this.onViewClick(false);
40599 doRelay : function(foo, bar, hname){
40600 if(hname == 'down' || this.scope.isExpanded()){
40601 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40609 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40613 initNumberEvent : function(e)
40615 this.inputEl().on("keydown" , this.fireKey, this);
40616 this.inputEl().on("focus", this.onFocus, this);
40617 this.inputEl().on("blur", this.onBlur, this);
40619 this.inputEl().relayEvent('keyup', this);
40621 if(this.indicator){
40622 this.indicator.addClass('invisible');
40625 this.originalValue = this.getValue();
40627 if(this.validationEvent == 'keyup'){
40628 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40629 this.inputEl().on('keyup', this.filterValidation, this);
40631 else if(this.validationEvent !== false){
40632 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40635 if(this.selectOnFocus){
40636 this.on("focus", this.preFocus, this);
40639 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40640 this.inputEl().on("keypress", this.filterKeys, this);
40642 this.inputEl().relayEvent('keypress', this);
40645 var allowed = "0123456789";
40647 if(this.allowDecimals){
40648 allowed += this.decimalSeparator;
40651 if(this.allowNegative){
40655 if(this.thousandsDelimiter) {
40659 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40661 var keyPress = function(e){
40663 var k = e.getKey();
40665 var c = e.getCharCode();
40668 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40669 allowed.indexOf(String.fromCharCode(c)) === -1
40675 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40679 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40684 this.inputEl().on("keypress", keyPress, this);
40688 onTriggerClick : function(e)
40695 this.loadNext = false;
40697 if(this.isExpanded()){
40702 this.hasFocus = true;
40704 if(this.triggerAction == 'all') {
40705 this.doQuery(this.allQuery, true);
40709 this.doQuery(this.getRawValue());
40712 getCurrency : function()
40714 var v = this.currencyEl().getValue();
40719 restrictHeight : function()
40721 this.list.alignTo(this.currencyEl(), this.listAlign);
40722 this.list.alignTo(this.currencyEl(), this.listAlign);
40725 onViewClick : function(view, doFocus, el, e)
40727 var index = this.view.getSelectedIndexes()[0];
40729 var r = this.store.getAt(index);
40732 this.onSelect(r, index);
40736 onSelect : function(record, index){
40738 if(this.fireEvent('beforeselect', this, record, index) !== false){
40740 this.setFromCurrencyData(index > -1 ? record.data : false);
40744 this.fireEvent('select', this, record, index);
40748 setFromCurrencyData : function(o)
40752 this.lastCurrency = o;
40754 if (this.currencyField) {
40755 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40757 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40760 this.lastSelectionText = currency;
40762 //setting default currency
40763 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40764 this.setCurrency(this.defaultCurrency);
40768 this.setCurrency(currency);
40771 setFromData : function(o)
40775 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40777 this.setFromCurrencyData(c);
40782 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40784 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40787 this.setValue(value);
40791 setCurrency : function(v)
40793 this.currencyValue = v;
40796 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40801 setValue : function(v)
40803 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40809 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40811 this.inputEl().dom.value = (v == '') ? '' :
40812 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40814 if(!this.allowZero && v === '0') {
40815 this.hiddenEl().dom.value = '';
40816 this.inputEl().dom.value = '';
40823 getRawValue : function()
40825 var v = this.inputEl().getValue();
40830 getValue : function()
40832 return this.fixPrecision(this.parseValue(this.getRawValue()));
40835 parseValue : function(value)
40837 if(this.thousandsDelimiter) {
40839 r = new RegExp(",", "g");
40840 value = value.replace(r, "");
40843 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40844 return isNaN(value) ? '' : value;
40848 fixPrecision : function(value)
40850 if(this.thousandsDelimiter) {
40852 r = new RegExp(",", "g");
40853 value = value.replace(r, "");
40856 var nan = isNaN(value);
40858 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40859 return nan ? '' : value;
40861 return parseFloat(value).toFixed(this.decimalPrecision);
40864 decimalPrecisionFcn : function(v)
40866 return Math.floor(v);
40869 validateValue : function(value)
40871 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40875 var num = this.parseValue(value);
40878 this.markInvalid(String.format(this.nanText, value));
40882 if(num < this.minValue){
40883 this.markInvalid(String.format(this.minText, this.minValue));
40887 if(num > this.maxValue){
40888 this.markInvalid(String.format(this.maxText, this.maxValue));
40895 validate : function()
40897 if(this.disabled || this.allowBlank){
40902 var currency = this.getCurrency();
40904 if(this.validateValue(this.getRawValue()) && currency.length){
40909 this.markInvalid();
40913 getName: function()
40918 beforeBlur : function()
40924 var v = this.parseValue(this.getRawValue());
40931 onBlur : function()
40935 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40936 //this.el.removeClass(this.focusClass);
40939 this.hasFocus = false;
40941 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40945 var v = this.getValue();
40947 if(String(v) !== String(this.startValue)){
40948 this.fireEvent('change', this, v, this.startValue);
40951 this.fireEvent("blur", this);
40954 inputEl : function()
40956 return this.el.select('.roo-money-amount-input', true).first();
40959 currencyEl : function()
40961 return this.el.select('.roo-money-currency-input', true).first();
40964 hiddenEl : function()
40966 return this.el.select('input.hidden-number-input',true).first();