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);
2838 this.setSize(w, this.height);
2842 var body_childs = this.bodyEl.dom.childNodes;
2843 var full_height = this.headerEl.getHeight() + this.footerEl.getHeight();
2844 for(var i = 0; i < body_childs.length; i++) {
2845 full_height += body_childs[i].offsetHeight;
2848 this.setSize(w, Math.min(full_height, Roo.lib.Dom.getViewportHeight(true) - 60));
2853 setSize : function(w,h)
2863 if (!this.rendered) {
2867 //this.el.setStyle('display', 'block');
2868 this.el.removeClass('hideing');
2869 this.el.addClass('show');
2871 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
2874 this.el.addClass('in');
2877 this.el.addClass('in');
2880 // not sure how we can show data in here..
2882 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2885 Roo.get(document.body).addClass("x-body-masked");
2887 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2888 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2889 this.maskEl.addClass('show');
2893 this.fireEvent('show', this);
2895 // set zindex here - otherwise it appears to be ignored...
2896 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2899 this.items.forEach( function(e) {
2900 e.layout ? e.layout() : false;
2908 if(this.fireEvent("beforehide", this) !== false){
2909 this.maskEl.removeClass('show');
2910 Roo.get(document.body).removeClass("x-body-masked");
2911 this.el.removeClass('in');
2912 this.el.select('.modal-dialog', true).first().setStyle('transform','');
2914 if(this.animate){ // why
2915 this.el.addClass('hideing');
2917 if (!this.el.hasClass('hideing')) {
2918 return; // it's been shown again...
2920 this.el.removeClass('show');
2921 this.el.removeClass('hideing');
2925 this.el.removeClass('show');
2927 this.fireEvent('hide', this);
2930 isVisible : function()
2933 return this.el.hasClass('show') && !this.el.hasClass('hideing');
2937 addButton : function(str, cb)
2941 var b = Roo.apply({}, { html : str } );
2942 b.xns = b.xns || Roo.bootstrap;
2943 b.xtype = b.xtype || 'Button';
2944 if (typeof(b.listeners) == 'undefined') {
2945 b.listeners = { click : cb.createDelegate(this) };
2948 var btn = Roo.factory(b);
2950 btn.render(this.el.select('.modal-footer div').first());
2956 setDefaultButton : function(btn)
2958 //this.el.select('.modal-footer').()
2962 resizeTo: function(w,h)
2966 this.dialogEl.setWidth(w);
2967 if (this.diff === false) {
2968 this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2971 this.bodyEl.setHeight(h-this.diff);
2973 this.fireEvent('resize', this);
2976 setContentSize : function(w, h)
2980 onButtonClick: function(btn,e)
2983 this.fireEvent('btnclick', btn.name, e);
2986 * Set the title of the Dialog
2987 * @param {String} str new Title
2989 setTitle: function(str) {
2990 this.titleEl.dom.innerHTML = str;
2993 * Set the body of the Dialog
2994 * @param {String} str new Title
2996 setBody: function(str) {
2997 this.bodyEl.dom.innerHTML = str;
3000 * Set the body of the Dialog using the template
3001 * @param {Obj} data - apply this data to the template and replace the body contents.
3003 applyBody: function(obj)
3006 Roo.log("Error - using apply Body without a template");
3009 this.tmpl.overwrite(this.bodyEl, obj);
3015 Roo.apply(Roo.bootstrap.Modal, {
3017 * Button config that displays a single OK button
3026 * Button config that displays Yes and No buttons
3042 * Button config that displays OK and Cancel buttons
3057 * Button config that displays Yes, No and Cancel buttons
3081 * messagebox - can be used as a replace
3085 * @class Roo.MessageBox
3086 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
3090 Roo.Msg.alert('Status', 'Changes saved successfully.');
3092 // Prompt for user data:
3093 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3095 // process text value...
3099 // Show a dialog using config options:
3101 title:'Save Changes?',
3102 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3103 buttons: Roo.Msg.YESNOCANCEL,
3110 Roo.bootstrap.MessageBox = function(){
3111 var dlg, opt, mask, waitTimer;
3112 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3113 var buttons, activeTextEl, bwidth;
3117 var handleButton = function(button){
3119 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3123 var handleHide = function(){
3125 dlg.el.removeClass(opt.cls);
3128 // Roo.TaskMgr.stop(waitTimer);
3129 // waitTimer = null;
3134 var updateButtons = function(b){
3137 buttons["ok"].hide();
3138 buttons["cancel"].hide();
3139 buttons["yes"].hide();
3140 buttons["no"].hide();
3141 //dlg.footer.dom.style.display = 'none';
3144 dlg.footerEl.dom.style.display = '';
3145 for(var k in buttons){
3146 if(typeof buttons[k] != "function"){
3149 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3150 width += buttons[k].el.getWidth()+15;
3160 var handleEsc = function(d, k, e){
3161 if(opt && opt.closable !== false){
3171 * Returns a reference to the underlying {@link Roo.BasicDialog} element
3172 * @return {Roo.BasicDialog} The BasicDialog element
3174 getDialog : function(){
3176 dlg = new Roo.bootstrap.Modal( {
3179 //constraintoviewport:false,
3181 //collapsible : false,
3186 //buttonAlign:"center",
3187 closeClick : function(){
3188 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3191 handleButton("cancel");
3196 dlg.on("hide", handleHide);
3198 //dlg.addKeyListener(27, handleEsc);
3200 this.buttons = buttons;
3201 var bt = this.buttonText;
3202 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3203 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3204 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3205 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3207 bodyEl = dlg.bodyEl.createChild({
3209 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3210 '<textarea class="roo-mb-textarea"></textarea>' +
3211 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
3213 msgEl = bodyEl.dom.firstChild;
3214 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3215 textboxEl.enableDisplayMode();
3216 textboxEl.addKeyListener([10,13], function(){
3217 if(dlg.isVisible() && opt && opt.buttons){
3220 }else if(opt.buttons.yes){
3221 handleButton("yes");
3225 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3226 textareaEl.enableDisplayMode();
3227 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3228 progressEl.enableDisplayMode();
3230 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3231 var pf = progressEl.dom.firstChild;
3233 pp = Roo.get(pf.firstChild);
3234 pp.setHeight(pf.offsetHeight);
3242 * Updates the message box body text
3243 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3244 * the XHTML-compliant non-breaking space character '&#160;')
3245 * @return {Roo.MessageBox} This message box
3247 updateText : function(text)
3249 if(!dlg.isVisible() && !opt.width){
3250 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3251 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3253 msgEl.innerHTML = text || ' ';
3255 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3256 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3258 Math.min(opt.width || cw , this.maxWidth),
3259 Math.max(opt.minWidth || this.minWidth, bwidth)
3262 activeTextEl.setWidth(w);
3264 if(dlg.isVisible()){
3265 dlg.fixedcenter = false;
3267 // to big, make it scroll. = But as usual stupid IE does not support
3270 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3271 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3272 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3274 bodyEl.dom.style.height = '';
3275 bodyEl.dom.style.overflowY = '';
3278 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3280 bodyEl.dom.style.overflowX = '';
3283 dlg.setContentSize(w, bodyEl.getHeight());
3284 if(dlg.isVisible()){
3285 dlg.fixedcenter = true;
3291 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
3292 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3293 * @param {Number} value Any number between 0 and 1 (e.g., .5)
3294 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3295 * @return {Roo.MessageBox} This message box
3297 updateProgress : function(value, text){
3299 this.updateText(text);
3302 if (pp) { // weird bug on my firefox - for some reason this is not defined
3303 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3304 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3310 * Returns true if the message box is currently displayed
3311 * @return {Boolean} True if the message box is visible, else false
3313 isVisible : function(){
3314 return dlg && dlg.isVisible();
3318 * Hides the message box if it is displayed
3321 if(this.isVisible()){
3327 * Displays a new message box, or reinitializes an existing message box, based on the config options
3328 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3329 * The following config object properties are supported:
3331 Property Type Description
3332 ---------- --------------- ------------------------------------------------------------------------------------
3333 animEl String/Element An id or Element from which the message box should animate as it opens and
3334 closes (defaults to undefined)
3335 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3336 cancel:'Bar'}), or false to not show any buttons (defaults to false)
3337 closable Boolean False to hide the top-right close button (defaults to true). Note that
3338 progress and wait dialogs will ignore this property and always hide the
3339 close button as they can only be closed programmatically.
3340 cls String A custom CSS class to apply to the message box element
3341 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
3342 displayed (defaults to 75)
3343 fn Function A callback function to execute after closing the dialog. The arguments to the
3344 function will be btn (the name of the button that was clicked, if applicable,
3345 e.g. "ok"), and text (the value of the active text field, if applicable).
3346 Progress and wait dialogs will ignore this option since they do not respond to
3347 user actions and can only be closed programmatically, so any required function
3348 should be called by the same code after it closes the dialog.
3349 icon String A CSS class that provides a background image to be used as an icon for
3350 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3351 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
3352 minWidth Number The minimum width in pixels of the message box (defaults to 100)
3353 modal Boolean False to allow user interaction with the page while the message box is
3354 displayed (defaults to true)
3355 msg String A string that will replace the existing message box body text (defaults
3356 to the XHTML-compliant non-breaking space character ' ')
3357 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
3358 progress Boolean True to display a progress bar (defaults to false)
3359 progressText String The text to display inside the progress bar if progress = true (defaults to '')
3360 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
3361 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
3362 title String The title text
3363 value String The string value to set into the active textbox element if displayed
3364 wait Boolean True to display a progress bar (defaults to false)
3365 width Number The width of the dialog in pixels
3372 msg: 'Please enter your address:',
3374 buttons: Roo.MessageBox.OKCANCEL,
3377 animEl: 'addAddressBtn'
3380 * @param {Object} config Configuration options
3381 * @return {Roo.MessageBox} This message box
3383 show : function(options)
3386 // this causes nightmares if you show one dialog after another
3387 // especially on callbacks..
3389 if(this.isVisible()){
3392 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3393 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
3394 Roo.log("New Dialog Message:" + options.msg )
3395 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3396 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3399 var d = this.getDialog();
3401 d.setTitle(opt.title || " ");
3402 d.closeEl.setDisplayed(opt.closable !== false);
3403 activeTextEl = textboxEl;
3404 opt.prompt = opt.prompt || (opt.multiline ? true : false);
3409 textareaEl.setHeight(typeof opt.multiline == "number" ?
3410 opt.multiline : this.defaultTextHeight);
3411 activeTextEl = textareaEl;
3420 progressEl.setDisplayed(opt.progress === true);
3421 this.updateProgress(0);
3422 activeTextEl.dom.value = opt.value || "";
3424 dlg.setDefaultButton(activeTextEl);
3426 var bs = opt.buttons;
3430 }else if(bs && bs.yes){
3431 db = buttons["yes"];
3433 dlg.setDefaultButton(db);
3435 bwidth = updateButtons(opt.buttons);
3436 this.updateText(opt.msg);
3438 d.el.addClass(opt.cls);
3440 d.proxyDrag = opt.proxyDrag === true;
3441 d.modal = opt.modal !== false;
3442 d.mask = opt.modal !== false ? mask : false;
3444 // force it to the end of the z-index stack so it gets a cursor in FF
3445 document.body.appendChild(dlg.el.dom);
3446 d.animateTarget = null;
3447 d.show(options.animEl);
3453 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
3454 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3455 * and closing the message box when the process is complete.
3456 * @param {String} title The title bar text
3457 * @param {String} msg The message box body text
3458 * @return {Roo.MessageBox} This message box
3460 progress : function(title, msg){
3467 minWidth: this.minProgressWidth,
3474 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3475 * If a callback function is passed it will be called after the user clicks the button, and the
3476 * id of the button that was clicked will be passed as the only parameter to the callback
3477 * (could also be the top-right close button).
3478 * @param {String} title The title bar text
3479 * @param {String} msg The message box body text
3480 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3481 * @param {Object} scope (optional) The scope of the callback function
3482 * @return {Roo.MessageBox} This message box
3484 alert : function(title, msg, fn, scope)
3499 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
3500 * interaction while waiting for a long-running process to complete that does not have defined intervals.
3501 * You are responsible for closing the message box when the process is complete.
3502 * @param {String} msg The message box body text
3503 * @param {String} title (optional) The title bar text
3504 * @return {Roo.MessageBox} This message box
3506 wait : function(msg, title){
3517 waitTimer = Roo.TaskMgr.start({
3519 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3527 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3528 * If a callback function is passed it will be called after the user clicks either button, and the id of the
3529 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3530 * @param {String} title The title bar text
3531 * @param {String} msg The message box body text
3532 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3533 * @param {Object} scope (optional) The scope of the callback function
3534 * @return {Roo.MessageBox} This message box
3536 confirm : function(title, msg, fn, scope){
3540 buttons: this.YESNO,
3549 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3550 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
3551 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3552 * (could also be the top-right close button) and the text that was entered will be passed as the two
3553 * parameters to the callback.
3554 * @param {String} title The title bar text
3555 * @param {String} msg The message box body text
3556 * @param {Function} fn (optional) The callback function invoked after the message box is closed
3557 * @param {Object} scope (optional) The scope of the callback function
3558 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3559 * property, or the height in pixels to create the textbox (defaults to false / single-line)
3560 * @return {Roo.MessageBox} This message box
3562 prompt : function(title, msg, fn, scope, multiline){
3566 buttons: this.OKCANCEL,
3571 multiline: multiline,
3578 * Button config that displays a single OK button
3583 * Button config that displays Yes and No buttons
3586 YESNO : {yes:true, no:true},
3588 * Button config that displays OK and Cancel buttons
3591 OKCANCEL : {ok:true, cancel:true},
3593 * Button config that displays Yes, No and Cancel buttons
3596 YESNOCANCEL : {yes:true, no:true, cancel:true},
3599 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3602 defaultTextHeight : 75,
3604 * The maximum width in pixels of the message box (defaults to 600)
3609 * The minimum width in pixels of the message box (defaults to 100)
3614 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
3615 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3618 minProgressWidth : 250,
3620 * An object containing the default button text strings that can be overriden for localized language support.
3621 * Supported properties are: ok, cancel, yes and no.
3622 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3635 * Shorthand for {@link Roo.MessageBox}
3637 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3638 Roo.Msg = Roo.Msg || Roo.MessageBox;
3647 * @class Roo.bootstrap.Navbar
3648 * @extends Roo.bootstrap.Component
3649 * Bootstrap Navbar class
3652 * Create a new Navbar
3653 * @param {Object} config The config object
3657 Roo.bootstrap.Navbar = function(config){
3658 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3662 * @event beforetoggle
3663 * Fire before toggle the menu
3664 * @param {Roo.EventObject} e
3666 "beforetoggle" : true
3670 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
3679 getAutoCreate : function(){
3682 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3686 initEvents :function ()
3688 //Roo.log(this.el.select('.navbar-toggle',true));
3689 this.el.select('.navbar-toggle',true).on('click', function() {
3690 if(this.fireEvent('beforetoggle', this) !== false){
3691 this.el.select('.navbar-collapse',true).toggleClass('in');
3701 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3703 var size = this.el.getSize();
3704 this.maskEl.setSize(size.width, size.height);
3705 this.maskEl.enableDisplayMode("block");
3714 getChildContainer : function()
3716 if (this.el.select('.collapse').getCount()) {
3717 return this.el.select('.collapse',true).first();
3750 * @class Roo.bootstrap.NavSimplebar
3751 * @extends Roo.bootstrap.Navbar
3752 * Bootstrap Sidebar class
3754 * @cfg {Boolean} inverse is inverted color
3756 * @cfg {String} type (nav | pills | tabs)
3757 * @cfg {Boolean} arrangement stacked | justified
3758 * @cfg {String} align (left | right) alignment
3760 * @cfg {Boolean} main (true|false) main nav bar? default false
3761 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3763 * @cfg {String} tag (header|footer|nav|div) default is nav
3769 * Create a new Sidebar
3770 * @param {Object} config The config object
3774 Roo.bootstrap.NavSimplebar = function(config){
3775 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3778 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3794 getAutoCreate : function(){
3798 tag : this.tag || 'div',
3811 this.type = this.type || 'nav';
3812 if (['tabs','pills'].indexOf(this.type)!==-1) {
3813 cfg.cn[0].cls += ' nav-' + this.type
3817 if (this.type!=='nav') {
3818 Roo.log('nav type must be nav/tabs/pills')
3820 cfg.cn[0].cls += ' navbar-nav'
3826 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3827 cfg.cn[0].cls += ' nav-' + this.arrangement;
3831 if (this.align === 'right') {
3832 cfg.cn[0].cls += ' navbar-right';
3836 cfg.cls += ' navbar-inverse';
3863 * @class Roo.bootstrap.NavHeaderbar
3864 * @extends Roo.bootstrap.NavSimplebar
3865 * Bootstrap Sidebar class
3867 * @cfg {String} brand what is brand
3868 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3869 * @cfg {String} brand_href href of the brand
3870 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
3871 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3872 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3873 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3876 * Create a new Sidebar
3877 * @param {Object} config The config object
3881 Roo.bootstrap.NavHeaderbar = function(config){
3882 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3886 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3893 desktopCenter : false,
3896 getAutoCreate : function(){
3899 tag: this.nav || 'nav',
3906 if (this.desktopCenter) {
3907 cn.push({cls : 'container', cn : []});
3914 cls: 'navbar-header',
3919 cls: 'navbar-toggle',
3920 'data-toggle': 'collapse',
3925 html: 'Toggle navigation'
3947 cls: 'collapse navbar-collapse',
3951 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3953 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3954 cfg.cls += ' navbar-' + this.position;
3956 // tag can override this..
3958 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3961 if (this.brand !== '') {
3964 href: this.brand_href ? this.brand_href : '#',
3965 cls: 'navbar-brand',
3973 cfg.cls += ' main-nav';
3981 getHeaderChildContainer : function()
3983 if (this.srButton && this.el.select('.navbar-header').getCount()) {
3984 return this.el.select('.navbar-header',true).first();
3987 return this.getChildContainer();
3991 initEvents : function()
3993 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3995 if (this.autohide) {
4000 Roo.get(document).on('scroll',function(e) {
4001 var ns = Roo.get(document).getScroll().top;
4002 var os = prevScroll;
4006 ft.removeClass('slideDown');
4007 ft.addClass('slideUp');
4010 ft.removeClass('slideUp');
4011 ft.addClass('slideDown');
4032 * @class Roo.bootstrap.NavSidebar
4033 * @extends Roo.bootstrap.Navbar
4034 * Bootstrap Sidebar class
4037 * Create a new Sidebar
4038 * @param {Object} config The config object
4042 Roo.bootstrap.NavSidebar = function(config){
4043 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4046 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
4048 sidebar : true, // used by Navbar Item and NavbarGroup at present...
4050 getAutoCreate : function(){
4055 cls: 'sidebar sidebar-nav'
4077 * @class Roo.bootstrap.NavGroup
4078 * @extends Roo.bootstrap.Component
4079 * Bootstrap NavGroup class
4080 * @cfg {String} align (left|right)
4081 * @cfg {Boolean} inverse
4082 * @cfg {String} type (nav|pills|tab) default nav
4083 * @cfg {String} navId - reference Id for navbar.
4087 * Create a new nav group
4088 * @param {Object} config The config object
4091 Roo.bootstrap.NavGroup = function(config){
4092 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4095 Roo.bootstrap.NavGroup.register(this);
4099 * Fires when the active item changes
4100 * @param {Roo.bootstrap.NavGroup} this
4101 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4102 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
4109 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
4120 getAutoCreate : function()
4122 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4129 if (['tabs','pills'].indexOf(this.type)!==-1) {
4130 cfg.cls += ' nav-' + this.type
4132 if (this.type!=='nav') {
4133 Roo.log('nav type must be nav/tabs/pills')
4135 cfg.cls += ' navbar-nav'
4138 if (this.parent() && this.parent().sidebar) {
4141 cls: 'dashboard-menu sidebar-menu'
4147 if (this.form === true) {
4153 if (this.align === 'right') {
4154 cfg.cls += ' navbar-right';
4156 cfg.cls += ' navbar-left';
4160 if (this.align === 'right') {
4161 cfg.cls += ' navbar-right';
4165 cfg.cls += ' navbar-inverse';
4173 * sets the active Navigation item
4174 * @param {Roo.bootstrap.NavItem} the new current navitem
4176 setActiveItem : function(item)
4179 Roo.each(this.navItems, function(v){
4184 v.setActive(false, true);
4191 item.setActive(true, true);
4192 this.fireEvent('changed', this, item, prev);
4197 * gets the active Navigation item
4198 * @return {Roo.bootstrap.NavItem} the current navitem
4200 getActive : function()
4204 Roo.each(this.navItems, function(v){
4215 indexOfNav : function()
4219 Roo.each(this.navItems, function(v,i){
4230 * adds a Navigation item
4231 * @param {Roo.bootstrap.NavItem} the navitem to add
4233 addItem : function(cfg)
4235 var cn = new Roo.bootstrap.NavItem(cfg);
4237 cn.parentId = this.id;
4238 cn.onRender(this.el, null);
4242 * register a Navigation item
4243 * @param {Roo.bootstrap.NavItem} the navitem to add
4245 register : function(item)
4247 this.navItems.push( item);
4248 item.navId = this.navId;
4253 * clear all the Navigation item
4256 clearAll : function()
4259 this.el.dom.innerHTML = '';
4262 getNavItem: function(tabId)
4265 Roo.each(this.navItems, function(e) {
4266 if (e.tabId == tabId) {
4276 setActiveNext : function()
4278 var i = this.indexOfNav(this.getActive());
4279 if (i > this.navItems.length) {
4282 this.setActiveItem(this.navItems[i+1]);
4284 setActivePrev : function()
4286 var i = this.indexOfNav(this.getActive());
4290 this.setActiveItem(this.navItems[i-1]);
4292 clearWasActive : function(except) {
4293 Roo.each(this.navItems, function(e) {
4294 if (e.tabId != except.tabId && e.was_active) {
4295 e.was_active = false;
4302 getWasActive : function ()
4305 Roo.each(this.navItems, function(e) {
4320 Roo.apply(Roo.bootstrap.NavGroup, {
4324 * register a Navigation Group
4325 * @param {Roo.bootstrap.NavGroup} the navgroup to add
4327 register : function(navgrp)
4329 this.groups[navgrp.navId] = navgrp;
4333 * fetch a Navigation Group based on the navigation ID
4334 * @param {string} the navgroup to add
4335 * @returns {Roo.bootstrap.NavGroup} the navgroup
4337 get: function(navId) {
4338 if (typeof(this.groups[navId]) == 'undefined') {
4340 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4342 return this.groups[navId] ;
4357 * @class Roo.bootstrap.NavItem
4358 * @extends Roo.bootstrap.Component
4359 * Bootstrap Navbar.NavItem class
4360 * @cfg {String} href link to
4361 * @cfg {String} html content of button
4362 * @cfg {String} badge text inside badge
4363 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4364 * @cfg {String} glyphicon name of glyphicon
4365 * @cfg {String} icon name of font awesome icon
4366 * @cfg {Boolean} active Is item active
4367 * @cfg {Boolean} disabled Is item disabled
4369 * @cfg {Boolean} preventDefault (true | false) default false
4370 * @cfg {String} tabId the tab that this item activates.
4371 * @cfg {String} tagtype (a|span) render as a href or span?
4372 * @cfg {Boolean} animateRef (true|false) link to element default false
4375 * Create a new Navbar Item
4376 * @param {Object} config The config object
4378 Roo.bootstrap.NavItem = function(config){
4379 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4384 * The raw click event for the entire grid.
4385 * @param {Roo.EventObject} e
4390 * Fires when the active item active state changes
4391 * @param {Roo.bootstrap.NavItem} this
4392 * @param {boolean} state the new state
4398 * Fires when scroll to element
4399 * @param {Roo.bootstrap.NavItem} this
4400 * @param {Object} options
4401 * @param {Roo.EventObject} e
4409 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
4417 preventDefault : false,
4424 getAutoCreate : function(){
4433 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4435 if (this.disabled) {
4436 cfg.cls += ' disabled';
4439 if (this.href || this.html || this.glyphicon || this.icon) {
4443 href : this.href || "#",
4444 html: this.html || ''
4449 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4452 if(this.glyphicon) {
4453 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
4458 cfg.cn[0].html += " <span class='caret'></span>";
4462 if (this.badge !== '') {
4464 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4472 initEvents: function()
4474 if (typeof (this.menu) != 'undefined') {
4475 this.menu.parentType = this.xtype;
4476 this.menu.triggerEl = this.el;
4477 this.menu = this.addxtype(Roo.apply({}, this.menu));
4480 this.el.select('a',true).on('click', this.onClick, this);
4482 if(this.tagtype == 'span'){
4483 this.el.select('span',true).on('click', this.onClick, this);
4486 // at this point parent should be available..
4487 this.parent().register(this);
4490 onClick : function(e)
4492 if (e.getTarget('.dropdown-menu-item')) {
4493 // did you click on a menu itemm.... - then don't trigger onclick..
4498 this.preventDefault ||
4501 Roo.log("NavItem - prevent Default?");
4505 if (this.disabled) {
4509 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4510 if (tg && tg.transition) {
4511 Roo.log("waiting for the transitionend");
4517 //Roo.log("fire event clicked");
4518 if(this.fireEvent('click', this, e) === false){
4522 if(this.tagtype == 'span'){
4526 //Roo.log(this.href);
4527 var ael = this.el.select('a',true).first();
4530 if(ael && this.animateRef && this.href.indexOf('#') > -1){
4531 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4532 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4533 return; // ignore... - it's a 'hash' to another page.
4535 Roo.log("NavItem - prevent Default?");
4537 this.scrollToElement(e);
4541 var p = this.parent();
4543 if (['tabs','pills'].indexOf(p.type)!==-1) {
4544 if (typeof(p.setActiveItem) !== 'undefined') {
4545 p.setActiveItem(this);
4549 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4550 if (p.parentType == 'NavHeaderbar' && !this.menu) {
4551 // remove the collapsed menu expand...
4552 p.parent().el.select('.navbar-collapse',true).removeClass('in');
4556 isActive: function () {
4559 setActive : function(state, fire, is_was_active)
4561 if (this.active && !state && this.navId) {
4562 this.was_active = true;
4563 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4565 nv.clearWasActive(this);
4569 this.active = state;
4572 this.el.removeClass('active');
4573 } else if (!this.el.hasClass('active')) {
4574 this.el.addClass('active');
4577 this.fireEvent('changed', this, state);
4580 // show a panel if it's registered and related..
4582 if (!this.navId || !this.tabId || !state || is_was_active) {
4586 var tg = Roo.bootstrap.TabGroup.get(this.navId);
4590 var pan = tg.getPanelByName(this.tabId);
4594 // if we can not flip to new panel - go back to old nav highlight..
4595 if (false == tg.showPanel(pan)) {
4596 var nv = Roo.bootstrap.NavGroup.get(this.navId);
4598 var onav = nv.getWasActive();
4600 onav.setActive(true, false, true);
4609 // this should not be here...
4610 setDisabled : function(state)
4612 this.disabled = state;
4614 this.el.removeClass('disabled');
4615 } else if (!this.el.hasClass('disabled')) {
4616 this.el.addClass('disabled');
4622 * Fetch the element to display the tooltip on.
4623 * @return {Roo.Element} defaults to this.el
4625 tooltipEl : function()
4627 return this.el.select('' + this.tagtype + '', true).first();
4630 scrollToElement : function(e)
4632 var c = document.body;
4635 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4637 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4638 c = document.documentElement;
4641 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4647 var o = target.calcOffsetsTo(c);
4654 this.fireEvent('scrollto', this, options, e);
4656 Roo.get(c).scrollTo('top', options.value, true);
4669 * <span> icon </span>
4670 * <span> text </span>
4671 * <span>badge </span>
4675 * @class Roo.bootstrap.NavSidebarItem
4676 * @extends Roo.bootstrap.NavItem
4677 * Bootstrap Navbar.NavSidebarItem class
4678 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4679 * {Boolean} open is the menu open
4680 * {Boolean} buttonView use button as the tigger el rather that a (default false)
4681 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4682 * {String} buttonSize (sm|md|lg)the extra classes for the button
4683 * {Boolean} showArrow show arrow next to the text (default true)
4685 * Create a new Navbar Button
4686 * @param {Object} config The config object
4688 Roo.bootstrap.NavSidebarItem = function(config){
4689 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4694 * The raw click event for the entire grid.
4695 * @param {Roo.EventObject} e
4700 * Fires when the active item active state changes
4701 * @param {Roo.bootstrap.NavSidebarItem} this
4702 * @param {boolean} state the new state
4710 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
4712 badgeWeight : 'default',
4718 buttonWeight : 'default',
4724 getAutoCreate : function(){
4729 href : this.href || '#',
4735 if(this.buttonView){
4738 href : this.href || '#',
4739 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4752 cfg.cls += ' active';
4755 if (this.disabled) {
4756 cfg.cls += ' disabled';
4759 cfg.cls += ' open x-open';
4762 if (this.glyphicon || this.icon) {
4763 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
4764 a.cn.push({ tag : 'i', cls : c }) ;
4767 if(!this.buttonView){
4770 html : this.html || ''
4777 if (this.badge !== '') {
4778 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
4784 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4787 a.cls += ' dropdown-toggle treeview' ;
4793 initEvents : function()
4795 if (typeof (this.menu) != 'undefined') {
4796 this.menu.parentType = this.xtype;
4797 this.menu.triggerEl = this.el;
4798 this.menu = this.addxtype(Roo.apply({}, this.menu));
4801 this.el.on('click', this.onClick, this);
4803 if(this.badge !== ''){
4804 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4809 onClick : function(e)
4816 if(this.preventDefault){
4820 this.fireEvent('click', this);
4823 disable : function()
4825 this.setDisabled(true);
4830 this.setDisabled(false);
4833 setDisabled : function(state)
4835 if(this.disabled == state){
4839 this.disabled = state;
4842 this.el.addClass('disabled');
4846 this.el.removeClass('disabled');
4851 setActive : function(state)
4853 if(this.active == state){
4857 this.active = state;
4860 this.el.addClass('active');
4864 this.el.removeClass('active');
4869 isActive: function ()
4874 setBadge : function(str)
4880 this.badgeEl.dom.innerHTML = str;
4897 * @class Roo.bootstrap.Row
4898 * @extends Roo.bootstrap.Component
4899 * Bootstrap Row class (contains columns...)
4903 * @param {Object} config The config object
4906 Roo.bootstrap.Row = function(config){
4907 Roo.bootstrap.Row.superclass.constructor.call(this, config);
4910 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
4912 getAutoCreate : function(){
4931 * @class Roo.bootstrap.Element
4932 * @extends Roo.bootstrap.Component
4933 * Bootstrap Element class
4934 * @cfg {String} html contents of the element
4935 * @cfg {String} tag tag of the element
4936 * @cfg {String} cls class of the element
4937 * @cfg {Boolean} preventDefault (true|false) default false
4938 * @cfg {Boolean} clickable (true|false) default false
4941 * Create a new Element
4942 * @param {Object} config The config object
4945 Roo.bootstrap.Element = function(config){
4946 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4952 * When a element is chick
4953 * @param {Roo.bootstrap.Element} this
4954 * @param {Roo.EventObject} e
4960 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4965 preventDefault: false,
4968 getAutoCreate : function(){
4972 // cls: this.cls, double assign in parent class Component.js :: onRender
4979 initEvents: function()
4981 Roo.bootstrap.Element.superclass.initEvents.call(this);
4984 this.el.on('click', this.onClick, this);
4989 onClick : function(e)
4991 if(this.preventDefault){
4995 this.fireEvent('click', this, e);
4998 getValue : function()
5000 return this.el.dom.innerHTML;
5003 setValue : function(value)
5005 this.el.dom.innerHTML = value;
5020 * @class Roo.bootstrap.Pagination
5021 * @extends Roo.bootstrap.Component
5022 * Bootstrap Pagination class
5023 * @cfg {String} size xs | sm | md | lg
5024 * @cfg {Boolean} inverse false | true
5027 * Create a new Pagination
5028 * @param {Object} config The config object
5031 Roo.bootstrap.Pagination = function(config){
5032 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5035 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
5041 getAutoCreate : function(){
5047 cfg.cls += ' inverse';
5053 cfg.cls += " " + this.cls;
5071 * @class Roo.bootstrap.PaginationItem
5072 * @extends Roo.bootstrap.Component
5073 * Bootstrap PaginationItem class
5074 * @cfg {String} html text
5075 * @cfg {String} href the link
5076 * @cfg {Boolean} preventDefault (true | false) default true
5077 * @cfg {Boolean} active (true | false) default false
5078 * @cfg {Boolean} disabled default false
5082 * Create a new PaginationItem
5083 * @param {Object} config The config object
5087 Roo.bootstrap.PaginationItem = function(config){
5088 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5093 * The raw click event for the entire grid.
5094 * @param {Roo.EventObject} e
5100 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
5104 preventDefault: true,
5109 getAutoCreate : function(){
5115 href : this.href ? this.href : '#',
5116 html : this.html ? this.html : ''
5126 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5130 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5136 initEvents: function() {
5138 this.el.on('click', this.onClick, this);
5141 onClick : function(e)
5143 Roo.log('PaginationItem on click ');
5144 if(this.preventDefault){
5152 this.fireEvent('click', this, e);
5168 * @class Roo.bootstrap.Slider
5169 * @extends Roo.bootstrap.Component
5170 * Bootstrap Slider class
5173 * Create a new Slider
5174 * @param {Object} config The config object
5177 Roo.bootstrap.Slider = function(config){
5178 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5181 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
5183 getAutoCreate : function(){
5187 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5191 cls: 'ui-slider-handle ui-state-default ui-corner-all'
5203 * Ext JS Library 1.1.1
5204 * Copyright(c) 2006-2007, Ext JS, LLC.
5206 * Originally Released Under LGPL - original licence link has changed is not relivant.
5209 * <script type="text/javascript">
5214 * @class Roo.grid.ColumnModel
5215 * @extends Roo.util.Observable
5216 * This is the default implementation of a ColumnModel used by the Grid. It defines
5217 * the columns in the grid.
5220 var colModel = new Roo.grid.ColumnModel([
5221 {header: "Ticker", width: 60, sortable: true, locked: true},
5222 {header: "Company Name", width: 150, sortable: true},
5223 {header: "Market Cap.", width: 100, sortable: true},
5224 {header: "$ Sales", width: 100, sortable: true, renderer: money},
5225 {header: "Employees", width: 100, sortable: true, resizable: false}
5230 * The config options listed for this class are options which may appear in each
5231 * individual column definition.
5232 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5234 * @param {Object} config An Array of column config objects. See this class's
5235 * config objects for details.
5237 Roo.grid.ColumnModel = function(config){
5239 * The config passed into the constructor
5241 this.config = config;
5244 // if no id, create one
5245 // if the column does not have a dataIndex mapping,
5246 // map it to the order it is in the config
5247 for(var i = 0, len = config.length; i < len; i++){
5249 if(typeof c.dataIndex == "undefined"){
5252 if(typeof c.renderer == "string"){
5253 c.renderer = Roo.util.Format[c.renderer];
5255 if(typeof c.id == "undefined"){
5258 if(c.editor && c.editor.xtype){
5259 c.editor = Roo.factory(c.editor, Roo.grid);
5261 if(c.editor && c.editor.isFormField){
5262 c.editor = new Roo.grid.GridEditor(c.editor);
5264 this.lookup[c.id] = c;
5268 * The width of columns which have no width specified (defaults to 100)
5271 this.defaultWidth = 100;
5274 * Default sortable of columns which have no sortable specified (defaults to false)
5277 this.defaultSortable = false;
5281 * @event widthchange
5282 * Fires when the width of a column changes.
5283 * @param {ColumnModel} this
5284 * @param {Number} columnIndex The column index
5285 * @param {Number} newWidth The new width
5287 "widthchange": true,
5289 * @event headerchange
5290 * Fires when the text of a header changes.
5291 * @param {ColumnModel} this
5292 * @param {Number} columnIndex The column index
5293 * @param {Number} newText The new header text
5295 "headerchange": true,
5297 * @event hiddenchange
5298 * Fires when a column is hidden or "unhidden".
5299 * @param {ColumnModel} this
5300 * @param {Number} columnIndex The column index
5301 * @param {Boolean} hidden true if hidden, false otherwise
5303 "hiddenchange": true,
5305 * @event columnmoved
5306 * Fires when a column is moved.
5307 * @param {ColumnModel} this
5308 * @param {Number} oldIndex
5309 * @param {Number} newIndex
5311 "columnmoved" : true,
5313 * @event columlockchange
5314 * Fires when a column's locked state is changed
5315 * @param {ColumnModel} this
5316 * @param {Number} colIndex
5317 * @param {Boolean} locked true if locked
5319 "columnlockchange" : true
5321 Roo.grid.ColumnModel.superclass.constructor.call(this);
5323 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5325 * @cfg {String} header The header text to display in the Grid view.
5328 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5329 * {@link Roo.data.Record} definition from which to draw the column's value. If not
5330 * specified, the column's index is used as an index into the Record's data Array.
5333 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5334 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5337 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5338 * Defaults to the value of the {@link #defaultSortable} property.
5339 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5342 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
5345 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
5348 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5351 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5354 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5355 * given the cell's data value. See {@link #setRenderer}. If not specified, the
5356 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5357 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5360 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
5363 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
5366 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
5369 * @cfg {String} cursor (Optional)
5372 * @cfg {String} tooltip (Optional)
5375 * @cfg {Number} xs (Optional)
5378 * @cfg {Number} sm (Optional)
5381 * @cfg {Number} md (Optional)
5384 * @cfg {Number} lg (Optional)
5387 * Returns the id of the column at the specified index.
5388 * @param {Number} index The column index
5389 * @return {String} the id
5391 getColumnId : function(index){
5392 return this.config[index].id;
5396 * Returns the column for a specified id.
5397 * @param {String} id The column id
5398 * @return {Object} the column
5400 getColumnById : function(id){
5401 return this.lookup[id];
5406 * Returns the column for a specified dataIndex.
5407 * @param {String} dataIndex The column dataIndex
5408 * @return {Object|Boolean} the column or false if not found
5410 getColumnByDataIndex: function(dataIndex){
5411 var index = this.findColumnIndex(dataIndex);
5412 return index > -1 ? this.config[index] : false;
5416 * Returns the index for a specified column id.
5417 * @param {String} id The column id
5418 * @return {Number} the index, or -1 if not found
5420 getIndexById : function(id){
5421 for(var i = 0, len = this.config.length; i < len; i++){
5422 if(this.config[i].id == id){
5430 * Returns the index for a specified column dataIndex.
5431 * @param {String} dataIndex The column dataIndex
5432 * @return {Number} the index, or -1 if not found
5435 findColumnIndex : function(dataIndex){
5436 for(var i = 0, len = this.config.length; i < len; i++){
5437 if(this.config[i].dataIndex == dataIndex){
5445 moveColumn : function(oldIndex, newIndex){
5446 var c = this.config[oldIndex];
5447 this.config.splice(oldIndex, 1);
5448 this.config.splice(newIndex, 0, c);
5449 this.dataMap = null;
5450 this.fireEvent("columnmoved", this, oldIndex, newIndex);
5453 isLocked : function(colIndex){
5454 return this.config[colIndex].locked === true;
5457 setLocked : function(colIndex, value, suppressEvent){
5458 if(this.isLocked(colIndex) == value){
5461 this.config[colIndex].locked = value;
5463 this.fireEvent("columnlockchange", this, colIndex, value);
5467 getTotalLockedWidth : function(){
5469 for(var i = 0; i < this.config.length; i++){
5470 if(this.isLocked(i) && !this.isHidden(i)){
5471 this.totalWidth += this.getColumnWidth(i);
5477 getLockedCount : function(){
5478 for(var i = 0, len = this.config.length; i < len; i++){
5479 if(!this.isLocked(i)){
5484 return this.config.length;
5488 * Returns the number of columns.
5491 getColumnCount : function(visibleOnly){
5492 if(visibleOnly === true){
5494 for(var i = 0, len = this.config.length; i < len; i++){
5495 if(!this.isHidden(i)){
5501 return this.config.length;
5505 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5506 * @param {Function} fn
5507 * @param {Object} scope (optional)
5508 * @return {Array} result
5510 getColumnsBy : function(fn, scope){
5512 for(var i = 0, len = this.config.length; i < len; i++){
5513 var c = this.config[i];
5514 if(fn.call(scope||this, c, i) === true){
5522 * Returns true if the specified column is sortable.
5523 * @param {Number} col The column index
5526 isSortable : function(col){
5527 if(typeof this.config[col].sortable == "undefined"){
5528 return this.defaultSortable;
5530 return this.config[col].sortable;
5534 * Returns the rendering (formatting) function defined for the column.
5535 * @param {Number} col The column index.
5536 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5538 getRenderer : function(col){
5539 if(!this.config[col].renderer){
5540 return Roo.grid.ColumnModel.defaultRenderer;
5542 return this.config[col].renderer;
5546 * Sets the rendering (formatting) function for a column.
5547 * @param {Number} col The column index
5548 * @param {Function} fn The function to use to process the cell's raw data
5549 * to return HTML markup for the grid view. The render function is called with
5550 * the following parameters:<ul>
5551 * <li>Data value.</li>
5552 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5553 * <li>css A CSS style string to apply to the table cell.</li>
5554 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5555 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5556 * <li>Row index</li>
5557 * <li>Column index</li>
5558 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5560 setRenderer : function(col, fn){
5561 this.config[col].renderer = fn;
5565 * Returns the width for the specified column.
5566 * @param {Number} col The column index
5569 getColumnWidth : function(col){
5570 return this.config[col].width * 1 || this.defaultWidth;
5574 * Sets the width for a column.
5575 * @param {Number} col The column index
5576 * @param {Number} width The new width
5578 setColumnWidth : function(col, width, suppressEvent){
5579 this.config[col].width = width;
5580 this.totalWidth = null;
5582 this.fireEvent("widthchange", this, col, width);
5587 * Returns the total width of all columns.
5588 * @param {Boolean} includeHidden True to include hidden column widths
5591 getTotalWidth : function(includeHidden){
5592 if(!this.totalWidth){
5593 this.totalWidth = 0;
5594 for(var i = 0, len = this.config.length; i < len; i++){
5595 if(includeHidden || !this.isHidden(i)){
5596 this.totalWidth += this.getColumnWidth(i);
5600 return this.totalWidth;
5604 * Returns the header for the specified column.
5605 * @param {Number} col The column index
5608 getColumnHeader : function(col){
5609 return this.config[col].header;
5613 * Sets the header for a column.
5614 * @param {Number} col The column index
5615 * @param {String} header The new header
5617 setColumnHeader : function(col, header){
5618 this.config[col].header = header;
5619 this.fireEvent("headerchange", this, col, header);
5623 * Returns the tooltip for the specified column.
5624 * @param {Number} col The column index
5627 getColumnTooltip : function(col){
5628 return this.config[col].tooltip;
5631 * Sets the tooltip for a column.
5632 * @param {Number} col The column index
5633 * @param {String} tooltip The new tooltip
5635 setColumnTooltip : function(col, tooltip){
5636 this.config[col].tooltip = tooltip;
5640 * Returns the dataIndex for the specified column.
5641 * @param {Number} col The column index
5644 getDataIndex : function(col){
5645 return this.config[col].dataIndex;
5649 * Sets the dataIndex for a column.
5650 * @param {Number} col The column index
5651 * @param {Number} dataIndex The new dataIndex
5653 setDataIndex : function(col, dataIndex){
5654 this.config[col].dataIndex = dataIndex;
5660 * Returns true if the cell is editable.
5661 * @param {Number} colIndex The column index
5662 * @param {Number} rowIndex The row index - this is nto actually used..?
5665 isCellEditable : function(colIndex, rowIndex){
5666 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5670 * Returns the editor defined for the cell/column.
5671 * return false or null to disable editing.
5672 * @param {Number} colIndex The column index
5673 * @param {Number} rowIndex The row index
5676 getCellEditor : function(colIndex, rowIndex){
5677 return this.config[colIndex].editor;
5681 * Sets if a column is editable.
5682 * @param {Number} col The column index
5683 * @param {Boolean} editable True if the column is editable
5685 setEditable : function(col, editable){
5686 this.config[col].editable = editable;
5691 * Returns true if the column is hidden.
5692 * @param {Number} colIndex The column index
5695 isHidden : function(colIndex){
5696 return this.config[colIndex].hidden;
5701 * Returns true if the column width cannot be changed
5703 isFixed : function(colIndex){
5704 return this.config[colIndex].fixed;
5708 * Returns true if the column can be resized
5711 isResizable : function(colIndex){
5712 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5715 * Sets if a column is hidden.
5716 * @param {Number} colIndex The column index
5717 * @param {Boolean} hidden True if the column is hidden
5719 setHidden : function(colIndex, hidden){
5720 this.config[colIndex].hidden = hidden;
5721 this.totalWidth = null;
5722 this.fireEvent("hiddenchange", this, colIndex, hidden);
5726 * Sets the editor for a column.
5727 * @param {Number} col The column index
5728 * @param {Object} editor The editor object
5730 setEditor : function(col, editor){
5731 this.config[col].editor = editor;
5735 Roo.grid.ColumnModel.defaultRenderer = function(value)
5737 if(typeof value == "object") {
5740 if(typeof value == "string" && value.length < 1){
5744 return String.format("{0}", value);
5747 // Alias for backwards compatibility
5748 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5751 * Ext JS Library 1.1.1
5752 * Copyright(c) 2006-2007, Ext JS, LLC.
5754 * Originally Released Under LGPL - original licence link has changed is not relivant.
5757 * <script type="text/javascript">
5761 * @class Roo.LoadMask
5762 * A simple utility class for generically masking elements while loading data. If the element being masked has
5763 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5764 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
5765 * element's UpdateManager load indicator and will be destroyed after the initial load.
5767 * Create a new LoadMask
5768 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5769 * @param {Object} config The config object
5771 Roo.LoadMask = function(el, config){
5772 this.el = Roo.get(el);
5773 Roo.apply(this, config);
5775 this.store.on('beforeload', this.onBeforeLoad, this);
5776 this.store.on('load', this.onLoad, this);
5777 this.store.on('loadexception', this.onLoadException, this);
5778 this.removeMask = false;
5780 var um = this.el.getUpdateManager();
5781 um.showLoadIndicator = false; // disable the default indicator
5782 um.on('beforeupdate', this.onBeforeLoad, this);
5783 um.on('update', this.onLoad, this);
5784 um.on('failure', this.onLoad, this);
5785 this.removeMask = true;
5789 Roo.LoadMask.prototype = {
5791 * @cfg {Boolean} removeMask
5792 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5793 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
5797 * The text to display in a centered loading message box (defaults to 'Loading...')
5801 * @cfg {String} msgCls
5802 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5804 msgCls : 'x-mask-loading',
5807 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5813 * Disables the mask to prevent it from being displayed
5815 disable : function(){
5816 this.disabled = true;
5820 * Enables the mask so that it can be displayed
5822 enable : function(){
5823 this.disabled = false;
5826 onLoadException : function()
5830 if (typeof(arguments[3]) != 'undefined') {
5831 Roo.MessageBox.alert("Error loading",arguments[3]);
5835 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5836 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5843 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5848 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5852 onBeforeLoad : function(){
5854 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5859 destroy : function(){
5861 this.store.un('beforeload', this.onBeforeLoad, this);
5862 this.store.un('load', this.onLoad, this);
5863 this.store.un('loadexception', this.onLoadException, this);
5865 var um = this.el.getUpdateManager();
5866 um.un('beforeupdate', this.onBeforeLoad, this);
5867 um.un('update', this.onLoad, this);
5868 um.un('failure', this.onLoad, this);
5879 * @class Roo.bootstrap.Table
5880 * @extends Roo.bootstrap.Component
5881 * Bootstrap Table class
5882 * @cfg {String} cls table class
5883 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5884 * @cfg {String} bgcolor Specifies the background color for a table
5885 * @cfg {Number} border Specifies whether the table cells should have borders or not
5886 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5887 * @cfg {Number} cellspacing Specifies the space between cells
5888 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5889 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5890 * @cfg {String} sortable Specifies that the table should be sortable
5891 * @cfg {String} summary Specifies a summary of the content of a table
5892 * @cfg {Number} width Specifies the width of a table
5893 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5895 * @cfg {boolean} striped Should the rows be alternative striped
5896 * @cfg {boolean} bordered Add borders to the table
5897 * @cfg {boolean} hover Add hover highlighting
5898 * @cfg {boolean} condensed Format condensed
5899 * @cfg {boolean} responsive Format condensed
5900 * @cfg {Boolean} loadMask (true|false) default false
5901 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5902 * @cfg {Boolean} headerShow (true|false) generate thead, default true
5903 * @cfg {Boolean} rowSelection (true|false) default false
5904 * @cfg {Boolean} cellSelection (true|false) default false
5905 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5906 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
5907 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
5908 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
5912 * Create a new Table
5913 * @param {Object} config The config object
5916 Roo.bootstrap.Table = function(config){
5917 Roo.bootstrap.Table.superclass.constructor.call(this, config);
5922 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5923 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5924 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5925 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5927 this.sm = this.sm || {xtype: 'RowSelectionModel'};
5929 this.sm.grid = this;
5930 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5931 this.sm = this.selModel;
5932 this.sm.xmodule = this.xmodule || false;
5935 if (this.cm && typeof(this.cm.config) == 'undefined') {
5936 this.colModel = new Roo.grid.ColumnModel(this.cm);
5937 this.cm = this.colModel;
5938 this.cm.xmodule = this.xmodule || false;
5941 this.store= Roo.factory(this.store, Roo.data);
5942 this.ds = this.store;
5943 this.ds.xmodule = this.xmodule || false;
5946 if (this.footer && this.store) {
5947 this.footer.dataSource = this.ds;
5948 this.footer = Roo.factory(this.footer);
5955 * Fires when a cell is clicked
5956 * @param {Roo.bootstrap.Table} this
5957 * @param {Roo.Element} el
5958 * @param {Number} rowIndex
5959 * @param {Number} columnIndex
5960 * @param {Roo.EventObject} e
5964 * @event celldblclick
5965 * Fires when a cell is double clicked
5966 * @param {Roo.bootstrap.Table} this
5967 * @param {Roo.Element} el
5968 * @param {Number} rowIndex
5969 * @param {Number} columnIndex
5970 * @param {Roo.EventObject} e
5972 "celldblclick" : true,
5975 * Fires when a row is clicked
5976 * @param {Roo.bootstrap.Table} this
5977 * @param {Roo.Element} el
5978 * @param {Number} rowIndex
5979 * @param {Roo.EventObject} e
5983 * @event rowdblclick
5984 * Fires when a row is double clicked
5985 * @param {Roo.bootstrap.Table} this
5986 * @param {Roo.Element} el
5987 * @param {Number} rowIndex
5988 * @param {Roo.EventObject} e
5990 "rowdblclick" : true,
5993 * Fires when a mouseover occur
5994 * @param {Roo.bootstrap.Table} this
5995 * @param {Roo.Element} el
5996 * @param {Number} rowIndex
5997 * @param {Number} columnIndex
5998 * @param {Roo.EventObject} e
6003 * Fires when a mouseout occur
6004 * @param {Roo.bootstrap.Table} this
6005 * @param {Roo.Element} el
6006 * @param {Number} rowIndex
6007 * @param {Number} columnIndex
6008 * @param {Roo.EventObject} e
6013 * Fires when a row is rendered, so you can change add a style to it.
6014 * @param {Roo.bootstrap.Table} this
6015 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
6019 * @event rowsrendered
6020 * Fires when all the rows have been rendered
6021 * @param {Roo.bootstrap.Table} this
6023 'rowsrendered' : true,
6025 * @event contextmenu
6026 * The raw contextmenu event for the entire grid.
6027 * @param {Roo.EventObject} e
6029 "contextmenu" : true,
6031 * @event rowcontextmenu
6032 * Fires when a row is right clicked
6033 * @param {Roo.bootstrap.Table} this
6034 * @param {Number} rowIndex
6035 * @param {Roo.EventObject} e
6037 "rowcontextmenu" : true,
6039 * @event cellcontextmenu
6040 * Fires when a cell is right clicked
6041 * @param {Roo.bootstrap.Table} this
6042 * @param {Number} rowIndex
6043 * @param {Number} cellIndex
6044 * @param {Roo.EventObject} e
6046 "cellcontextmenu" : true,
6048 * @event headercontextmenu
6049 * Fires when a header is right clicked
6050 * @param {Roo.bootstrap.Table} this
6051 * @param {Number} columnIndex
6052 * @param {Roo.EventObject} e
6054 "headercontextmenu" : true
6058 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
6084 rowSelection : false,
6085 cellSelection : false,
6088 // Roo.Element - the tbody
6090 // Roo.Element - thead element
6093 container: false, // used by gridpanel...
6099 auto_hide_footer : false,
6101 getAutoCreate : function()
6103 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6110 if (this.scrollBody) {
6111 cfg.cls += ' table-body-fixed';
6114 cfg.cls += ' table-striped';
6118 cfg.cls += ' table-hover';
6120 if (this.bordered) {
6121 cfg.cls += ' table-bordered';
6123 if (this.condensed) {
6124 cfg.cls += ' table-condensed';
6126 if (this.responsive) {
6127 cfg.cls += ' table-responsive';
6131 cfg.cls+= ' ' +this.cls;
6134 // this lot should be simplifed...
6147 ].forEach(function(k) {
6155 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6158 if(this.store || this.cm){
6159 if(this.headerShow){
6160 cfg.cn.push(this.renderHeader());
6163 cfg.cn.push(this.renderBody());
6165 if(this.footerShow){
6166 cfg.cn.push(this.renderFooter());
6168 // where does this come from?
6169 //cfg.cls+= ' TableGrid';
6172 return { cn : [ cfg ] };
6175 initEvents : function()
6177 if(!this.store || !this.cm){
6180 if (this.selModel) {
6181 this.selModel.initEvents();
6185 //Roo.log('initEvents with ds!!!!');
6187 this.mainBody = this.el.select('tbody', true).first();
6188 this.mainHead = this.el.select('thead', true).first();
6189 this.mainFoot = this.el.select('tfoot', true).first();
6195 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6196 e.on('click', _this.sort, _this);
6199 this.mainBody.on("click", this.onClick, this);
6200 this.mainBody.on("dblclick", this.onDblClick, this);
6202 // why is this done????? = it breaks dialogs??
6203 //this.parent().el.setStyle('position', 'relative');
6207 this.footer.parentId = this.id;
6208 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6211 this.el.select('tfoot tr td').first().addClass('hide');
6216 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6219 this.store.on('load', this.onLoad, this);
6220 this.store.on('beforeload', this.onBeforeLoad, this);
6221 this.store.on('update', this.onUpdate, this);
6222 this.store.on('add', this.onAdd, this);
6223 this.store.on("clear", this.clear, this);
6225 this.el.on("contextmenu", this.onContextMenu, this);
6227 this.mainBody.on('scroll', this.onBodyScroll, this);
6229 this.cm.on("headerchange", this.onHeaderChange, this);
6231 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6235 onContextMenu : function(e, t)
6237 this.processEvent("contextmenu", e);
6240 processEvent : function(name, e)
6242 if (name != 'touchstart' ) {
6243 this.fireEvent(name, e);
6246 var t = e.getTarget();
6248 var cell = Roo.get(t);
6254 if(cell.findParent('tfoot', false, true)){
6258 if(cell.findParent('thead', false, true)){
6260 if(e.getTarget().nodeName.toLowerCase() != 'th'){
6261 cell = Roo.get(t).findParent('th', false, true);
6263 Roo.log("failed to find th in thead?");
6264 Roo.log(e.getTarget());
6269 var cellIndex = cell.dom.cellIndex;
6271 var ename = name == 'touchstart' ? 'click' : name;
6272 this.fireEvent("header" + ename, this, cellIndex, e);
6277 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6278 cell = Roo.get(t).findParent('td', false, true);
6280 Roo.log("failed to find th in tbody?");
6281 Roo.log(e.getTarget());
6286 var row = cell.findParent('tr', false, true);
6287 var cellIndex = cell.dom.cellIndex;
6288 var rowIndex = row.dom.rowIndex - 1;
6292 this.fireEvent("row" + name, this, rowIndex, e);
6296 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6302 onMouseover : function(e, el)
6304 var cell = Roo.get(el);
6310 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6311 cell = cell.findParent('td', false, true);
6314 var row = cell.findParent('tr', false, true);
6315 var cellIndex = cell.dom.cellIndex;
6316 var rowIndex = row.dom.rowIndex - 1; // start from 0
6318 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6322 onMouseout : function(e, el)
6324 var cell = Roo.get(el);
6330 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6331 cell = cell.findParent('td', false, true);
6334 var row = cell.findParent('tr', false, true);
6335 var cellIndex = cell.dom.cellIndex;
6336 var rowIndex = row.dom.rowIndex - 1; // start from 0
6338 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6342 onClick : function(e, el)
6344 var cell = Roo.get(el);
6346 if(!cell || (!this.cellSelection && !this.rowSelection)){
6350 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6351 cell = cell.findParent('td', false, true);
6354 if(!cell || typeof(cell) == 'undefined'){
6358 var row = cell.findParent('tr', false, true);
6360 if(!row || typeof(row) == 'undefined'){
6364 var cellIndex = cell.dom.cellIndex;
6365 var rowIndex = this.getRowIndex(row);
6367 // why??? - should these not be based on SelectionModel?
6368 if(this.cellSelection){
6369 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6372 if(this.rowSelection){
6373 this.fireEvent('rowclick', this, row, rowIndex, e);
6379 onDblClick : function(e,el)
6381 var cell = Roo.get(el);
6383 if(!cell || (!this.cellSelection && !this.rowSelection)){
6387 if(e.getTarget().nodeName.toLowerCase() != 'td'){
6388 cell = cell.findParent('td', false, true);
6391 if(!cell || typeof(cell) == 'undefined'){
6395 var row = cell.findParent('tr', false, true);
6397 if(!row || typeof(row) == 'undefined'){
6401 var cellIndex = cell.dom.cellIndex;
6402 var rowIndex = this.getRowIndex(row);
6404 if(this.cellSelection){
6405 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6408 if(this.rowSelection){
6409 this.fireEvent('rowdblclick', this, row, rowIndex, e);
6413 sort : function(e,el)
6415 var col = Roo.get(el);
6417 if(!col.hasClass('sortable')){
6421 var sort = col.attr('sort');
6424 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6428 this.store.sortInfo = {field : sort, direction : dir};
6431 Roo.log("calling footer first");
6432 this.footer.onClick('first');
6435 this.store.load({ params : { start : 0 } });
6439 renderHeader : function()
6447 this.totalWidth = 0;
6449 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6451 var config = cm.config[i];
6455 cls : 'x-hcol-' + i,
6457 html: cm.getColumnHeader(i)
6462 if(typeof(config.sortable) != 'undefined' && config.sortable){
6464 c.html = '<i class="glyphicon"></i>' + c.html;
6467 if(typeof(config.lgHeader) != 'undefined'){
6468 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6471 if(typeof(config.mdHeader) != 'undefined'){
6472 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6475 if(typeof(config.smHeader) != 'undefined'){
6476 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6479 if(typeof(config.xsHeader) != 'undefined'){
6480 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6487 if(typeof(config.tooltip) != 'undefined'){
6488 c.tooltip = config.tooltip;
6491 if(typeof(config.colspan) != 'undefined'){
6492 c.colspan = config.colspan;
6495 if(typeof(config.hidden) != 'undefined' && config.hidden){
6496 c.style += ' display:none;';
6499 if(typeof(config.dataIndex) != 'undefined'){
6500 c.sort = config.dataIndex;
6505 if(typeof(config.align) != 'undefined' && config.align.length){
6506 c.style += ' text-align:' + config.align + ';';
6509 if(typeof(config.width) != 'undefined'){
6510 c.style += ' width:' + config.width + 'px;';
6511 this.totalWidth += config.width;
6513 this.totalWidth += 100; // assume minimum of 100 per column?
6516 if(typeof(config.cls) != 'undefined'){
6517 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6520 ['xs','sm','md','lg'].map(function(size){
6522 if(typeof(config[size]) == 'undefined'){
6526 if (!config[size]) { // 0 = hidden
6527 c.cls += ' hidden-' + size;
6531 c.cls += ' col-' + size + '-' + config[size];
6541 renderBody : function()
6551 colspan : this.cm.getColumnCount()
6561 renderFooter : function()
6571 colspan : this.cm.getColumnCount()
6585 // Roo.log('ds onload');
6590 var ds = this.store;
6592 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6593 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6594 if (_this.store.sortInfo) {
6596 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6597 e.select('i', true).addClass(['glyphicon-arrow-up']);
6600 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6601 e.select('i', true).addClass(['glyphicon-arrow-down']);
6606 var tbody = this.mainBody;
6608 if(ds.getCount() > 0){
6609 ds.data.each(function(d,rowIndex){
6610 var row = this.renderRow(cm, ds, rowIndex);
6612 tbody.createChild(row);
6616 if(row.cellObjects.length){
6617 Roo.each(row.cellObjects, function(r){
6618 _this.renderCellObject(r);
6625 var tfoot = this.el.select('tfoot', true).first();
6627 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6629 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6631 var total = this.ds.getTotalCount();
6633 if(this.footer.pageSize < total){
6634 this.mainFoot.show();
6638 Roo.each(this.el.select('tbody td', true).elements, function(e){
6639 e.on('mouseover', _this.onMouseover, _this);
6642 Roo.each(this.el.select('tbody td', true).elements, function(e){
6643 e.on('mouseout', _this.onMouseout, _this);
6645 this.fireEvent('rowsrendered', this);
6651 onUpdate : function(ds,record)
6653 this.refreshRow(record);
6657 onRemove : function(ds, record, index, isUpdate){
6658 if(isUpdate !== true){
6659 this.fireEvent("beforerowremoved", this, index, record);
6661 var bt = this.mainBody.dom;
6663 var rows = this.el.select('tbody > tr', true).elements;
6665 if(typeof(rows[index]) != 'undefined'){
6666 bt.removeChild(rows[index].dom);
6669 // if(bt.rows[index]){
6670 // bt.removeChild(bt.rows[index]);
6673 if(isUpdate !== true){
6674 //this.stripeRows(index);
6675 //this.syncRowHeights(index, index);
6677 this.fireEvent("rowremoved", this, index, record);
6681 onAdd : function(ds, records, rowIndex)
6683 //Roo.log('on Add called');
6684 // - note this does not handle multiple adding very well..
6685 var bt = this.mainBody.dom;
6686 for (var i =0 ; i < records.length;i++) {
6687 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6688 //Roo.log(records[i]);
6689 //Roo.log(this.store.getAt(rowIndex+i));
6690 this.insertRow(this.store, rowIndex + i, false);
6697 refreshRow : function(record){
6698 var ds = this.store, index;
6699 if(typeof record == 'number'){
6701 record = ds.getAt(index);
6703 index = ds.indexOf(record);
6705 this.insertRow(ds, index, true);
6707 this.onRemove(ds, record, index+1, true);
6709 //this.syncRowHeights(index, index);
6711 this.fireEvent("rowupdated", this, index, record);
6714 insertRow : function(dm, rowIndex, isUpdate){
6717 this.fireEvent("beforerowsinserted", this, rowIndex);
6719 //var s = this.getScrollState();
6720 var row = this.renderRow(this.cm, this.store, rowIndex);
6721 // insert before rowIndex..
6722 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6726 if(row.cellObjects.length){
6727 Roo.each(row.cellObjects, function(r){
6728 _this.renderCellObject(r);
6733 this.fireEvent("rowsinserted", this, rowIndex);
6734 //this.syncRowHeights(firstRow, lastRow);
6735 //this.stripeRows(firstRow);
6742 getRowDom : function(rowIndex)
6744 var rows = this.el.select('tbody > tr', true).elements;
6746 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6749 // returns the object tree for a tr..
6752 renderRow : function(cm, ds, rowIndex)
6754 var d = ds.getAt(rowIndex);
6758 cls : 'x-row-' + rowIndex,
6762 var cellObjects = [];
6764 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6765 var config = cm.config[i];
6767 var renderer = cm.getRenderer(i);
6771 if(typeof(renderer) !== 'undefined'){
6772 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6774 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6775 // and are rendered into the cells after the row is rendered - using the id for the element.
6777 if(typeof(value) === 'object'){
6787 rowIndex : rowIndex,
6792 this.fireEvent('rowclass', this, rowcfg);
6796 cls : rowcfg.rowClass + ' x-col-' + i,
6798 html: (typeof(value) === 'object') ? '' : value
6805 if(typeof(config.colspan) != 'undefined'){
6806 td.colspan = config.colspan;
6809 if(typeof(config.hidden) != 'undefined' && config.hidden){
6810 td.style += ' display:none;';
6813 if(typeof(config.align) != 'undefined' && config.align.length){
6814 td.style += ' text-align:' + config.align + ';';
6816 if(typeof(config.valign) != 'undefined' && config.valign.length){
6817 td.style += ' vertical-align:' + config.valign + ';';
6820 if(typeof(config.width) != 'undefined'){
6821 td.style += ' width:' + config.width + 'px;';
6824 if(typeof(config.cursor) != 'undefined'){
6825 td.style += ' cursor:' + config.cursor + ';';
6828 if(typeof(config.cls) != 'undefined'){
6829 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6832 ['xs','sm','md','lg'].map(function(size){
6834 if(typeof(config[size]) == 'undefined'){
6838 if (!config[size]) { // 0 = hidden
6839 td.cls += ' hidden-' + size;
6843 td.cls += ' col-' + size + '-' + config[size];
6851 row.cellObjects = cellObjects;
6859 onBeforeLoad : function()
6868 this.el.select('tbody', true).first().dom.innerHTML = '';
6871 * Show or hide a row.
6872 * @param {Number} rowIndex to show or hide
6873 * @param {Boolean} state hide
6875 setRowVisibility : function(rowIndex, state)
6877 var bt = this.mainBody.dom;
6879 var rows = this.el.select('tbody > tr', true).elements;
6881 if(typeof(rows[rowIndex]) == 'undefined'){
6884 rows[rowIndex].dom.style.display = state ? '' : 'none';
6888 getSelectionModel : function(){
6890 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6892 return this.selModel;
6895 * Render the Roo.bootstrap object from renderder
6897 renderCellObject : function(r)
6901 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6903 var t = r.cfg.render(r.container);
6906 Roo.each(r.cfg.cn, function(c){
6908 container: t.getChildContainer(),
6911 _this.renderCellObject(child);
6916 getRowIndex : function(row)
6920 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6931 * Returns the grid's underlying element = used by panel.Grid
6932 * @return {Element} The element
6934 getGridEl : function(){
6938 * Forces a resize - used by panel.Grid
6939 * @return {Element} The element
6941 autoSize : function()
6943 //var ctr = Roo.get(this.container.dom.parentElement);
6944 var ctr = Roo.get(this.el.dom);
6946 var thd = this.getGridEl().select('thead',true).first();
6947 var tbd = this.getGridEl().select('tbody', true).first();
6948 var tfd = this.getGridEl().select('tfoot', true).first();
6950 var cw = ctr.getWidth();
6954 tbd.setSize(ctr.getWidth(),
6955 ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6957 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6960 cw = Math.max(cw, this.totalWidth);
6961 this.getGridEl().select('tr',true).setWidth(cw);
6962 // resize 'expandable coloumn?
6964 return; // we doe not have a view in this design..
6967 onBodyScroll: function()
6969 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6971 this.mainHead.setStyle({
6972 'position' : 'relative',
6973 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6979 var scrollHeight = this.mainBody.dom.scrollHeight;
6981 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6983 var height = this.mainBody.getHeight();
6985 if(scrollHeight - height == scrollTop) {
6987 var total = this.ds.getTotalCount();
6989 if(this.footer.cursor + this.footer.pageSize < total){
6991 this.footer.ds.load({
6993 start : this.footer.cursor + this.footer.pageSize,
6994 limit : this.footer.pageSize
7004 onHeaderChange : function()
7006 var header = this.renderHeader();
7007 var table = this.el.select('table', true).first();
7009 this.mainHead.remove();
7010 this.mainHead = table.createChild(header, this.mainBody, false);
7013 onHiddenChange : function(colModel, colIndex, hidden)
7015 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7016 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7018 this.CSS.updateRule(thSelector, "display", "");
7019 this.CSS.updateRule(tdSelector, "display", "");
7022 this.CSS.updateRule(thSelector, "display", "none");
7023 this.CSS.updateRule(tdSelector, "display", "none");
7026 this.onHeaderChange();
7043 * @class Roo.bootstrap.TableCell
7044 * @extends Roo.bootstrap.Component
7045 * Bootstrap TableCell class
7046 * @cfg {String} html cell contain text
7047 * @cfg {String} cls cell class
7048 * @cfg {String} tag cell tag (td|th) default td
7049 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7050 * @cfg {String} align Aligns the content in a cell
7051 * @cfg {String} axis Categorizes cells
7052 * @cfg {String} bgcolor Specifies the background color of a cell
7053 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7054 * @cfg {Number} colspan Specifies the number of columns a cell should span
7055 * @cfg {String} headers Specifies one or more header cells a cell is related to
7056 * @cfg {Number} height Sets the height of a cell
7057 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7058 * @cfg {Number} rowspan Sets the number of rows a cell should span
7059 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7060 * @cfg {String} valign Vertical aligns the content in a cell
7061 * @cfg {Number} width Specifies the width of a cell
7064 * Create a new TableCell
7065 * @param {Object} config The config object
7068 Roo.bootstrap.TableCell = function(config){
7069 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7072 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
7092 getAutoCreate : function(){
7093 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7113 cfg.align=this.align
7119 cfg.bgcolor=this.bgcolor
7122 cfg.charoff=this.charoff
7125 cfg.colspan=this.colspan
7128 cfg.headers=this.headers
7131 cfg.height=this.height
7134 cfg.nowrap=this.nowrap
7137 cfg.rowspan=this.rowspan
7140 cfg.scope=this.scope
7143 cfg.valign=this.valign
7146 cfg.width=this.width
7165 * @class Roo.bootstrap.TableRow
7166 * @extends Roo.bootstrap.Component
7167 * Bootstrap TableRow class
7168 * @cfg {String} cls row class
7169 * @cfg {String} align Aligns the content in a table row
7170 * @cfg {String} bgcolor Specifies a background color for a table row
7171 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7172 * @cfg {String} valign Vertical aligns the content in a table row
7175 * Create a new TableRow
7176 * @param {Object} config The config object
7179 Roo.bootstrap.TableRow = function(config){
7180 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7183 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
7191 getAutoCreate : function(){
7192 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7202 cfg.align = this.align;
7205 cfg.bgcolor = this.bgcolor;
7208 cfg.charoff = this.charoff;
7211 cfg.valign = this.valign;
7229 * @class Roo.bootstrap.TableBody
7230 * @extends Roo.bootstrap.Component
7231 * Bootstrap TableBody class
7232 * @cfg {String} cls element class
7233 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7234 * @cfg {String} align Aligns the content inside the element
7235 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7236 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7239 * Create a new TableBody
7240 * @param {Object} config The config object
7243 Roo.bootstrap.TableBody = function(config){
7244 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7247 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
7255 getAutoCreate : function(){
7256 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7270 cfg.align = this.align;
7273 cfg.charoff = this.charoff;
7276 cfg.valign = this.valign;
7283 // initEvents : function()
7290 // this.store = Roo.factory(this.store, Roo.data);
7291 // this.store.on('load', this.onLoad, this);
7293 // this.store.load();
7297 // onLoad: function ()
7299 // this.fireEvent('load', this);
7309 * Ext JS Library 1.1.1
7310 * Copyright(c) 2006-2007, Ext JS, LLC.
7312 * Originally Released Under LGPL - original licence link has changed is not relivant.
7315 * <script type="text/javascript">
7318 // as we use this in bootstrap.
7319 Roo.namespace('Roo.form');
7321 * @class Roo.form.Action
7322 * Internal Class used to handle form actions
7324 * @param {Roo.form.BasicForm} el The form element or its id
7325 * @param {Object} config Configuration options
7330 // define the action interface
7331 Roo.form.Action = function(form, options){
7333 this.options = options || {};
7336 * Client Validation Failed
7339 Roo.form.Action.CLIENT_INVALID = 'client';
7341 * Server Validation Failed
7344 Roo.form.Action.SERVER_INVALID = 'server';
7346 * Connect to Server Failed
7349 Roo.form.Action.CONNECT_FAILURE = 'connect';
7351 * Reading Data from Server Failed
7354 Roo.form.Action.LOAD_FAILURE = 'load';
7356 Roo.form.Action.prototype = {
7358 failureType : undefined,
7359 response : undefined,
7363 run : function(options){
7368 success : function(response){
7373 handleResponse : function(response){
7377 // default connection failure
7378 failure : function(response){
7380 this.response = response;
7381 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7382 this.form.afterAction(this, false);
7385 processResponse : function(response){
7386 this.response = response;
7387 if(!response.responseText){
7390 this.result = this.handleResponse(response);
7394 // utility functions used internally
7395 getUrl : function(appendParams){
7396 var url = this.options.url || this.form.url || this.form.el.dom.action;
7398 var p = this.getParams();
7400 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7406 getMethod : function(){
7407 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7410 getParams : function(){
7411 var bp = this.form.baseParams;
7412 var p = this.options.params;
7414 if(typeof p == "object"){
7415 p = Roo.urlEncode(Roo.applyIf(p, bp));
7416 }else if(typeof p == 'string' && bp){
7417 p += '&' + Roo.urlEncode(bp);
7420 p = Roo.urlEncode(bp);
7425 createCallback : function(){
7427 success: this.success,
7428 failure: this.failure,
7430 timeout: (this.form.timeout*1000),
7431 upload: this.form.fileUpload ? this.success : undefined
7436 Roo.form.Action.Submit = function(form, options){
7437 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7440 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7443 haveProgress : false,
7444 uploadComplete : false,
7446 // uploadProgress indicator.
7447 uploadProgress : function()
7449 if (!this.form.progressUrl) {
7453 if (!this.haveProgress) {
7454 Roo.MessageBox.progress("Uploading", "Uploading");
7456 if (this.uploadComplete) {
7457 Roo.MessageBox.hide();
7461 this.haveProgress = true;
7463 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7465 var c = new Roo.data.Connection();
7467 url : this.form.progressUrl,
7472 success : function(req){
7473 //console.log(data);
7477 rdata = Roo.decode(req.responseText)
7479 Roo.log("Invalid data from server..");
7483 if (!rdata || !rdata.success) {
7485 Roo.MessageBox.alert(Roo.encode(rdata));
7488 var data = rdata.data;
7490 if (this.uploadComplete) {
7491 Roo.MessageBox.hide();
7496 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7497 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7500 this.uploadProgress.defer(2000,this);
7503 failure: function(data) {
7504 Roo.log('progress url failed ');
7515 // run get Values on the form, so it syncs any secondary forms.
7516 this.form.getValues();
7518 var o = this.options;
7519 var method = this.getMethod();
7520 var isPost = method == 'POST';
7521 if(o.clientValidation === false || this.form.isValid()){
7523 if (this.form.progressUrl) {
7524 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7525 (new Date() * 1) + '' + Math.random());
7530 Roo.Ajax.request(Roo.apply(this.createCallback(), {
7531 form:this.form.el.dom,
7532 url:this.getUrl(!isPost),
7534 params:isPost ? this.getParams() : null,
7535 isUpload: this.form.fileUpload
7538 this.uploadProgress();
7540 }else if (o.clientValidation !== false){ // client validation failed
7541 this.failureType = Roo.form.Action.CLIENT_INVALID;
7542 this.form.afterAction(this, false);
7546 success : function(response)
7548 this.uploadComplete= true;
7549 if (this.haveProgress) {
7550 Roo.MessageBox.hide();
7554 var result = this.processResponse(response);
7555 if(result === true || result.success){
7556 this.form.afterAction(this, true);
7560 this.form.markInvalid(result.errors);
7561 this.failureType = Roo.form.Action.SERVER_INVALID;
7563 this.form.afterAction(this, false);
7565 failure : function(response)
7567 this.uploadComplete= true;
7568 if (this.haveProgress) {
7569 Roo.MessageBox.hide();
7572 this.response = response;
7573 this.failureType = Roo.form.Action.CONNECT_FAILURE;
7574 this.form.afterAction(this, false);
7577 handleResponse : function(response){
7578 if(this.form.errorReader){
7579 var rs = this.form.errorReader.read(response);
7582 for(var i = 0, len = rs.records.length; i < len; i++) {
7583 var r = rs.records[i];
7587 if(errors.length < 1){
7591 success : rs.success,
7597 ret = Roo.decode(response.responseText);
7601 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7611 Roo.form.Action.Load = function(form, options){
7612 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7613 this.reader = this.form.reader;
7616 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7621 Roo.Ajax.request(Roo.apply(
7622 this.createCallback(), {
7623 method:this.getMethod(),
7624 url:this.getUrl(false),
7625 params:this.getParams()
7629 success : function(response){
7631 var result = this.processResponse(response);
7632 if(result === true || !result.success || !result.data){
7633 this.failureType = Roo.form.Action.LOAD_FAILURE;
7634 this.form.afterAction(this, false);
7637 this.form.clearInvalid();
7638 this.form.setValues(result.data);
7639 this.form.afterAction(this, true);
7642 handleResponse : function(response){
7643 if(this.form.reader){
7644 var rs = this.form.reader.read(response);
7645 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7647 success : rs.success,
7651 return Roo.decode(response.responseText);
7655 Roo.form.Action.ACTION_TYPES = {
7656 'load' : Roo.form.Action.Load,
7657 'submit' : Roo.form.Action.Submit
7666 * @class Roo.bootstrap.Form
7667 * @extends Roo.bootstrap.Component
7668 * Bootstrap Form class
7669 * @cfg {String} method GET | POST (default POST)
7670 * @cfg {String} labelAlign top | left (default top)
7671 * @cfg {String} align left | right - for navbars
7672 * @cfg {Boolean} loadMask load mask when submit (default true)
7677 * @param {Object} config The config object
7681 Roo.bootstrap.Form = function(config){
7683 Roo.bootstrap.Form.superclass.constructor.call(this, config);
7685 Roo.bootstrap.Form.popover.apply();
7689 * @event clientvalidation
7690 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7691 * @param {Form} this
7692 * @param {Boolean} valid true if the form has passed client-side validation
7694 clientvalidation: true,
7696 * @event beforeaction
7697 * Fires before any action is performed. Return false to cancel the action.
7698 * @param {Form} this
7699 * @param {Action} action The action to be performed
7703 * @event actionfailed
7704 * Fires when an action fails.
7705 * @param {Form} this
7706 * @param {Action} action The action that failed
7708 actionfailed : true,
7710 * @event actioncomplete
7711 * Fires when an action is completed.
7712 * @param {Form} this
7713 * @param {Action} action The action that completed
7715 actioncomplete : true
7719 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
7722 * @cfg {String} method
7723 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7728 * The URL to use for form actions if one isn't supplied in the action options.
7731 * @cfg {Boolean} fileUpload
7732 * Set to true if this form is a file upload.
7736 * @cfg {Object} baseParams
7737 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7741 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7745 * @cfg {Sting} align (left|right) for navbar forms
7750 activeAction : null,
7753 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7754 * element by passing it or its id or mask the form itself by passing in true.
7757 waitMsgTarget : false,
7762 * @cfg {Boolean} errorMask (true|false) default false
7767 * @cfg {Number} maskOffset Default 100
7772 * @cfg {Boolean} maskBody
7776 getAutoCreate : function(){
7780 method : this.method || 'POST',
7781 id : this.id || Roo.id(),
7784 if (this.parent().xtype.match(/^Nav/)) {
7785 cfg.cls = 'navbar-form navbar-' + this.align;
7789 if (this.labelAlign == 'left' ) {
7790 cfg.cls += ' form-horizontal';
7796 initEvents : function()
7798 this.el.on('submit', this.onSubmit, this);
7799 // this was added as random key presses on the form where triggering form submit.
7800 this.el.on('keypress', function(e) {
7801 if (e.getCharCode() != 13) {
7804 // we might need to allow it for textareas.. and some other items.
7805 // check e.getTarget().
7807 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7811 Roo.log("keypress blocked");
7819 onSubmit : function(e){
7824 * Returns true if client-side validation on the form is successful.
7827 isValid : function(){
7828 var items = this.getItems();
7832 items.each(function(f){
7840 if(!target && f.el.isVisible(true)){
7846 if(this.errorMask && !valid){
7847 Roo.bootstrap.Form.popover.mask(this, target);
7854 * Returns true if any fields in this form have changed since their original load.
7857 isDirty : function(){
7859 var items = this.getItems();
7860 items.each(function(f){
7870 * Performs a predefined action (submit or load) or custom actions you define on this form.
7871 * @param {String} actionName The name of the action type
7872 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7873 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7874 * accept other config options):
7876 Property Type Description
7877 ---------------- --------------- ----------------------------------------------------------------------------------
7878 url String The url for the action (defaults to the form's url)
7879 method String The form method to use (defaults to the form's method, or POST if not defined)
7880 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7881 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7882 validate the form on the client (defaults to false)
7884 * @return {BasicForm} this
7886 doAction : function(action, options){
7887 if(typeof action == 'string'){
7888 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7890 if(this.fireEvent('beforeaction', this, action) !== false){
7891 this.beforeAction(action);
7892 action.run.defer(100, action);
7898 beforeAction : function(action){
7899 var o = action.options;
7904 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7906 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7909 // not really supported yet.. ??
7911 //if(this.waitMsgTarget === true){
7912 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7913 //}else if(this.waitMsgTarget){
7914 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7915 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7917 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7923 afterAction : function(action, success){
7924 this.activeAction = null;
7925 var o = action.options;
7930 Roo.get(document.body).unmask();
7936 //if(this.waitMsgTarget === true){
7937 // this.el.unmask();
7938 //}else if(this.waitMsgTarget){
7939 // this.waitMsgTarget.unmask();
7941 // Roo.MessageBox.updateProgress(1);
7942 // Roo.MessageBox.hide();
7949 Roo.callback(o.success, o.scope, [this, action]);
7950 this.fireEvent('actioncomplete', this, action);
7954 // failure condition..
7955 // we have a scenario where updates need confirming.
7956 // eg. if a locking scenario exists..
7957 // we look for { errors : { needs_confirm : true }} in the response.
7959 (typeof(action.result) != 'undefined') &&
7960 (typeof(action.result.errors) != 'undefined') &&
7961 (typeof(action.result.errors.needs_confirm) != 'undefined')
7964 Roo.log("not supported yet");
7967 Roo.MessageBox.confirm(
7968 "Change requires confirmation",
7969 action.result.errorMsg,
7974 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7984 Roo.callback(o.failure, o.scope, [this, action]);
7985 // show an error message if no failed handler is set..
7986 if (!this.hasListener('actionfailed')) {
7987 Roo.log("need to add dialog support");
7989 Roo.MessageBox.alert("Error",
7990 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7991 action.result.errorMsg :
7992 "Saving Failed, please check your entries or try again"
7997 this.fireEvent('actionfailed', this, action);
8002 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8003 * @param {String} id The value to search for
8006 findField : function(id){
8007 var items = this.getItems();
8008 var field = items.get(id);
8010 items.each(function(f){
8011 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8018 return field || null;
8021 * Mark fields in this form invalid in bulk.
8022 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8023 * @return {BasicForm} this
8025 markInvalid : function(errors){
8026 if(errors instanceof Array){
8027 for(var i = 0, len = errors.length; i < len; i++){
8028 var fieldError = errors[i];
8029 var f = this.findField(fieldError.id);
8031 f.markInvalid(fieldError.msg);
8037 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8038 field.markInvalid(errors[id]);
8042 //Roo.each(this.childForms || [], function (f) {
8043 // f.markInvalid(errors);
8050 * Set values for fields in this form in bulk.
8051 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8052 * @return {BasicForm} this
8054 setValues : function(values){
8055 if(values instanceof Array){ // array of objects
8056 for(var i = 0, len = values.length; i < len; i++){
8058 var f = this.findField(v.id);
8060 f.setValue(v.value);
8061 if(this.trackResetOnLoad){
8062 f.originalValue = f.getValue();
8066 }else{ // object hash
8069 if(typeof values[id] != 'function' && (field = this.findField(id))){
8071 if (field.setFromData &&
8073 field.displayField &&
8074 // combos' with local stores can
8075 // be queried via setValue()
8076 // to set their value..
8077 (field.store && !field.store.isLocal)
8081 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8082 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8083 field.setFromData(sd);
8085 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8087 field.setFromData(values);
8090 field.setValue(values[id]);
8094 if(this.trackResetOnLoad){
8095 field.originalValue = field.getValue();
8101 //Roo.each(this.childForms || [], function (f) {
8102 // f.setValues(values);
8109 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8110 * they are returned as an array.
8111 * @param {Boolean} asString
8114 getValues : function(asString){
8115 //if (this.childForms) {
8116 // copy values from the child forms
8117 // Roo.each(this.childForms, function (f) {
8118 // this.setValues(f.getValues());
8124 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8125 if(asString === true){
8128 return Roo.urlDecode(fs);
8132 * Returns the fields in this form as an object with key/value pairs.
8133 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8136 getFieldValues : function(with_hidden)
8138 var items = this.getItems();
8140 items.each(function(f){
8146 var v = f.getValue();
8148 if (f.inputType =='radio') {
8149 if (typeof(ret[f.getName()]) == 'undefined') {
8150 ret[f.getName()] = ''; // empty..
8153 if (!f.el.dom.checked) {
8161 if(f.xtype == 'MoneyField'){
8162 ret[f.currencyName] = f.getCurrency();
8165 // not sure if this supported any more..
8166 if ((typeof(v) == 'object') && f.getRawValue) {
8167 v = f.getRawValue() ; // dates..
8169 // combo boxes where name != hiddenName...
8170 if (f.name !== false && f.name != '' && f.name != f.getName()) {
8171 ret[f.name] = f.getRawValue();
8173 ret[f.getName()] = v;
8180 * Clears all invalid messages in this form.
8181 * @return {BasicForm} this
8183 clearInvalid : function(){
8184 var items = this.getItems();
8186 items.each(function(f){
8195 * @return {BasicForm} this
8198 var items = this.getItems();
8199 items.each(function(f){
8203 Roo.each(this.childForms || [], function (f) {
8211 getItems : function()
8213 var r=new Roo.util.MixedCollection(false, function(o){
8214 return o.id || (o.id = Roo.id());
8216 var iter = function(el) {
8223 Roo.each(el.items,function(e) {
8232 hideFields : function(items)
8234 Roo.each(items, function(i){
8236 var f = this.findField(i);
8242 if(f.xtype == 'DateField'){
8243 f.setVisible(false);
8252 showFields : function(items)
8254 Roo.each(items, function(i){
8256 var f = this.findField(i);
8262 if(f.xtype == 'DateField'){
8274 Roo.apply(Roo.bootstrap.Form, {
8301 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8302 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8303 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8304 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8307 this.maskEl.top.enableDisplayMode("block");
8308 this.maskEl.left.enableDisplayMode("block");
8309 this.maskEl.bottom.enableDisplayMode("block");
8310 this.maskEl.right.enableDisplayMode("block");
8312 this.toolTip = new Roo.bootstrap.Tooltip({
8313 cls : 'roo-form-error-popover',
8315 'left' : ['r-l', [-2,0], 'right'],
8316 'right' : ['l-r', [2,0], 'left'],
8317 'bottom' : ['tl-bl', [0,2], 'top'],
8318 'top' : [ 'bl-tl', [0,-2], 'bottom']
8322 this.toolTip.render(Roo.get(document.body));
8324 this.toolTip.el.enableDisplayMode("block");
8326 Roo.get(document.body).on('click', function(){
8330 Roo.get(document.body).on('touchstart', function(){
8334 this.isApplied = true
8337 mask : function(form, target)
8341 this.target = target;
8343 if(!this.form.errorMask || !target.el){
8347 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8349 Roo.log(scrollable);
8351 var ot = this.target.el.calcOffsetsTo(scrollable);
8353 var scrollTo = ot[1] - this.form.maskOffset;
8355 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8357 scrollable.scrollTo('top', scrollTo);
8359 var box = this.target.el.getBox();
8361 var zIndex = Roo.bootstrap.Modal.zIndex++;
8364 this.maskEl.top.setStyle('position', 'absolute');
8365 this.maskEl.top.setStyle('z-index', zIndex);
8366 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8367 this.maskEl.top.setLeft(0);
8368 this.maskEl.top.setTop(0);
8369 this.maskEl.top.show();
8371 this.maskEl.left.setStyle('position', 'absolute');
8372 this.maskEl.left.setStyle('z-index', zIndex);
8373 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8374 this.maskEl.left.setLeft(0);
8375 this.maskEl.left.setTop(box.y - this.padding);
8376 this.maskEl.left.show();
8378 this.maskEl.bottom.setStyle('position', 'absolute');
8379 this.maskEl.bottom.setStyle('z-index', zIndex);
8380 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8381 this.maskEl.bottom.setLeft(0);
8382 this.maskEl.bottom.setTop(box.bottom + this.padding);
8383 this.maskEl.bottom.show();
8385 this.maskEl.right.setStyle('position', 'absolute');
8386 this.maskEl.right.setStyle('z-index', zIndex);
8387 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8388 this.maskEl.right.setLeft(box.right + this.padding);
8389 this.maskEl.right.setTop(box.y - this.padding);
8390 this.maskEl.right.show();
8392 this.toolTip.bindEl = this.target.el;
8394 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8396 var tip = this.target.blankText;
8398 if(this.target.getValue() !== '' ) {
8400 if (this.target.invalidText.length) {
8401 tip = this.target.invalidText;
8402 } else if (this.target.regexText.length){
8403 tip = this.target.regexText;
8407 this.toolTip.show(tip);
8409 this.intervalID = window.setInterval(function() {
8410 Roo.bootstrap.Form.popover.unmask();
8413 window.onwheel = function(){ return false;};
8415 (function(){ this.isMasked = true; }).defer(500, this);
8421 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8425 this.maskEl.top.setStyle('position', 'absolute');
8426 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8427 this.maskEl.top.hide();
8429 this.maskEl.left.setStyle('position', 'absolute');
8430 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8431 this.maskEl.left.hide();
8433 this.maskEl.bottom.setStyle('position', 'absolute');
8434 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8435 this.maskEl.bottom.hide();
8437 this.maskEl.right.setStyle('position', 'absolute');
8438 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8439 this.maskEl.right.hide();
8441 this.toolTip.hide();
8443 this.toolTip.el.hide();
8445 window.onwheel = function(){ return true;};
8447 if(this.intervalID){
8448 window.clearInterval(this.intervalID);
8449 this.intervalID = false;
8452 this.isMasked = false;
8462 * Ext JS Library 1.1.1
8463 * Copyright(c) 2006-2007, Ext JS, LLC.
8465 * Originally Released Under LGPL - original licence link has changed is not relivant.
8468 * <script type="text/javascript">
8471 * @class Roo.form.VTypes
8472 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8475 Roo.form.VTypes = function(){
8476 // closure these in so they are only created once.
8477 var alpha = /^[a-zA-Z_]+$/;
8478 var alphanum = /^[a-zA-Z0-9_]+$/;
8479 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8480 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8482 // All these messages and functions are configurable
8485 * The function used to validate email addresses
8486 * @param {String} value The email address
8488 'email' : function(v){
8489 return email.test(v);
8492 * The error text to display when the email validation function returns false
8495 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8497 * The keystroke filter mask to be applied on email input
8500 'emailMask' : /[a-z0-9_\.\-@]/i,
8503 * The function used to validate URLs
8504 * @param {String} value The URL
8506 'url' : function(v){
8510 * The error text to display when the url validation function returns false
8513 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8516 * The function used to validate alpha values
8517 * @param {String} value The value
8519 'alpha' : function(v){
8520 return alpha.test(v);
8523 * The error text to display when the alpha validation function returns false
8526 'alphaText' : 'This field should only contain letters and _',
8528 * The keystroke filter mask to be applied on alpha input
8531 'alphaMask' : /[a-z_]/i,
8534 * The function used to validate alphanumeric values
8535 * @param {String} value The value
8537 'alphanum' : function(v){
8538 return alphanum.test(v);
8541 * The error text to display when the alphanumeric validation function returns false
8544 'alphanumText' : 'This field should only contain letters, numbers and _',
8546 * The keystroke filter mask to be applied on alphanumeric input
8549 'alphanumMask' : /[a-z0-9_]/i
8559 * @class Roo.bootstrap.Input
8560 * @extends Roo.bootstrap.Component
8561 * Bootstrap Input class
8562 * @cfg {Boolean} disabled is it disabled
8563 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8564 * @cfg {String} name name of the input
8565 * @cfg {string} fieldLabel - the label associated
8566 * @cfg {string} placeholder - placeholder to put in text.
8567 * @cfg {string} before - input group add on before
8568 * @cfg {string} after - input group add on after
8569 * @cfg {string} size - (lg|sm) or leave empty..
8570 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8571 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8572 * @cfg {Number} md colspan out of 12 for computer-sized screens
8573 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8574 * @cfg {string} value default value of the input
8575 * @cfg {Number} labelWidth set the width of label
8576 * @cfg {Number} labellg set the width of label (1-12)
8577 * @cfg {Number} labelmd set the width of label (1-12)
8578 * @cfg {Number} labelsm set the width of label (1-12)
8579 * @cfg {Number} labelxs set the width of label (1-12)
8580 * @cfg {String} labelAlign (top|left)
8581 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8582 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8583 * @cfg {String} indicatorpos (left|right) default left
8584 * @cfg {String} capture (user|camera) use for file input only. (default empty)
8585 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8587 * @cfg {String} align (left|center|right) Default left
8588 * @cfg {Boolean} forceFeedback (true|false) Default false
8591 * Create a new Input
8592 * @param {Object} config The config object
8595 Roo.bootstrap.Input = function(config){
8597 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8602 * Fires when this field receives input focus.
8603 * @param {Roo.form.Field} this
8608 * Fires when this field loses input focus.
8609 * @param {Roo.form.Field} this
8614 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8615 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8616 * @param {Roo.form.Field} this
8617 * @param {Roo.EventObject} e The event object
8622 * Fires just before the field blurs if the field value has changed.
8623 * @param {Roo.form.Field} this
8624 * @param {Mixed} newValue The new value
8625 * @param {Mixed} oldValue The original value
8630 * Fires after the field has been marked as invalid.
8631 * @param {Roo.form.Field} this
8632 * @param {String} msg The validation message
8637 * Fires after the field has been validated with no errors.
8638 * @param {Roo.form.Field} this
8643 * Fires after the key up
8644 * @param {Roo.form.Field} this
8645 * @param {Roo.EventObject} e The event Object
8651 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8653 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8654 automatic validation (defaults to "keyup").
8656 validationEvent : "keyup",
8658 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8660 validateOnBlur : true,
8662 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8664 validationDelay : 250,
8666 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8668 focusClass : "x-form-focus", // not needed???
8672 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8674 invalidClass : "has-warning",
8677 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8679 validClass : "has-success",
8682 * @cfg {Boolean} hasFeedback (true|false) default true
8687 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8689 invalidFeedbackClass : "glyphicon-warning-sign",
8692 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8694 validFeedbackClass : "glyphicon-ok",
8697 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8699 selectOnFocus : false,
8702 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8706 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8711 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8713 disableKeyFilter : false,
8716 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8720 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8724 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8726 blankText : "Please complete this mandatory field",
8729 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8733 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8735 maxLength : Number.MAX_VALUE,
8737 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8739 minLengthText : "The minimum length for this field is {0}",
8741 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8743 maxLengthText : "The maximum length for this field is {0}",
8747 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8748 * If available, this function will be called only after the basic validators all return true, and will be passed the
8749 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8753 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8754 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8755 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8759 * @cfg {String} regexText -- Depricated - use Invalid Text
8764 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8770 autocomplete: false,
8789 formatedValue : false,
8790 forceFeedback : false,
8792 indicatorpos : 'left',
8802 parentLabelAlign : function()
8805 while (parent.parent()) {
8806 parent = parent.parent();
8807 if (typeof(parent.labelAlign) !='undefined') {
8808 return parent.labelAlign;
8815 getAutoCreate : function()
8817 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8823 if(this.inputType != 'hidden'){
8824 cfg.cls = 'form-group' //input-group
8830 type : this.inputType,
8832 cls : 'form-control',
8833 placeholder : this.placeholder || '',
8834 autocomplete : this.autocomplete || 'new-password'
8837 if(this.capture.length){
8838 input.capture = this.capture;
8841 if(this.accept.length){
8842 input.accept = this.accept + "/*";
8846 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8849 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8850 input.maxLength = this.maxLength;
8853 if (this.disabled) {
8854 input.disabled=true;
8857 if (this.readOnly) {
8858 input.readonly=true;
8862 input.name = this.name;
8866 input.cls += ' input-' + this.size;
8870 ['xs','sm','md','lg'].map(function(size){
8871 if (settings[size]) {
8872 cfg.cls += ' col-' + size + '-' + settings[size];
8876 var inputblock = input;
8880 cls: 'glyphicon form-control-feedback'
8883 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8886 cls : 'has-feedback',
8894 if (this.before || this.after) {
8897 cls : 'input-group',
8901 if (this.before && typeof(this.before) == 'string') {
8903 inputblock.cn.push({
8905 cls : 'roo-input-before input-group-addon',
8909 if (this.before && typeof(this.before) == 'object') {
8910 this.before = Roo.factory(this.before);
8912 inputblock.cn.push({
8914 cls : 'roo-input-before input-group-' +
8915 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8919 inputblock.cn.push(input);
8921 if (this.after && typeof(this.after) == 'string') {
8922 inputblock.cn.push({
8924 cls : 'roo-input-after input-group-addon',
8928 if (this.after && typeof(this.after) == 'object') {
8929 this.after = Roo.factory(this.after);
8931 inputblock.cn.push({
8933 cls : 'roo-input-after input-group-' +
8934 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8938 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8939 inputblock.cls += ' has-feedback';
8940 inputblock.cn.push(feedback);
8944 if (align ==='left' && this.fieldLabel.length) {
8946 cfg.cls += ' roo-form-group-label-left';
8951 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8952 tooltip : 'This field is required'
8957 cls : 'control-label',
8958 html : this.fieldLabel
8969 var labelCfg = cfg.cn[1];
8970 var contentCfg = cfg.cn[2];
8972 if(this.indicatorpos == 'right'){
8977 cls : 'control-label',
8981 html : this.fieldLabel
8985 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8986 tooltip : 'This field is required'
8999 labelCfg = cfg.cn[0];
9000 contentCfg = cfg.cn[1];
9004 if(this.labelWidth > 12){
9005 labelCfg.style = "width: " + this.labelWidth + 'px';
9008 if(this.labelWidth < 13 && this.labelmd == 0){
9009 this.labelmd = this.labelWidth;
9012 if(this.labellg > 0){
9013 labelCfg.cls += ' col-lg-' + this.labellg;
9014 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9017 if(this.labelmd > 0){
9018 labelCfg.cls += ' col-md-' + this.labelmd;
9019 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9022 if(this.labelsm > 0){
9023 labelCfg.cls += ' col-sm-' + this.labelsm;
9024 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9027 if(this.labelxs > 0){
9028 labelCfg.cls += ' col-xs-' + this.labelxs;
9029 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9033 } else if ( this.fieldLabel.length) {
9038 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9039 tooltip : 'This field is required'
9043 //cls : 'input-group-addon',
9044 html : this.fieldLabel
9052 if(this.indicatorpos == 'right'){
9057 //cls : 'input-group-addon',
9058 html : this.fieldLabel
9063 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9064 tooltip : 'This field is required'
9084 if (this.parentType === 'Navbar' && this.parent().bar) {
9085 cfg.cls += ' navbar-form';
9088 if (this.parentType === 'NavGroup') {
9089 cfg.cls += ' navbar-form';
9097 * return the real input element.
9099 inputEl: function ()
9101 return this.el.select('input.form-control',true).first();
9104 tooltipEl : function()
9106 return this.inputEl();
9109 indicatorEl : function()
9111 var indicator = this.el.select('i.roo-required-indicator',true).first();
9121 setDisabled : function(v)
9123 var i = this.inputEl().dom;
9125 i.removeAttribute('disabled');
9129 i.setAttribute('disabled','true');
9131 initEvents : function()
9134 this.inputEl().on("keydown" , this.fireKey, this);
9135 this.inputEl().on("focus", this.onFocus, this);
9136 this.inputEl().on("blur", this.onBlur, this);
9138 this.inputEl().relayEvent('keyup', this);
9140 this.indicator = this.indicatorEl();
9143 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
9146 // reference to original value for reset
9147 this.originalValue = this.getValue();
9148 //Roo.form.TextField.superclass.initEvents.call(this);
9149 if(this.validationEvent == 'keyup'){
9150 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9151 this.inputEl().on('keyup', this.filterValidation, this);
9153 else if(this.validationEvent !== false){
9154 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9157 if(this.selectOnFocus){
9158 this.on("focus", this.preFocus, this);
9161 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9162 this.inputEl().on("keypress", this.filterKeys, this);
9164 this.inputEl().relayEvent('keypress', this);
9167 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
9168 this.el.on("click", this.autoSize, this);
9171 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9172 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9175 if (typeof(this.before) == 'object') {
9176 this.before.render(this.el.select('.roo-input-before',true).first());
9178 if (typeof(this.after) == 'object') {
9179 this.after.render(this.el.select('.roo-input-after',true).first());
9182 this.inputEl().on('change', this.onChange, this);
9185 filterValidation : function(e){
9186 if(!e.isNavKeyPress()){
9187 this.validationTask.delay(this.validationDelay);
9191 * Validates the field value
9192 * @return {Boolean} True if the value is valid, else false
9194 validate : function(){
9195 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9196 if(this.disabled || this.validateValue(this.getRawValue())){
9207 * Validates a value according to the field's validation rules and marks the field as invalid
9208 * if the validation fails
9209 * @param {Mixed} value The value to validate
9210 * @return {Boolean} True if the value is valid, else false
9212 validateValue : function(value)
9214 if(this.getVisibilityEl().hasClass('hidden')){
9218 if(value.length < 1) { // if it's blank
9219 if(this.allowBlank){
9225 if(value.length < this.minLength){
9228 if(value.length > this.maxLength){
9232 var vt = Roo.form.VTypes;
9233 if(!vt[this.vtype](value, this)){
9237 if(typeof this.validator == "function"){
9238 var msg = this.validator(value);
9242 if (typeof(msg) == 'string') {
9243 this.invalidText = msg;
9247 if(this.regex && !this.regex.test(value)){
9255 fireKey : function(e){
9256 //Roo.log('field ' + e.getKey());
9257 if(e.isNavKeyPress()){
9258 this.fireEvent("specialkey", this, e);
9261 focus : function (selectText){
9263 this.inputEl().focus();
9264 if(selectText === true){
9265 this.inputEl().dom.select();
9271 onFocus : function(){
9272 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9273 // this.el.addClass(this.focusClass);
9276 this.hasFocus = true;
9277 this.startValue = this.getValue();
9278 this.fireEvent("focus", this);
9282 beforeBlur : Roo.emptyFn,
9286 onBlur : function(){
9288 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9289 //this.el.removeClass(this.focusClass);
9291 this.hasFocus = false;
9292 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9295 var v = this.getValue();
9296 if(String(v) !== String(this.startValue)){
9297 this.fireEvent('change', this, v, this.startValue);
9299 this.fireEvent("blur", this);
9302 onChange : function(e)
9304 var v = this.getValue();
9305 if(String(v) !== String(this.startValue)){
9306 this.fireEvent('change', this, v, this.startValue);
9312 * Resets the current field value to the originally loaded value and clears any validation messages
9315 this.setValue(this.originalValue);
9319 * Returns the name of the field
9320 * @return {Mixed} name The name field
9322 getName: function(){
9326 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
9327 * @return {Mixed} value The field value
9329 getValue : function(){
9331 var v = this.inputEl().getValue();
9336 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
9337 * @return {Mixed} value The field value
9339 getRawValue : function(){
9340 var v = this.inputEl().getValue();
9346 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
9347 * @param {Mixed} value The value to set
9349 setRawValue : function(v){
9350 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9353 selectText : function(start, end){
9354 var v = this.getRawValue();
9356 start = start === undefined ? 0 : start;
9357 end = end === undefined ? v.length : end;
9358 var d = this.inputEl().dom;
9359 if(d.setSelectionRange){
9360 d.setSelectionRange(start, end);
9361 }else if(d.createTextRange){
9362 var range = d.createTextRange();
9363 range.moveStart("character", start);
9364 range.moveEnd("character", v.length-end);
9371 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
9372 * @param {Mixed} value The value to set
9374 setValue : function(v){
9377 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9383 processValue : function(value){
9384 if(this.stripCharsRe){
9385 var newValue = value.replace(this.stripCharsRe, '');
9386 if(newValue !== value){
9387 this.setRawValue(newValue);
9394 preFocus : function(){
9396 if(this.selectOnFocus){
9397 this.inputEl().dom.select();
9400 filterKeys : function(e){
9402 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9405 var c = e.getCharCode(), cc = String.fromCharCode(c);
9406 if(Roo.isIE && (e.isSpecialKey() || !cc)){
9409 if(!this.maskRe.test(cc)){
9414 * Clear any invalid styles/messages for this field
9416 clearInvalid : function(){
9418 if(!this.el || this.preventMark){ // not rendered
9423 this.el.removeClass(this.invalidClass);
9425 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9427 var feedback = this.el.select('.form-control-feedback', true).first();
9430 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9436 this.indicator.removeClass('visible');
9437 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9440 this.fireEvent('valid', this);
9444 * Mark this field as valid
9446 markValid : function()
9448 if(!this.el || this.preventMark){ // not rendered...
9452 this.el.removeClass([this.invalidClass, this.validClass]);
9454 var feedback = this.el.select('.form-control-feedback', true).first();
9457 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9461 this.indicator.removeClass('visible');
9462 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9469 if(this.allowBlank && !this.getRawValue().length){
9473 this.el.addClass(this.validClass);
9475 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9477 var feedback = this.el.select('.form-control-feedback', true).first();
9480 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9481 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9486 this.fireEvent('valid', this);
9490 * Mark this field as invalid
9491 * @param {String} msg The validation message
9493 markInvalid : function(msg)
9495 if(!this.el || this.preventMark){ // not rendered
9499 this.el.removeClass([this.invalidClass, this.validClass]);
9501 var feedback = this.el.select('.form-control-feedback', true).first();
9504 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9511 if(this.allowBlank && !this.getRawValue().length){
9516 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9517 this.indicator.addClass('visible');
9520 this.el.addClass(this.invalidClass);
9522 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9524 var feedback = this.el.select('.form-control-feedback', true).first();
9527 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9529 if(this.getValue().length || this.forceFeedback){
9530 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9537 this.fireEvent('invalid', this, msg);
9540 SafariOnKeyDown : function(event)
9542 // this is a workaround for a password hang bug on chrome/ webkit.
9543 if (this.inputEl().dom.type != 'password') {
9547 var isSelectAll = false;
9549 if(this.inputEl().dom.selectionEnd > 0){
9550 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9552 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9553 event.preventDefault();
9558 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9560 event.preventDefault();
9561 // this is very hacky as keydown always get's upper case.
9563 var cc = String.fromCharCode(event.getCharCode());
9564 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9568 adjustWidth : function(tag, w){
9569 tag = tag.toLowerCase();
9570 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9571 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9575 if(tag == 'textarea'){
9578 }else if(Roo.isOpera){
9582 if(tag == 'textarea'){
9590 setFieldLabel : function(v)
9597 var ar = this.el.select('label > span',true);
9599 if (ar.elements.length) {
9600 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9601 this.fieldLabel = v;
9605 var br = this.el.select('label',true);
9607 if(br.elements.length) {
9608 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9609 this.fieldLabel = v;
9613 Roo.log('Cannot Found any of label > span || label in input');
9617 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9618 this.fieldLabel = v;
9633 * @class Roo.bootstrap.TextArea
9634 * @extends Roo.bootstrap.Input
9635 * Bootstrap TextArea class
9636 * @cfg {Number} cols Specifies the visible width of a text area
9637 * @cfg {Number} rows Specifies the visible number of lines in a text area
9638 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9639 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9640 * @cfg {string} html text
9643 * Create a new TextArea
9644 * @param {Object} config The config object
9647 Roo.bootstrap.TextArea = function(config){
9648 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9652 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9662 getAutoCreate : function(){
9664 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9670 if(this.inputType != 'hidden'){
9671 cfg.cls = 'form-group' //input-group
9679 value : this.value || '',
9680 html: this.html || '',
9681 cls : 'form-control',
9682 placeholder : this.placeholder || ''
9686 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9687 input.maxLength = this.maxLength;
9691 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9695 input.cols = this.cols;
9698 if (this.readOnly) {
9699 input.readonly = true;
9703 input.name = this.name;
9707 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9711 ['xs','sm','md','lg'].map(function(size){
9712 if (settings[size]) {
9713 cfg.cls += ' col-' + size + '-' + settings[size];
9717 var inputblock = input;
9719 if(this.hasFeedback && !this.allowBlank){
9723 cls: 'glyphicon form-control-feedback'
9727 cls : 'has-feedback',
9736 if (this.before || this.after) {
9739 cls : 'input-group',
9743 inputblock.cn.push({
9745 cls : 'input-group-addon',
9750 inputblock.cn.push(input);
9752 if(this.hasFeedback && !this.allowBlank){
9753 inputblock.cls += ' has-feedback';
9754 inputblock.cn.push(feedback);
9758 inputblock.cn.push({
9760 cls : 'input-group-addon',
9767 if (align ==='left' && this.fieldLabel.length) {
9772 cls : 'control-label',
9773 html : this.fieldLabel
9784 if(this.labelWidth > 12){
9785 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9788 if(this.labelWidth < 13 && this.labelmd == 0){
9789 this.labelmd = this.labelWidth;
9792 if(this.labellg > 0){
9793 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9794 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9797 if(this.labelmd > 0){
9798 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9799 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9802 if(this.labelsm > 0){
9803 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9804 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9807 if(this.labelxs > 0){
9808 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9809 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9812 } else if ( this.fieldLabel.length) {
9817 //cls : 'input-group-addon',
9818 html : this.fieldLabel
9836 if (this.disabled) {
9837 input.disabled=true;
9844 * return the real textarea element.
9846 inputEl: function ()
9848 return this.el.select('textarea.form-control',true).first();
9852 * Clear any invalid styles/messages for this field
9854 clearInvalid : function()
9857 if(!this.el || this.preventMark){ // not rendered
9861 var label = this.el.select('label', true).first();
9862 var icon = this.el.select('i.fa-star', true).first();
9868 this.el.removeClass(this.invalidClass);
9870 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9872 var feedback = this.el.select('.form-control-feedback', true).first();
9875 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9880 this.fireEvent('valid', this);
9884 * Mark this field as valid
9886 markValid : function()
9888 if(!this.el || this.preventMark){ // not rendered
9892 this.el.removeClass([this.invalidClass, this.validClass]);
9894 var feedback = this.el.select('.form-control-feedback', true).first();
9897 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9900 if(this.disabled || this.allowBlank){
9904 var label = this.el.select('label', true).first();
9905 var icon = this.el.select('i.fa-star', true).first();
9911 this.el.addClass(this.validClass);
9913 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9915 var feedback = this.el.select('.form-control-feedback', true).first();
9918 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9919 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9924 this.fireEvent('valid', this);
9928 * Mark this field as invalid
9929 * @param {String} msg The validation message
9931 markInvalid : function(msg)
9933 if(!this.el || this.preventMark){ // not rendered
9937 this.el.removeClass([this.invalidClass, this.validClass]);
9939 var feedback = this.el.select('.form-control-feedback', true).first();
9942 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9945 if(this.disabled || this.allowBlank){
9949 var label = this.el.select('label', true).first();
9950 var icon = this.el.select('i.fa-star', true).first();
9952 if(!this.getValue().length && label && !icon){
9953 this.el.createChild({
9955 cls : 'text-danger fa fa-lg fa-star',
9956 tooltip : 'This field is required',
9957 style : 'margin-right:5px;'
9961 this.el.addClass(this.invalidClass);
9963 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9965 var feedback = this.el.select('.form-control-feedback', true).first();
9968 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9970 if(this.getValue().length || this.forceFeedback){
9971 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9978 this.fireEvent('invalid', this, msg);
9986 * trigger field - base class for combo..
9991 * @class Roo.bootstrap.TriggerField
9992 * @extends Roo.bootstrap.Input
9993 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9994 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9995 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9996 * for which you can provide a custom implementation. For example:
9998 var trigger = new Roo.bootstrap.TriggerField();
9999 trigger.onTriggerClick = myTriggerFn;
10000 trigger.applyTo('my-field');
10003 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10004 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10005 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
10006 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10007 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10010 * Create a new TriggerField.
10011 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10012 * to the base TextField)
10014 Roo.bootstrap.TriggerField = function(config){
10015 this.mimicing = false;
10016 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10019 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
10021 * @cfg {String} triggerClass A CSS class to apply to the trigger
10024 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10029 * @cfg {Boolean} removable (true|false) special filter default false
10033 /** @cfg {Boolean} grow @hide */
10034 /** @cfg {Number} growMin @hide */
10035 /** @cfg {Number} growMax @hide */
10041 autoSize: Roo.emptyFn,
10045 deferHeight : true,
10048 actionMode : 'wrap',
10053 getAutoCreate : function(){
10055 var align = this.labelAlign || this.parentLabelAlign();
10060 cls: 'form-group' //input-group
10067 type : this.inputType,
10068 cls : 'form-control',
10069 autocomplete: 'new-password',
10070 placeholder : this.placeholder || ''
10074 input.name = this.name;
10077 input.cls += ' input-' + this.size;
10080 if (this.disabled) {
10081 input.disabled=true;
10084 var inputblock = input;
10086 if(this.hasFeedback && !this.allowBlank){
10090 cls: 'glyphicon form-control-feedback'
10093 if(this.removable && !this.editable && !this.tickable){
10095 cls : 'has-feedback',
10101 cls : 'roo-combo-removable-btn close'
10108 cls : 'has-feedback',
10117 if(this.removable && !this.editable && !this.tickable){
10119 cls : 'roo-removable',
10125 cls : 'roo-combo-removable-btn close'
10132 if (this.before || this.after) {
10135 cls : 'input-group',
10139 inputblock.cn.push({
10141 cls : 'input-group-addon',
10146 inputblock.cn.push(input);
10148 if(this.hasFeedback && !this.allowBlank){
10149 inputblock.cls += ' has-feedback';
10150 inputblock.cn.push(feedback);
10154 inputblock.cn.push({
10156 cls : 'input-group-addon',
10169 cls: 'form-hidden-field'
10183 cls: 'form-hidden-field'
10187 cls: 'roo-select2-choices',
10191 cls: 'roo-select2-search-field',
10204 cls: 'roo-select2-container input-group',
10209 // cls: 'typeahead typeahead-long dropdown-menu',
10210 // style: 'display:none'
10215 if(!this.multiple && this.showToggleBtn){
10221 if (this.caret != false) {
10224 cls: 'fa fa-' + this.caret
10231 cls : 'input-group-addon btn dropdown-toggle',
10236 cls: 'combobox-clear',
10250 combobox.cls += ' roo-select2-container-multi';
10253 if (align ==='left' && this.fieldLabel.length) {
10255 cfg.cls += ' roo-form-group-label-left';
10260 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10261 tooltip : 'This field is required'
10266 cls : 'control-label',
10267 html : this.fieldLabel
10279 var labelCfg = cfg.cn[1];
10280 var contentCfg = cfg.cn[2];
10282 if(this.indicatorpos == 'right'){
10287 cls : 'control-label',
10291 html : this.fieldLabel
10295 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10296 tooltip : 'This field is required'
10309 labelCfg = cfg.cn[0];
10310 contentCfg = cfg.cn[1];
10313 if(this.labelWidth > 12){
10314 labelCfg.style = "width: " + this.labelWidth + 'px';
10317 if(this.labelWidth < 13 && this.labelmd == 0){
10318 this.labelmd = this.labelWidth;
10321 if(this.labellg > 0){
10322 labelCfg.cls += ' col-lg-' + this.labellg;
10323 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10326 if(this.labelmd > 0){
10327 labelCfg.cls += ' col-md-' + this.labelmd;
10328 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10331 if(this.labelsm > 0){
10332 labelCfg.cls += ' col-sm-' + this.labelsm;
10333 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10336 if(this.labelxs > 0){
10337 labelCfg.cls += ' col-xs-' + this.labelxs;
10338 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10341 } else if ( this.fieldLabel.length) {
10342 // Roo.log(" label");
10346 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10347 tooltip : 'This field is required'
10351 //cls : 'input-group-addon',
10352 html : this.fieldLabel
10360 if(this.indicatorpos == 'right'){
10368 html : this.fieldLabel
10372 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10373 tooltip : 'This field is required'
10386 // Roo.log(" no label && no align");
10393 ['xs','sm','md','lg'].map(function(size){
10394 if (settings[size]) {
10395 cfg.cls += ' col-' + size + '-' + settings[size];
10406 onResize : function(w, h){
10407 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10408 // if(typeof w == 'number'){
10409 // var x = w - this.trigger.getWidth();
10410 // this.inputEl().setWidth(this.adjustWidth('input', x));
10411 // this.trigger.setStyle('left', x+'px');
10416 adjustSize : Roo.BoxComponent.prototype.adjustSize,
10419 getResizeEl : function(){
10420 return this.inputEl();
10424 getPositionEl : function(){
10425 return this.inputEl();
10429 alignErrorIcon : function(){
10430 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10434 initEvents : function(){
10438 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10439 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10440 if(!this.multiple && this.showToggleBtn){
10441 this.trigger = this.el.select('span.dropdown-toggle',true).first();
10442 if(this.hideTrigger){
10443 this.trigger.setDisplayed(false);
10445 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10449 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10452 if(this.removable && !this.editable && !this.tickable){
10453 var close = this.closeTriggerEl();
10456 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10457 close.on('click', this.removeBtnClick, this, close);
10461 //this.trigger.addClassOnOver('x-form-trigger-over');
10462 //this.trigger.addClassOnClick('x-form-trigger-click');
10465 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10469 closeTriggerEl : function()
10471 var close = this.el.select('.roo-combo-removable-btn', true).first();
10472 return close ? close : false;
10475 removeBtnClick : function(e, h, el)
10477 e.preventDefault();
10479 if(this.fireEvent("remove", this) !== false){
10481 this.fireEvent("afterremove", this)
10485 createList : function()
10487 this.list = Roo.get(document.body).createChild({
10489 cls: 'typeahead typeahead-long dropdown-menu',
10490 style: 'display:none'
10493 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10498 initTrigger : function(){
10503 onDestroy : function(){
10505 this.trigger.removeAllListeners();
10506 // this.trigger.remove();
10509 // this.wrap.remove();
10511 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10515 onFocus : function(){
10516 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10518 if(!this.mimicing){
10519 this.wrap.addClass('x-trigger-wrap-focus');
10520 this.mimicing = true;
10521 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10522 if(this.monitorTab){
10523 this.el.on("keydown", this.checkTab, this);
10530 checkTab : function(e){
10531 if(e.getKey() == e.TAB){
10532 this.triggerBlur();
10537 onBlur : function(){
10542 mimicBlur : function(e, t){
10544 if(!this.wrap.contains(t) && this.validateBlur()){
10545 this.triggerBlur();
10551 triggerBlur : function(){
10552 this.mimicing = false;
10553 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10554 if(this.monitorTab){
10555 this.el.un("keydown", this.checkTab, this);
10557 //this.wrap.removeClass('x-trigger-wrap-focus');
10558 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10562 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10563 validateBlur : function(e, t){
10568 onDisable : function(){
10569 this.inputEl().dom.disabled = true;
10570 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10572 // this.wrap.addClass('x-item-disabled');
10577 onEnable : function(){
10578 this.inputEl().dom.disabled = false;
10579 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10581 // this.el.removeClass('x-item-disabled');
10586 onShow : function(){
10587 var ae = this.getActionEl();
10590 ae.dom.style.display = '';
10591 ae.dom.style.visibility = 'visible';
10597 onHide : function(){
10598 var ae = this.getActionEl();
10599 ae.dom.style.display = 'none';
10603 * The function that should handle the trigger's click event. This method does nothing by default until overridden
10604 * by an implementing function.
10606 * @param {EventObject} e
10608 onTriggerClick : Roo.emptyFn
10612 * Ext JS Library 1.1.1
10613 * Copyright(c) 2006-2007, Ext JS, LLC.
10615 * Originally Released Under LGPL - original licence link has changed is not relivant.
10618 * <script type="text/javascript">
10623 * @class Roo.data.SortTypes
10625 * Defines the default sorting (casting?) comparison functions used when sorting data.
10627 Roo.data.SortTypes = {
10629 * Default sort that does nothing
10630 * @param {Mixed} s The value being converted
10631 * @return {Mixed} The comparison value
10633 none : function(s){
10638 * The regular expression used to strip tags
10642 stripTagsRE : /<\/?[^>]+>/gi,
10645 * Strips all HTML tags to sort on text only
10646 * @param {Mixed} s The value being converted
10647 * @return {String} The comparison value
10649 asText : function(s){
10650 return String(s).replace(this.stripTagsRE, "");
10654 * Strips all HTML tags to sort on text only - Case insensitive
10655 * @param {Mixed} s The value being converted
10656 * @return {String} The comparison value
10658 asUCText : function(s){
10659 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10663 * Case insensitive string
10664 * @param {Mixed} s The value being converted
10665 * @return {String} The comparison value
10667 asUCString : function(s) {
10668 return String(s).toUpperCase();
10673 * @param {Mixed} s The value being converted
10674 * @return {Number} The comparison value
10676 asDate : function(s) {
10680 if(s instanceof Date){
10681 return s.getTime();
10683 return Date.parse(String(s));
10688 * @param {Mixed} s The value being converted
10689 * @return {Float} The comparison value
10691 asFloat : function(s) {
10692 var val = parseFloat(String(s).replace(/,/g, ""));
10701 * @param {Mixed} s The value being converted
10702 * @return {Number} The comparison value
10704 asInt : function(s) {
10705 var val = parseInt(String(s).replace(/,/g, ""));
10713 * Ext JS Library 1.1.1
10714 * Copyright(c) 2006-2007, Ext JS, LLC.
10716 * Originally Released Under LGPL - original licence link has changed is not relivant.
10719 * <script type="text/javascript">
10723 * @class Roo.data.Record
10724 * Instances of this class encapsulate both record <em>definition</em> information, and record
10725 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10726 * to access Records cached in an {@link Roo.data.Store} object.<br>
10728 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10729 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10732 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10734 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10735 * {@link #create}. The parameters are the same.
10736 * @param {Array} data An associative Array of data values keyed by the field name.
10737 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10738 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10739 * not specified an integer id is generated.
10741 Roo.data.Record = function(data, id){
10742 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10747 * Generate a constructor for a specific record layout.
10748 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10749 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10750 * Each field definition object may contain the following properties: <ul>
10751 * <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,
10752 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10753 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10754 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10755 * is being used, then this is a string containing the javascript expression to reference the data relative to
10756 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10757 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10758 * this may be omitted.</p></li>
10759 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10760 * <ul><li>auto (Default, implies no conversion)</li>
10765 * <li>date</li></ul></p></li>
10766 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10767 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10768 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10769 * by the Reader into an object that will be stored in the Record. It is passed the
10770 * following parameters:<ul>
10771 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10773 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10775 * <br>usage:<br><pre><code>
10776 var TopicRecord = Roo.data.Record.create(
10777 {name: 'title', mapping: 'topic_title'},
10778 {name: 'author', mapping: 'username'},
10779 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10780 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10781 {name: 'lastPoster', mapping: 'user2'},
10782 {name: 'excerpt', mapping: 'post_text'}
10785 var myNewRecord = new TopicRecord({
10786 title: 'Do my job please',
10789 lastPost: new Date(),
10790 lastPoster: 'Animal',
10791 excerpt: 'No way dude!'
10793 myStore.add(myNewRecord);
10798 Roo.data.Record.create = function(o){
10799 var f = function(){
10800 f.superclass.constructor.apply(this, arguments);
10802 Roo.extend(f, Roo.data.Record);
10803 var p = f.prototype;
10804 p.fields = new Roo.util.MixedCollection(false, function(field){
10807 for(var i = 0, len = o.length; i < len; i++){
10808 p.fields.add(new Roo.data.Field(o[i]));
10810 f.getField = function(name){
10811 return p.fields.get(name);
10816 Roo.data.Record.AUTO_ID = 1000;
10817 Roo.data.Record.EDIT = 'edit';
10818 Roo.data.Record.REJECT = 'reject';
10819 Roo.data.Record.COMMIT = 'commit';
10821 Roo.data.Record.prototype = {
10823 * Readonly flag - true if this record has been modified.
10832 join : function(store){
10833 this.store = store;
10837 * Set the named field to the specified value.
10838 * @param {String} name The name of the field to set.
10839 * @param {Object} value The value to set the field to.
10841 set : function(name, value){
10842 if(this.data[name] == value){
10846 if(!this.modified){
10847 this.modified = {};
10849 if(typeof this.modified[name] == 'undefined'){
10850 this.modified[name] = this.data[name];
10852 this.data[name] = value;
10853 if(!this.editing && this.store){
10854 this.store.afterEdit(this);
10859 * Get the value of the named field.
10860 * @param {String} name The name of the field to get the value of.
10861 * @return {Object} The value of the field.
10863 get : function(name){
10864 return this.data[name];
10868 beginEdit : function(){
10869 this.editing = true;
10870 this.modified = {};
10874 cancelEdit : function(){
10875 this.editing = false;
10876 delete this.modified;
10880 endEdit : function(){
10881 this.editing = false;
10882 if(this.dirty && this.store){
10883 this.store.afterEdit(this);
10888 * Usually called by the {@link Roo.data.Store} which owns the Record.
10889 * Rejects all changes made to the Record since either creation, or the last commit operation.
10890 * Modified fields are reverted to their original values.
10892 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10893 * of reject operations.
10895 reject : function(){
10896 var m = this.modified;
10898 if(typeof m[n] != "function"){
10899 this.data[n] = m[n];
10902 this.dirty = false;
10903 delete this.modified;
10904 this.editing = false;
10906 this.store.afterReject(this);
10911 * Usually called by the {@link Roo.data.Store} which owns the Record.
10912 * Commits all changes made to the Record since either creation, or the last commit operation.
10914 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10915 * of commit operations.
10917 commit : function(){
10918 this.dirty = false;
10919 delete this.modified;
10920 this.editing = false;
10922 this.store.afterCommit(this);
10927 hasError : function(){
10928 return this.error != null;
10932 clearError : function(){
10937 * Creates a copy of this record.
10938 * @param {String} id (optional) A new record id if you don't want to use this record's id
10941 copy : function(newId) {
10942 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10946 * Ext JS Library 1.1.1
10947 * Copyright(c) 2006-2007, Ext JS, LLC.
10949 * Originally Released Under LGPL - original licence link has changed is not relivant.
10952 * <script type="text/javascript">
10958 * @class Roo.data.Store
10959 * @extends Roo.util.Observable
10960 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10961 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10963 * 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
10964 * has no knowledge of the format of the data returned by the Proxy.<br>
10966 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10967 * instances from the data object. These records are cached and made available through accessor functions.
10969 * Creates a new Store.
10970 * @param {Object} config A config object containing the objects needed for the Store to access data,
10971 * and read the data into Records.
10973 Roo.data.Store = function(config){
10974 this.data = new Roo.util.MixedCollection(false);
10975 this.data.getKey = function(o){
10978 this.baseParams = {};
10980 this.paramNames = {
10985 "multisort" : "_multisort"
10988 if(config && config.data){
10989 this.inlineData = config.data;
10990 delete config.data;
10993 Roo.apply(this, config);
10995 if(this.reader){ // reader passed
10996 this.reader = Roo.factory(this.reader, Roo.data);
10997 this.reader.xmodule = this.xmodule || false;
10998 if(!this.recordType){
10999 this.recordType = this.reader.recordType;
11001 if(this.reader.onMetaChange){
11002 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11006 if(this.recordType){
11007 this.fields = this.recordType.prototype.fields;
11009 this.modified = [];
11013 * @event datachanged
11014 * Fires when the data cache has changed, and a widget which is using this Store
11015 * as a Record cache should refresh its view.
11016 * @param {Store} this
11018 datachanged : true,
11020 * @event metachange
11021 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11022 * @param {Store} this
11023 * @param {Object} meta The JSON metadata
11028 * Fires when Records have been added to the Store
11029 * @param {Store} this
11030 * @param {Roo.data.Record[]} records The array of Records added
11031 * @param {Number} index The index at which the record(s) were added
11036 * Fires when a Record has been removed from the Store
11037 * @param {Store} this
11038 * @param {Roo.data.Record} record The Record that was removed
11039 * @param {Number} index The index at which the record was removed
11044 * Fires when a Record has been updated
11045 * @param {Store} this
11046 * @param {Roo.data.Record} record The Record that was updated
11047 * @param {String} operation The update operation being performed. Value may be one of:
11049 Roo.data.Record.EDIT
11050 Roo.data.Record.REJECT
11051 Roo.data.Record.COMMIT
11057 * Fires when the data cache has been cleared.
11058 * @param {Store} this
11062 * @event beforeload
11063 * Fires before a request is made for a new data object. If the beforeload handler returns false
11064 * the load action will be canceled.
11065 * @param {Store} this
11066 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11070 * @event beforeloadadd
11071 * Fires after a new set of Records has been loaded.
11072 * @param {Store} this
11073 * @param {Roo.data.Record[]} records The Records that were loaded
11074 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11076 beforeloadadd : true,
11079 * Fires after a new set of Records has been loaded, before they are added to the store.
11080 * @param {Store} this
11081 * @param {Roo.data.Record[]} records The Records that were loaded
11082 * @param {Object} options The loading options that were specified (see {@link #load} for details)
11083 * @params {Object} return from reader
11087 * @event loadexception
11088 * Fires if an exception occurs in the Proxy during loading.
11089 * Called with the signature of the Proxy's "loadexception" event.
11090 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11093 * @param {Object} return from JsonData.reader() - success, totalRecords, records
11094 * @param {Object} load options
11095 * @param {Object} jsonData from your request (normally this contains the Exception)
11097 loadexception : true
11101 this.proxy = Roo.factory(this.proxy, Roo.data);
11102 this.proxy.xmodule = this.xmodule || false;
11103 this.relayEvents(this.proxy, ["loadexception"]);
11105 this.sortToggle = {};
11106 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11108 Roo.data.Store.superclass.constructor.call(this);
11110 if(this.inlineData){
11111 this.loadData(this.inlineData);
11112 delete this.inlineData;
11116 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11118 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
11119 * without a remote query - used by combo/forms at present.
11123 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11126 * @cfg {Array} data Inline data to be loaded when the store is initialized.
11129 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11130 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11133 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11134 * on any HTTP request
11137 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11140 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11144 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11145 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11147 remoteSort : false,
11150 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11151 * loaded or when a record is removed. (defaults to false).
11153 pruneModifiedRecords : false,
11156 lastOptions : null,
11159 * Add Records to the Store and fires the add event.
11160 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11162 add : function(records){
11163 records = [].concat(records);
11164 for(var i = 0, len = records.length; i < len; i++){
11165 records[i].join(this);
11167 var index = this.data.length;
11168 this.data.addAll(records);
11169 this.fireEvent("add", this, records, index);
11173 * Remove a Record from the Store and fires the remove event.
11174 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11176 remove : function(record){
11177 var index = this.data.indexOf(record);
11178 this.data.removeAt(index);
11180 if(this.pruneModifiedRecords){
11181 this.modified.remove(record);
11183 this.fireEvent("remove", this, record, index);
11187 * Remove all Records from the Store and fires the clear event.
11189 removeAll : function(){
11191 if(this.pruneModifiedRecords){
11192 this.modified = [];
11194 this.fireEvent("clear", this);
11198 * Inserts Records to the Store at the given index and fires the add event.
11199 * @param {Number} index The start index at which to insert the passed Records.
11200 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11202 insert : function(index, records){
11203 records = [].concat(records);
11204 for(var i = 0, len = records.length; i < len; i++){
11205 this.data.insert(index, records[i]);
11206 records[i].join(this);
11208 this.fireEvent("add", this, records, index);
11212 * Get the index within the cache of the passed Record.
11213 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11214 * @return {Number} The index of the passed Record. Returns -1 if not found.
11216 indexOf : function(record){
11217 return this.data.indexOf(record);
11221 * Get the index within the cache of the Record with the passed id.
11222 * @param {String} id The id of the Record to find.
11223 * @return {Number} The index of the Record. Returns -1 if not found.
11225 indexOfId : function(id){
11226 return this.data.indexOfKey(id);
11230 * Get the Record with the specified id.
11231 * @param {String} id The id of the Record to find.
11232 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11234 getById : function(id){
11235 return this.data.key(id);
11239 * Get the Record at the specified index.
11240 * @param {Number} index The index of the Record to find.
11241 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11243 getAt : function(index){
11244 return this.data.itemAt(index);
11248 * Returns a range of Records between specified indices.
11249 * @param {Number} startIndex (optional) The starting index (defaults to 0)
11250 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11251 * @return {Roo.data.Record[]} An array of Records
11253 getRange : function(start, end){
11254 return this.data.getRange(start, end);
11258 storeOptions : function(o){
11259 o = Roo.apply({}, o);
11262 this.lastOptions = o;
11266 * Loads the Record cache from the configured Proxy using the configured Reader.
11268 * If using remote paging, then the first load call must specify the <em>start</em>
11269 * and <em>limit</em> properties in the options.params property to establish the initial
11270 * position within the dataset, and the number of Records to cache on each read from the Proxy.
11272 * <strong>It is important to note that for remote data sources, loading is asynchronous,
11273 * and this call will return before the new data has been loaded. Perform any post-processing
11274 * in a callback function, or in a "load" event handler.</strong>
11276 * @param {Object} options An object containing properties which control loading options:<ul>
11277 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11278 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11279 * passed the following arguments:<ul>
11280 * <li>r : Roo.data.Record[]</li>
11281 * <li>options: Options object from the load call</li>
11282 * <li>success: Boolean success indicator</li></ul></li>
11283 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11284 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11287 load : function(options){
11288 options = options || {};
11289 if(this.fireEvent("beforeload", this, options) !== false){
11290 this.storeOptions(options);
11291 var p = Roo.apply(options.params || {}, this.baseParams);
11292 // if meta was not loaded from remote source.. try requesting it.
11293 if (!this.reader.metaFromRemote) {
11294 p._requestMeta = 1;
11296 if(this.sortInfo && this.remoteSort){
11297 var pn = this.paramNames;
11298 p[pn["sort"]] = this.sortInfo.field;
11299 p[pn["dir"]] = this.sortInfo.direction;
11301 if (this.multiSort) {
11302 var pn = this.paramNames;
11303 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11306 this.proxy.load(p, this.reader, this.loadRecords, this, options);
11311 * Reloads the Record cache from the configured Proxy using the configured Reader and
11312 * the options from the last load operation performed.
11313 * @param {Object} options (optional) An object containing properties which may override the options
11314 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11315 * the most recently used options are reused).
11317 reload : function(options){
11318 this.load(Roo.applyIf(options||{}, this.lastOptions));
11322 // Called as a callback by the Reader during a load operation.
11323 loadRecords : function(o, options, success){
11324 if(!o || success === false){
11325 if(success !== false){
11326 this.fireEvent("load", this, [], options, o);
11328 if(options.callback){
11329 options.callback.call(options.scope || this, [], options, false);
11333 // if data returned failure - throw an exception.
11334 if (o.success === false) {
11335 // show a message if no listener is registered.
11336 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11337 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11339 // loadmask wil be hooked into this..
11340 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11343 var r = o.records, t = o.totalRecords || r.length;
11345 this.fireEvent("beforeloadadd", this, r, options, o);
11347 if(!options || options.add !== true){
11348 if(this.pruneModifiedRecords){
11349 this.modified = [];
11351 for(var i = 0, len = r.length; i < len; i++){
11355 this.data = this.snapshot;
11356 delete this.snapshot;
11359 this.data.addAll(r);
11360 this.totalLength = t;
11362 this.fireEvent("datachanged", this);
11364 this.totalLength = Math.max(t, this.data.length+r.length);
11368 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11370 var e = new Roo.data.Record({});
11372 e.set(this.parent.displayField, this.parent.emptyTitle);
11373 e.set(this.parent.valueField, '');
11378 this.fireEvent("load", this, r, options, o);
11379 if(options.callback){
11380 options.callback.call(options.scope || this, r, options, true);
11386 * Loads data from a passed data block. A Reader which understands the format of the data
11387 * must have been configured in the constructor.
11388 * @param {Object} data The data block from which to read the Records. The format of the data expected
11389 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11390 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11392 loadData : function(o, append){
11393 var r = this.reader.readRecords(o);
11394 this.loadRecords(r, {add: append}, true);
11398 * Gets the number of cached records.
11400 * <em>If using paging, this may not be the total size of the dataset. If the data object
11401 * used by the Reader contains the dataset size, then the getTotalCount() function returns
11402 * the data set size</em>
11404 getCount : function(){
11405 return this.data.length || 0;
11409 * Gets the total number of records in the dataset as returned by the server.
11411 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11412 * the dataset size</em>
11414 getTotalCount : function(){
11415 return this.totalLength || 0;
11419 * Returns the sort state of the Store as an object with two properties:
11421 field {String} The name of the field by which the Records are sorted
11422 direction {String} The sort order, "ASC" or "DESC"
11425 getSortState : function(){
11426 return this.sortInfo;
11430 applySort : function(){
11431 if(this.sortInfo && !this.remoteSort){
11432 var s = this.sortInfo, f = s.field;
11433 var st = this.fields.get(f).sortType;
11434 var fn = function(r1, r2){
11435 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11436 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11438 this.data.sort(s.direction, fn);
11439 if(this.snapshot && this.snapshot != this.data){
11440 this.snapshot.sort(s.direction, fn);
11446 * Sets the default sort column and order to be used by the next load operation.
11447 * @param {String} fieldName The name of the field to sort by.
11448 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11450 setDefaultSort : function(field, dir){
11451 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11455 * Sort the Records.
11456 * If remote sorting is used, the sort is performed on the server, and the cache is
11457 * reloaded. If local sorting is used, the cache is sorted internally.
11458 * @param {String} fieldName The name of the field to sort by.
11459 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11461 sort : function(fieldName, dir){
11462 var f = this.fields.get(fieldName);
11464 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11466 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11467 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11472 this.sortToggle[f.name] = dir;
11473 this.sortInfo = {field: f.name, direction: dir};
11474 if(!this.remoteSort){
11476 this.fireEvent("datachanged", this);
11478 this.load(this.lastOptions);
11483 * Calls the specified function for each of the Records in the cache.
11484 * @param {Function} fn The function to call. The Record is passed as the first parameter.
11485 * Returning <em>false</em> aborts and exits the iteration.
11486 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11488 each : function(fn, scope){
11489 this.data.each(fn, scope);
11493 * Gets all records modified since the last commit. Modified records are persisted across load operations
11494 * (e.g., during paging).
11495 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11497 getModifiedRecords : function(){
11498 return this.modified;
11502 createFilterFn : function(property, value, anyMatch){
11503 if(!value.exec){ // not a regex
11504 value = String(value);
11505 if(value.length == 0){
11508 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11510 return function(r){
11511 return value.test(r.data[property]);
11516 * Sums the value of <i>property</i> for each record between start and end and returns the result.
11517 * @param {String} property A field on your records
11518 * @param {Number} start The record index to start at (defaults to 0)
11519 * @param {Number} end The last record index to include (defaults to length - 1)
11520 * @return {Number} The sum
11522 sum : function(property, start, end){
11523 var rs = this.data.items, v = 0;
11524 start = start || 0;
11525 end = (end || end === 0) ? end : rs.length-1;
11527 for(var i = start; i <= end; i++){
11528 v += (rs[i].data[property] || 0);
11534 * Filter the records by a specified property.
11535 * @param {String} field A field on your records
11536 * @param {String/RegExp} value Either a string that the field
11537 * should start with or a RegExp to test against the field
11538 * @param {Boolean} anyMatch True to match any part not just the beginning
11540 filter : function(property, value, anyMatch){
11541 var fn = this.createFilterFn(property, value, anyMatch);
11542 return fn ? this.filterBy(fn) : this.clearFilter();
11546 * Filter by a function. The specified function will be called with each
11547 * record in this data source. If the function returns true the record is included,
11548 * otherwise it is filtered.
11549 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11550 * @param {Object} scope (optional) The scope of the function (defaults to this)
11552 filterBy : function(fn, scope){
11553 this.snapshot = this.snapshot || this.data;
11554 this.data = this.queryBy(fn, scope||this);
11555 this.fireEvent("datachanged", this);
11559 * Query the records by a specified property.
11560 * @param {String} field A field on your records
11561 * @param {String/RegExp} value Either a string that the field
11562 * should start with or a RegExp to test against the field
11563 * @param {Boolean} anyMatch True to match any part not just the beginning
11564 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11566 query : function(property, value, anyMatch){
11567 var fn = this.createFilterFn(property, value, anyMatch);
11568 return fn ? this.queryBy(fn) : this.data.clone();
11572 * Query by a function. The specified function will be called with each
11573 * record in this data source. If the function returns true the record is included
11575 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11576 * @param {Object} scope (optional) The scope of the function (defaults to this)
11577 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11579 queryBy : function(fn, scope){
11580 var data = this.snapshot || this.data;
11581 return data.filterBy(fn, scope||this);
11585 * Collects unique values for a particular dataIndex from this store.
11586 * @param {String} dataIndex The property to collect
11587 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11588 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11589 * @return {Array} An array of the unique values
11591 collect : function(dataIndex, allowNull, bypassFilter){
11592 var d = (bypassFilter === true && this.snapshot) ?
11593 this.snapshot.items : this.data.items;
11594 var v, sv, r = [], l = {};
11595 for(var i = 0, len = d.length; i < len; i++){
11596 v = d[i].data[dataIndex];
11598 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11607 * Revert to a view of the Record cache with no filtering applied.
11608 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11610 clearFilter : function(suppressEvent){
11611 if(this.snapshot && this.snapshot != this.data){
11612 this.data = this.snapshot;
11613 delete this.snapshot;
11614 if(suppressEvent !== true){
11615 this.fireEvent("datachanged", this);
11621 afterEdit : function(record){
11622 if(this.modified.indexOf(record) == -1){
11623 this.modified.push(record);
11625 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11629 afterReject : function(record){
11630 this.modified.remove(record);
11631 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11635 afterCommit : function(record){
11636 this.modified.remove(record);
11637 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11641 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11642 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11644 commitChanges : function(){
11645 var m = this.modified.slice(0);
11646 this.modified = [];
11647 for(var i = 0, len = m.length; i < len; i++){
11653 * Cancel outstanding changes on all changed records.
11655 rejectChanges : function(){
11656 var m = this.modified.slice(0);
11657 this.modified = [];
11658 for(var i = 0, len = m.length; i < len; i++){
11663 onMetaChange : function(meta, rtype, o){
11664 this.recordType = rtype;
11665 this.fields = rtype.prototype.fields;
11666 delete this.snapshot;
11667 this.sortInfo = meta.sortInfo || this.sortInfo;
11668 this.modified = [];
11669 this.fireEvent('metachange', this, this.reader.meta);
11672 moveIndex : function(data, type)
11674 var index = this.indexOf(data);
11676 var newIndex = index + type;
11680 this.insert(newIndex, data);
11685 * Ext JS Library 1.1.1
11686 * Copyright(c) 2006-2007, Ext JS, LLC.
11688 * Originally Released Under LGPL - original licence link has changed is not relivant.
11691 * <script type="text/javascript">
11695 * @class Roo.data.SimpleStore
11696 * @extends Roo.data.Store
11697 * Small helper class to make creating Stores from Array data easier.
11698 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11699 * @cfg {Array} fields An array of field definition objects, or field name strings.
11700 * @cfg {Array} data The multi-dimensional array of data
11702 * @param {Object} config
11704 Roo.data.SimpleStore = function(config){
11705 Roo.data.SimpleStore.superclass.constructor.call(this, {
11707 reader: new Roo.data.ArrayReader({
11710 Roo.data.Record.create(config.fields)
11712 proxy : new Roo.data.MemoryProxy(config.data)
11716 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11718 * Ext JS Library 1.1.1
11719 * Copyright(c) 2006-2007, Ext JS, LLC.
11721 * Originally Released Under LGPL - original licence link has changed is not relivant.
11724 * <script type="text/javascript">
11729 * @extends Roo.data.Store
11730 * @class Roo.data.JsonStore
11731 * Small helper class to make creating Stores for JSON data easier. <br/>
11733 var store = new Roo.data.JsonStore({
11734 url: 'get-images.php',
11736 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11739 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11740 * JsonReader and HttpProxy (unless inline data is provided).</b>
11741 * @cfg {Array} fields An array of field definition objects, or field name strings.
11743 * @param {Object} config
11745 Roo.data.JsonStore = function(c){
11746 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11747 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11748 reader: new Roo.data.JsonReader(c, c.fields)
11751 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11753 * Ext JS Library 1.1.1
11754 * Copyright(c) 2006-2007, Ext JS, LLC.
11756 * Originally Released Under LGPL - original licence link has changed is not relivant.
11759 * <script type="text/javascript">
11763 Roo.data.Field = function(config){
11764 if(typeof config == "string"){
11765 config = {name: config};
11767 Roo.apply(this, config);
11770 this.type = "auto";
11773 var st = Roo.data.SortTypes;
11774 // named sortTypes are supported, here we look them up
11775 if(typeof this.sortType == "string"){
11776 this.sortType = st[this.sortType];
11779 // set default sortType for strings and dates
11780 if(!this.sortType){
11783 this.sortType = st.asUCString;
11786 this.sortType = st.asDate;
11789 this.sortType = st.none;
11794 var stripRe = /[\$,%]/g;
11796 // prebuilt conversion function for this field, instead of
11797 // switching every time we're reading a value
11799 var cv, dateFormat = this.dateFormat;
11804 cv = function(v){ return v; };
11807 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11811 return v !== undefined && v !== null && v !== '' ?
11812 parseInt(String(v).replace(stripRe, ""), 10) : '';
11817 return v !== undefined && v !== null && v !== '' ?
11818 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11823 cv = function(v){ return v === true || v === "true" || v == 1; };
11830 if(v instanceof Date){
11834 if(dateFormat == "timestamp"){
11835 return new Date(v*1000);
11837 return Date.parseDate(v, dateFormat);
11839 var parsed = Date.parse(v);
11840 return parsed ? new Date(parsed) : null;
11849 Roo.data.Field.prototype = {
11857 * Ext JS Library 1.1.1
11858 * Copyright(c) 2006-2007, Ext JS, LLC.
11860 * Originally Released Under LGPL - original licence link has changed is not relivant.
11863 * <script type="text/javascript">
11866 // Base class for reading structured data from a data source. This class is intended to be
11867 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11870 * @class Roo.data.DataReader
11871 * Base class for reading structured data from a data source. This class is intended to be
11872 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11875 Roo.data.DataReader = function(meta, recordType){
11879 this.recordType = recordType instanceof Array ?
11880 Roo.data.Record.create(recordType) : recordType;
11883 Roo.data.DataReader.prototype = {
11885 * Create an empty record
11886 * @param {Object} data (optional) - overlay some values
11887 * @return {Roo.data.Record} record created.
11889 newRow : function(d) {
11891 this.recordType.prototype.fields.each(function(c) {
11893 case 'int' : da[c.name] = 0; break;
11894 case 'date' : da[c.name] = new Date(); break;
11895 case 'float' : da[c.name] = 0.0; break;
11896 case 'boolean' : da[c.name] = false; break;
11897 default : da[c.name] = ""; break;
11901 return new this.recordType(Roo.apply(da, d));
11906 * Ext JS Library 1.1.1
11907 * Copyright(c) 2006-2007, Ext JS, LLC.
11909 * Originally Released Under LGPL - original licence link has changed is not relivant.
11912 * <script type="text/javascript">
11916 * @class Roo.data.DataProxy
11917 * @extends Roo.data.Observable
11918 * This class is an abstract base class for implementations which provide retrieval of
11919 * unformatted data objects.<br>
11921 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11922 * (of the appropriate type which knows how to parse the data object) to provide a block of
11923 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11925 * Custom implementations must implement the load method as described in
11926 * {@link Roo.data.HttpProxy#load}.
11928 Roo.data.DataProxy = function(){
11931 * @event beforeload
11932 * Fires before a network request is made to retrieve a data object.
11933 * @param {Object} This DataProxy object.
11934 * @param {Object} params The params parameter to the load function.
11939 * Fires before the load method's callback is called.
11940 * @param {Object} This DataProxy object.
11941 * @param {Object} o The data object.
11942 * @param {Object} arg The callback argument object passed to the load function.
11946 * @event loadexception
11947 * Fires if an Exception occurs during data retrieval.
11948 * @param {Object} This DataProxy object.
11949 * @param {Object} o The data object.
11950 * @param {Object} arg The callback argument object passed to the load function.
11951 * @param {Object} e The Exception.
11953 loadexception : true
11955 Roo.data.DataProxy.superclass.constructor.call(this);
11958 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11961 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11965 * Ext JS Library 1.1.1
11966 * Copyright(c) 2006-2007, Ext JS, LLC.
11968 * Originally Released Under LGPL - original licence link has changed is not relivant.
11971 * <script type="text/javascript">
11974 * @class Roo.data.MemoryProxy
11975 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11976 * to the Reader when its load method is called.
11978 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11980 Roo.data.MemoryProxy = function(data){
11984 Roo.data.MemoryProxy.superclass.constructor.call(this);
11988 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11991 * Load data from the requested source (in this case an in-memory
11992 * data object passed to the constructor), read the data object into
11993 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11994 * process that block using the passed callback.
11995 * @param {Object} params This parameter is not used by the MemoryProxy class.
11996 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11997 * object into a block of Roo.data.Records.
11998 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11999 * The function must be passed <ul>
12000 * <li>The Record block object</li>
12001 * <li>The "arg" argument from the load function</li>
12002 * <li>A boolean success indicator</li>
12004 * @param {Object} scope The scope in which to call the callback
12005 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12007 load : function(params, reader, callback, scope, arg){
12008 params = params || {};
12011 result = reader.readRecords(this.data);
12013 this.fireEvent("loadexception", this, arg, null, e);
12014 callback.call(scope, null, arg, false);
12017 callback.call(scope, result, arg, true);
12021 update : function(params, records){
12026 * Ext JS Library 1.1.1
12027 * Copyright(c) 2006-2007, Ext JS, LLC.
12029 * Originally Released Under LGPL - original licence link has changed is not relivant.
12032 * <script type="text/javascript">
12035 * @class Roo.data.HttpProxy
12036 * @extends Roo.data.DataProxy
12037 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12038 * configured to reference a certain URL.<br><br>
12040 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12041 * from which the running page was served.<br><br>
12043 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12045 * Be aware that to enable the browser to parse an XML document, the server must set
12046 * the Content-Type header in the HTTP response to "text/xml".
12048 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12049 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
12050 * will be used to make the request.
12052 Roo.data.HttpProxy = function(conn){
12053 Roo.data.HttpProxy.superclass.constructor.call(this);
12054 // is conn a conn config or a real conn?
12056 this.useAjax = !conn || !conn.events;
12060 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12061 // thse are take from connection...
12064 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12067 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12068 * extra parameters to each request made by this object. (defaults to undefined)
12071 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12072 * to each request made by this object. (defaults to undefined)
12075 * @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)
12078 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12081 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12087 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12091 * Return the {@link Roo.data.Connection} object being used by this Proxy.
12092 * @return {Connection} The Connection object. This object may be used to subscribe to events on
12093 * a finer-grained basis than the DataProxy events.
12095 getConnection : function(){
12096 return this.useAjax ? Roo.Ajax : this.conn;
12100 * Load data from the configured {@link Roo.data.Connection}, read the data object into
12101 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12102 * process that block using the passed callback.
12103 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12104 * for the request to the remote server.
12105 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12106 * object into a block of Roo.data.Records.
12107 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12108 * The function must be passed <ul>
12109 * <li>The Record block object</li>
12110 * <li>The "arg" argument from the load function</li>
12111 * <li>A boolean success indicator</li>
12113 * @param {Object} scope The scope in which to call the callback
12114 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12116 load : function(params, reader, callback, scope, arg){
12117 if(this.fireEvent("beforeload", this, params) !== false){
12119 params : params || {},
12121 callback : callback,
12126 callback : this.loadResponse,
12130 Roo.applyIf(o, this.conn);
12131 if(this.activeRequest){
12132 Roo.Ajax.abort(this.activeRequest);
12134 this.activeRequest = Roo.Ajax.request(o);
12136 this.conn.request(o);
12139 callback.call(scope||this, null, arg, false);
12144 loadResponse : function(o, success, response){
12145 delete this.activeRequest;
12147 this.fireEvent("loadexception", this, o, response);
12148 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12153 result = o.reader.read(response);
12155 this.fireEvent("loadexception", this, o, response, e);
12156 o.request.callback.call(o.request.scope, null, o.request.arg, false);
12160 this.fireEvent("load", this, o, o.request.arg);
12161 o.request.callback.call(o.request.scope, result, o.request.arg, true);
12165 update : function(dataSet){
12170 updateResponse : function(dataSet){
12175 * Ext JS Library 1.1.1
12176 * Copyright(c) 2006-2007, Ext JS, LLC.
12178 * Originally Released Under LGPL - original licence link has changed is not relivant.
12181 * <script type="text/javascript">
12185 * @class Roo.data.ScriptTagProxy
12186 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12187 * other than the originating domain of the running page.<br><br>
12189 * <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
12190 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12192 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12193 * source code that is used as the source inside a <script> tag.<br><br>
12195 * In order for the browser to process the returned data, the server must wrap the data object
12196 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12197 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12198 * depending on whether the callback name was passed:
12201 boolean scriptTag = false;
12202 String cb = request.getParameter("callback");
12205 response.setContentType("text/javascript");
12207 response.setContentType("application/x-json");
12209 Writer out = response.getWriter();
12211 out.write(cb + "(");
12213 out.print(dataBlock.toJsonString());
12220 * @param {Object} config A configuration object.
12222 Roo.data.ScriptTagProxy = function(config){
12223 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12224 Roo.apply(this, config);
12225 this.head = document.getElementsByTagName("head")[0];
12228 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12230 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12232 * @cfg {String} url The URL from which to request the data object.
12235 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12239 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12240 * the server the name of the callback function set up by the load call to process the returned data object.
12241 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12242 * javascript output which calls this named function passing the data object as its only parameter.
12244 callbackParam : "callback",
12246 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12247 * name to the request.
12252 * Load data from the configured URL, read the data object into
12253 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12254 * process that block using the passed callback.
12255 * @param {Object} params An object containing properties which are to be used as HTTP parameters
12256 * for the request to the remote server.
12257 * @param {Roo.data.DataReader} reader The Reader object which converts the data
12258 * object into a block of Roo.data.Records.
12259 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12260 * The function must be passed <ul>
12261 * <li>The Record block object</li>
12262 * <li>The "arg" argument from the load function</li>
12263 * <li>A boolean success indicator</li>
12265 * @param {Object} scope The scope in which to call the callback
12266 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12268 load : function(params, reader, callback, scope, arg){
12269 if(this.fireEvent("beforeload", this, params) !== false){
12271 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12273 var url = this.url;
12274 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12276 url += "&_dc=" + (new Date().getTime());
12278 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12281 cb : "stcCallback"+transId,
12282 scriptId : "stcScript"+transId,
12286 callback : callback,
12292 window[trans.cb] = function(o){
12293 conn.handleResponse(o, trans);
12296 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12298 if(this.autoAbort !== false){
12302 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12304 var script = document.createElement("script");
12305 script.setAttribute("src", url);
12306 script.setAttribute("type", "text/javascript");
12307 script.setAttribute("id", trans.scriptId);
12308 this.head.appendChild(script);
12310 this.trans = trans;
12312 callback.call(scope||this, null, arg, false);
12317 isLoading : function(){
12318 return this.trans ? true : false;
12322 * Abort the current server request.
12324 abort : function(){
12325 if(this.isLoading()){
12326 this.destroyTrans(this.trans);
12331 destroyTrans : function(trans, isLoaded){
12332 this.head.removeChild(document.getElementById(trans.scriptId));
12333 clearTimeout(trans.timeoutId);
12335 window[trans.cb] = undefined;
12337 delete window[trans.cb];
12340 // if hasn't been loaded, wait for load to remove it to prevent script error
12341 window[trans.cb] = function(){
12342 window[trans.cb] = undefined;
12344 delete window[trans.cb];
12351 handleResponse : function(o, trans){
12352 this.trans = false;
12353 this.destroyTrans(trans, true);
12356 result = trans.reader.readRecords(o);
12358 this.fireEvent("loadexception", this, o, trans.arg, e);
12359 trans.callback.call(trans.scope||window, null, trans.arg, false);
12362 this.fireEvent("load", this, o, trans.arg);
12363 trans.callback.call(trans.scope||window, result, trans.arg, true);
12367 handleFailure : function(trans){
12368 this.trans = false;
12369 this.destroyTrans(trans, false);
12370 this.fireEvent("loadexception", this, null, trans.arg);
12371 trans.callback.call(trans.scope||window, null, trans.arg, false);
12375 * Ext JS Library 1.1.1
12376 * Copyright(c) 2006-2007, Ext JS, LLC.
12378 * Originally Released Under LGPL - original licence link has changed is not relivant.
12381 * <script type="text/javascript">
12385 * @class Roo.data.JsonReader
12386 * @extends Roo.data.DataReader
12387 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12388 * based on mappings in a provided Roo.data.Record constructor.
12390 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12391 * in the reply previously.
12396 var RecordDef = Roo.data.Record.create([
12397 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
12398 {name: 'occupation'} // This field will use "occupation" as the mapping.
12400 var myReader = new Roo.data.JsonReader({
12401 totalProperty: "results", // The property which contains the total dataset size (optional)
12402 root: "rows", // The property which contains an Array of row objects
12403 id: "id" // The property within each row object that provides an ID for the record (optional)
12407 * This would consume a JSON file like this:
12409 { 'results': 2, 'rows': [
12410 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12411 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12414 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12415 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12416 * paged from the remote server.
12417 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12418 * @cfg {String} root name of the property which contains the Array of row objects.
12419 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12420 * @cfg {Array} fields Array of field definition objects
12422 * Create a new JsonReader
12423 * @param {Object} meta Metadata configuration options
12424 * @param {Object} recordType Either an Array of field definition objects,
12425 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12427 Roo.data.JsonReader = function(meta, recordType){
12430 // set some defaults:
12431 Roo.applyIf(meta, {
12432 totalProperty: 'total',
12433 successProperty : 'success',
12438 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12440 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12443 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
12444 * Used by Store query builder to append _requestMeta to params.
12447 metaFromRemote : false,
12449 * This method is only used by a DataProxy which has retrieved data from a remote server.
12450 * @param {Object} response The XHR object which contains the JSON data in its responseText.
12451 * @return {Object} data A data block which is used by an Roo.data.Store object as
12452 * a cache of Roo.data.Records.
12454 read : function(response){
12455 var json = response.responseText;
12457 var o = /* eval:var:o */ eval("("+json+")");
12459 throw {message: "JsonReader.read: Json object not found"};
12465 this.metaFromRemote = true;
12466 this.meta = o.metaData;
12467 this.recordType = Roo.data.Record.create(o.metaData.fields);
12468 this.onMetaChange(this.meta, this.recordType, o);
12470 return this.readRecords(o);
12473 // private function a store will implement
12474 onMetaChange : function(meta, recordType, o){
12481 simpleAccess: function(obj, subsc) {
12488 getJsonAccessor: function(){
12490 return function(expr) {
12492 return(re.test(expr))
12493 ? new Function("obj", "return obj." + expr)
12498 return Roo.emptyFn;
12503 * Create a data block containing Roo.data.Records from an XML document.
12504 * @param {Object} o An object which contains an Array of row objects in the property specified
12505 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12506 * which contains the total size of the dataset.
12507 * @return {Object} data A data block which is used by an Roo.data.Store object as
12508 * a cache of Roo.data.Records.
12510 readRecords : function(o){
12512 * After any data loads, the raw JSON data is available for further custom processing.
12516 var s = this.meta, Record = this.recordType,
12517 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12519 // Generate extraction functions for the totalProperty, the root, the id, and for each field
12521 if(s.totalProperty) {
12522 this.getTotal = this.getJsonAccessor(s.totalProperty);
12524 if(s.successProperty) {
12525 this.getSuccess = this.getJsonAccessor(s.successProperty);
12527 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12529 var g = this.getJsonAccessor(s.id);
12530 this.getId = function(rec) {
12532 return (r === undefined || r === "") ? null : r;
12535 this.getId = function(){return null;};
12538 for(var jj = 0; jj < fl; jj++){
12540 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12541 this.ef[jj] = this.getJsonAccessor(map);
12545 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12546 if(s.totalProperty){
12547 var vt = parseInt(this.getTotal(o), 10);
12552 if(s.successProperty){
12553 var vs = this.getSuccess(o);
12554 if(vs === false || vs === 'false'){
12559 for(var i = 0; i < c; i++){
12562 var id = this.getId(n);
12563 for(var j = 0; j < fl; j++){
12565 var v = this.ef[j](n);
12567 Roo.log('missing convert for ' + f.name);
12571 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12573 var record = new Record(values, id);
12575 records[i] = record;
12581 totalRecords : totalRecords
12586 * Ext JS Library 1.1.1
12587 * Copyright(c) 2006-2007, Ext JS, LLC.
12589 * Originally Released Under LGPL - original licence link has changed is not relivant.
12592 * <script type="text/javascript">
12596 * @class Roo.data.ArrayReader
12597 * @extends Roo.data.DataReader
12598 * Data reader class to create an Array of Roo.data.Record objects from an Array.
12599 * Each element of that Array represents a row of data fields. The
12600 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12601 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12605 var RecordDef = Roo.data.Record.create([
12606 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
12607 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
12609 var myReader = new Roo.data.ArrayReader({
12610 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
12614 * This would consume an Array like this:
12616 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12618 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12620 * Create a new JsonReader
12621 * @param {Object} meta Metadata configuration options.
12622 * @param {Object} recordType Either an Array of field definition objects
12623 * as specified to {@link Roo.data.Record#create},
12624 * or an {@link Roo.data.Record} object
12625 * created using {@link Roo.data.Record#create}.
12627 Roo.data.ArrayReader = function(meta, recordType){
12628 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12631 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12633 * Create a data block containing Roo.data.Records from an XML document.
12634 * @param {Object} o An Array of row objects which represents the dataset.
12635 * @return {Object} data A data block which is used by an Roo.data.Store object as
12636 * a cache of Roo.data.Records.
12638 readRecords : function(o){
12639 var sid = this.meta ? this.meta.id : null;
12640 var recordType = this.recordType, fields = recordType.prototype.fields;
12643 for(var i = 0; i < root.length; i++){
12646 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12647 for(var j = 0, jlen = fields.length; j < jlen; j++){
12648 var f = fields.items[j];
12649 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12650 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12652 values[f.name] = v;
12654 var record = new recordType(values, id);
12656 records[records.length] = record;
12660 totalRecords : records.length
12669 * @class Roo.bootstrap.ComboBox
12670 * @extends Roo.bootstrap.TriggerField
12671 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12672 * @cfg {Boolean} append (true|false) default false
12673 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12674 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12675 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12676 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12677 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12678 * @cfg {Boolean} animate default true
12679 * @cfg {Boolean} emptyResultText only for touch device
12680 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12681 * @cfg {String} emptyTitle default ''
12683 * Create a new ComboBox.
12684 * @param {Object} config Configuration options
12686 Roo.bootstrap.ComboBox = function(config){
12687 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12691 * Fires when the dropdown list is expanded
12692 * @param {Roo.bootstrap.ComboBox} combo This combo box
12697 * Fires when the dropdown list is collapsed
12698 * @param {Roo.bootstrap.ComboBox} combo This combo box
12702 * @event beforeselect
12703 * Fires before a list item is selected. Return false to cancel the selection.
12704 * @param {Roo.bootstrap.ComboBox} combo This combo box
12705 * @param {Roo.data.Record} record The data record returned from the underlying store
12706 * @param {Number} index The index of the selected item in the dropdown list
12708 'beforeselect' : true,
12711 * Fires when a list item is selected
12712 * @param {Roo.bootstrap.ComboBox} combo This combo box
12713 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12714 * @param {Number} index The index of the selected item in the dropdown list
12718 * @event beforequery
12719 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12720 * The event object passed has these properties:
12721 * @param {Roo.bootstrap.ComboBox} combo This combo box
12722 * @param {String} query The query
12723 * @param {Boolean} forceAll true to force "all" query
12724 * @param {Boolean} cancel true to cancel the query
12725 * @param {Object} e The query event object
12727 'beforequery': true,
12730 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12731 * @param {Roo.bootstrap.ComboBox} combo This combo box
12736 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12737 * @param {Roo.bootstrap.ComboBox} combo This combo box
12738 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12743 * Fires when the remove value from the combobox array
12744 * @param {Roo.bootstrap.ComboBox} combo This combo box
12748 * @event afterremove
12749 * Fires when the remove value from the combobox array
12750 * @param {Roo.bootstrap.ComboBox} combo This combo box
12752 'afterremove' : true,
12754 * @event specialfilter
12755 * Fires when specialfilter
12756 * @param {Roo.bootstrap.ComboBox} combo This combo box
12758 'specialfilter' : true,
12761 * Fires when tick the element
12762 * @param {Roo.bootstrap.ComboBox} combo This combo box
12766 * @event touchviewdisplay
12767 * Fires when touch view require special display (default is using displayField)
12768 * @param {Roo.bootstrap.ComboBox} combo This combo box
12769 * @param {Object} cfg set html .
12771 'touchviewdisplay' : true
12776 this.tickItems = [];
12778 this.selectedIndex = -1;
12779 if(this.mode == 'local'){
12780 if(config.queryDelay === undefined){
12781 this.queryDelay = 10;
12783 if(config.minChars === undefined){
12789 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12792 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12793 * rendering into an Roo.Editor, defaults to false)
12796 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12797 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12800 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12803 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12804 * the dropdown list (defaults to undefined, with no header element)
12808 * @cfg {String/Roo.Template} tpl The template to use to render the output
12812 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12814 listWidth: undefined,
12816 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12817 * mode = 'remote' or 'text' if mode = 'local')
12819 displayField: undefined,
12822 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12823 * mode = 'remote' or 'value' if mode = 'local').
12824 * Note: use of a valueField requires the user make a selection
12825 * in order for a value to be mapped.
12827 valueField: undefined,
12829 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12834 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12835 * field's data value (defaults to the underlying DOM element's name)
12837 hiddenName: undefined,
12839 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12843 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12845 selectedClass: 'active',
12848 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12852 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12853 * anchor positions (defaults to 'tl-bl')
12855 listAlign: 'tl-bl?',
12857 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12861 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12862 * query specified by the allQuery config option (defaults to 'query')
12864 triggerAction: 'query',
12866 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12867 * (defaults to 4, does not apply if editable = false)
12871 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12872 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12876 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12877 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12881 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12882 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12886 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12887 * when editable = true (defaults to false)
12889 selectOnFocus:false,
12891 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12893 queryParam: 'query',
12895 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12896 * when mode = 'remote' (defaults to 'Loading...')
12898 loadingText: 'Loading...',
12900 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12904 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12908 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12909 * traditional select (defaults to true)
12913 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12917 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12921 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12922 * listWidth has a higher value)
12926 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12927 * allow the user to set arbitrary text into the field (defaults to false)
12929 forceSelection:false,
12931 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12932 * if typeAhead = true (defaults to 250)
12934 typeAheadDelay : 250,
12936 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12937 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12939 valueNotFoundText : undefined,
12941 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12943 blockFocus : false,
12946 * @cfg {Boolean} disableClear Disable showing of clear button.
12948 disableClear : false,
12950 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12952 alwaysQuery : false,
12955 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12960 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12962 invalidClass : "has-warning",
12965 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12967 validClass : "has-success",
12970 * @cfg {Boolean} specialFilter (true|false) special filter default false
12972 specialFilter : false,
12975 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12977 mobileTouchView : true,
12980 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12982 useNativeIOS : false,
12984 ios_options : false,
12996 btnPosition : 'right',
12997 triggerList : true,
12998 showToggleBtn : true,
13000 emptyResultText: 'Empty',
13001 triggerText : 'Select',
13004 // element that contains real text value.. (when hidden is used..)
13006 getAutoCreate : function()
13011 * Render classic select for iso
13014 if(Roo.isIOS && this.useNativeIOS){
13015 cfg = this.getAutoCreateNativeIOS();
13023 if(Roo.isTouch && this.mobileTouchView){
13024 cfg = this.getAutoCreateTouchView();
13031 if(!this.tickable){
13032 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13037 * ComboBox with tickable selections
13040 var align = this.labelAlign || this.parentLabelAlign();
13043 cls : 'form-group roo-combobox-tickable' //input-group
13046 var btn_text_select = '';
13047 var btn_text_done = '';
13048 var btn_text_cancel = '';
13050 if (this.btn_text_show) {
13051 btn_text_select = 'Select';
13052 btn_text_done = 'Done';
13053 btn_text_cancel = 'Cancel';
13058 cls : 'tickable-buttons',
13063 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13064 //html : this.triggerText
13065 html: btn_text_select
13071 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13073 html: btn_text_done
13079 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13081 html: btn_text_cancel
13087 buttons.cn.unshift({
13089 cls: 'roo-select2-search-field-input'
13095 Roo.each(buttons.cn, function(c){
13097 c.cls += ' btn-' + _this.size;
13100 if (_this.disabled) {
13111 cls: 'form-hidden-field'
13115 cls: 'roo-select2-choices',
13119 cls: 'roo-select2-search-field',
13130 cls: 'roo-select2-container input-group roo-select2-container-multi',
13135 // cls: 'typeahead typeahead-long dropdown-menu',
13136 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
13141 if(this.hasFeedback && !this.allowBlank){
13145 cls: 'glyphicon form-control-feedback'
13148 combobox.cn.push(feedback);
13152 if (align ==='left' && this.fieldLabel.length) {
13154 cfg.cls += ' roo-form-group-label-left';
13159 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13160 tooltip : 'This field is required'
13165 cls : 'control-label',
13166 html : this.fieldLabel
13178 var labelCfg = cfg.cn[1];
13179 var contentCfg = cfg.cn[2];
13182 if(this.indicatorpos == 'right'){
13188 cls : 'control-label',
13192 html : this.fieldLabel
13196 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13197 tooltip : 'This field is required'
13212 labelCfg = cfg.cn[0];
13213 contentCfg = cfg.cn[1];
13217 if(this.labelWidth > 12){
13218 labelCfg.style = "width: " + this.labelWidth + 'px';
13221 if(this.labelWidth < 13 && this.labelmd == 0){
13222 this.labelmd = this.labelWidth;
13225 if(this.labellg > 0){
13226 labelCfg.cls += ' col-lg-' + this.labellg;
13227 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13230 if(this.labelmd > 0){
13231 labelCfg.cls += ' col-md-' + this.labelmd;
13232 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13235 if(this.labelsm > 0){
13236 labelCfg.cls += ' col-sm-' + this.labelsm;
13237 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13240 if(this.labelxs > 0){
13241 labelCfg.cls += ' col-xs-' + this.labelxs;
13242 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13246 } else if ( this.fieldLabel.length) {
13247 // Roo.log(" label");
13251 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13252 tooltip : 'This field is required'
13256 //cls : 'input-group-addon',
13257 html : this.fieldLabel
13262 if(this.indicatorpos == 'right'){
13266 //cls : 'input-group-addon',
13267 html : this.fieldLabel
13271 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13272 tooltip : 'This field is required'
13281 // Roo.log(" no label && no align");
13288 ['xs','sm','md','lg'].map(function(size){
13289 if (settings[size]) {
13290 cfg.cls += ' col-' + size + '-' + settings[size];
13298 _initEventsCalled : false,
13301 initEvents: function()
13303 if (this._initEventsCalled) { // as we call render... prevent looping...
13306 this._initEventsCalled = true;
13309 throw "can not find store for combo";
13312 this.indicator = this.indicatorEl();
13314 this.store = Roo.factory(this.store, Roo.data);
13315 this.store.parent = this;
13317 // if we are building from html. then this element is so complex, that we can not really
13318 // use the rendered HTML.
13319 // so we have to trash and replace the previous code.
13320 if (Roo.XComponent.build_from_html) {
13321 // remove this element....
13322 var e = this.el.dom, k=0;
13323 while (e ) { e = e.previousSibling; ++k;}
13328 this.rendered = false;
13330 this.render(this.parent().getChildContainer(true), k);
13333 if(Roo.isIOS && this.useNativeIOS){
13334 this.initIOSView();
13342 if(Roo.isTouch && this.mobileTouchView){
13343 this.initTouchView();
13348 this.initTickableEvents();
13352 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13354 if(this.hiddenName){
13356 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13358 this.hiddenField.dom.value =
13359 this.hiddenValue !== undefined ? this.hiddenValue :
13360 this.value !== undefined ? this.value : '';
13362 // prevent input submission
13363 this.el.dom.removeAttribute('name');
13364 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13369 // this.el.dom.setAttribute('autocomplete', 'off');
13372 var cls = 'x-combo-list';
13374 //this.list = new Roo.Layer({
13375 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13381 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13382 _this.list.setWidth(lw);
13385 this.list.on('mouseover', this.onViewOver, this);
13386 this.list.on('mousemove', this.onViewMove, this);
13387 this.list.on('scroll', this.onViewScroll, this);
13390 this.list.swallowEvent('mousewheel');
13391 this.assetHeight = 0;
13394 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13395 this.assetHeight += this.header.getHeight();
13398 this.innerList = this.list.createChild({cls:cls+'-inner'});
13399 this.innerList.on('mouseover', this.onViewOver, this);
13400 this.innerList.on('mousemove', this.onViewMove, this);
13401 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13403 if(this.allowBlank && !this.pageSize && !this.disableClear){
13404 this.footer = this.list.createChild({cls:cls+'-ft'});
13405 this.pageTb = new Roo.Toolbar(this.footer);
13409 this.footer = this.list.createChild({cls:cls+'-ft'});
13410 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13411 {pageSize: this.pageSize});
13415 if (this.pageTb && this.allowBlank && !this.disableClear) {
13417 this.pageTb.add(new Roo.Toolbar.Fill(), {
13418 cls: 'x-btn-icon x-btn-clear',
13420 handler: function()
13423 _this.clearValue();
13424 _this.onSelect(false, -1);
13429 this.assetHeight += this.footer.getHeight();
13434 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13437 this.view = new Roo.View(this.list, this.tpl, {
13438 singleSelect:true, store: this.store, selectedClass: this.selectedClass
13440 //this.view.wrapEl.setDisplayed(false);
13441 this.view.on('click', this.onViewClick, this);
13444 this.store.on('beforeload', this.onBeforeLoad, this);
13445 this.store.on('load', this.onLoad, this);
13446 this.store.on('loadexception', this.onLoadException, this);
13448 if(this.resizable){
13449 this.resizer = new Roo.Resizable(this.list, {
13450 pinned:true, handles:'se'
13452 this.resizer.on('resize', function(r, w, h){
13453 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13454 this.listWidth = w;
13455 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13456 this.restrictHeight();
13458 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13461 if(!this.editable){
13462 this.editable = true;
13463 this.setEditable(false);
13468 if (typeof(this.events.add.listeners) != 'undefined') {
13470 this.addicon = this.wrap.createChild(
13471 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
13473 this.addicon.on('click', function(e) {
13474 this.fireEvent('add', this);
13477 if (typeof(this.events.edit.listeners) != 'undefined') {
13479 this.editicon = this.wrap.createChild(
13480 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
13481 if (this.addicon) {
13482 this.editicon.setStyle('margin-left', '40px');
13484 this.editicon.on('click', function(e) {
13486 // we fire even if inothing is selected..
13487 this.fireEvent('edit', this, this.lastData );
13493 this.keyNav = new Roo.KeyNav(this.inputEl(), {
13494 "up" : function(e){
13495 this.inKeyMode = true;
13499 "down" : function(e){
13500 if(!this.isExpanded()){
13501 this.onTriggerClick();
13503 this.inKeyMode = true;
13508 "enter" : function(e){
13509 // this.onViewClick();
13513 if(this.fireEvent("specialkey", this, e)){
13514 this.onViewClick(false);
13520 "esc" : function(e){
13524 "tab" : function(e){
13527 if(this.fireEvent("specialkey", this, e)){
13528 this.onViewClick(false);
13536 doRelay : function(foo, bar, hname){
13537 if(hname == 'down' || this.scope.isExpanded()){
13538 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13547 this.queryDelay = Math.max(this.queryDelay || 10,
13548 this.mode == 'local' ? 10 : 250);
13551 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13553 if(this.typeAhead){
13554 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13556 if(this.editable !== false){
13557 this.inputEl().on("keyup", this.onKeyUp, this);
13559 if(this.forceSelection){
13560 this.inputEl().on('blur', this.doForce, this);
13564 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13565 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13569 initTickableEvents: function()
13573 if(this.hiddenName){
13575 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13577 this.hiddenField.dom.value =
13578 this.hiddenValue !== undefined ? this.hiddenValue :
13579 this.value !== undefined ? this.value : '';
13581 // prevent input submission
13582 this.el.dom.removeAttribute('name');
13583 this.hiddenField.dom.setAttribute('name', this.hiddenName);
13588 // this.list = this.el.select('ul.dropdown-menu',true).first();
13590 this.choices = this.el.select('ul.roo-select2-choices', true).first();
13591 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13592 if(this.triggerList){
13593 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13596 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13597 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13599 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13600 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13602 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13603 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13605 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13606 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13607 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13610 this.cancelBtn.hide();
13615 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13616 _this.list.setWidth(lw);
13619 this.list.on('mouseover', this.onViewOver, this);
13620 this.list.on('mousemove', this.onViewMove, this);
13622 this.list.on('scroll', this.onViewScroll, this);
13625 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
13626 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13629 this.view = new Roo.View(this.list, this.tpl, {
13634 selectedClass: this.selectedClass
13637 //this.view.wrapEl.setDisplayed(false);
13638 this.view.on('click', this.onViewClick, this);
13642 this.store.on('beforeload', this.onBeforeLoad, this);
13643 this.store.on('load', this.onLoad, this);
13644 this.store.on('loadexception', this.onLoadException, this);
13647 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13648 "up" : function(e){
13649 this.inKeyMode = true;
13653 "down" : function(e){
13654 this.inKeyMode = true;
13658 "enter" : function(e){
13659 if(this.fireEvent("specialkey", this, e)){
13660 this.onViewClick(false);
13666 "esc" : function(e){
13667 this.onTickableFooterButtonClick(e, false, false);
13670 "tab" : function(e){
13671 this.fireEvent("specialkey", this, e);
13673 this.onTickableFooterButtonClick(e, false, false);
13680 doRelay : function(e, fn, key){
13681 if(this.scope.isExpanded()){
13682 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13691 this.queryDelay = Math.max(this.queryDelay || 10,
13692 this.mode == 'local' ? 10 : 250);
13695 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13697 if(this.typeAhead){
13698 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13701 if(this.editable !== false){
13702 this.tickableInputEl().on("keyup", this.onKeyUp, this);
13705 this.indicator = this.indicatorEl();
13707 if(this.indicator){
13708 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13709 this.indicator.hide();
13714 onDestroy : function(){
13716 this.view.setStore(null);
13717 this.view.el.removeAllListeners();
13718 this.view.el.remove();
13719 this.view.purgeListeners();
13722 this.list.dom.innerHTML = '';
13726 this.store.un('beforeload', this.onBeforeLoad, this);
13727 this.store.un('load', this.onLoad, this);
13728 this.store.un('loadexception', this.onLoadException, this);
13730 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13734 fireKey : function(e){
13735 if(e.isNavKeyPress() && !this.list.isVisible()){
13736 this.fireEvent("specialkey", this, e);
13741 onResize: function(w, h){
13742 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13744 // if(typeof w != 'number'){
13745 // // we do not handle it!?!?
13748 // var tw = this.trigger.getWidth();
13749 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13750 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13752 // this.inputEl().setWidth( this.adjustWidth('input', x));
13754 // //this.trigger.setStyle('left', x+'px');
13756 // if(this.list && this.listWidth === undefined){
13757 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13758 // this.list.setWidth(lw);
13759 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13767 * Allow or prevent the user from directly editing the field text. If false is passed,
13768 * the user will only be able to select from the items defined in the dropdown list. This method
13769 * is the runtime equivalent of setting the 'editable' config option at config time.
13770 * @param {Boolean} value True to allow the user to directly edit the field text
13772 setEditable : function(value){
13773 if(value == this.editable){
13776 this.editable = value;
13778 this.inputEl().dom.setAttribute('readOnly', true);
13779 this.inputEl().on('mousedown', this.onTriggerClick, this);
13780 this.inputEl().addClass('x-combo-noedit');
13782 this.inputEl().dom.setAttribute('readOnly', false);
13783 this.inputEl().un('mousedown', this.onTriggerClick, this);
13784 this.inputEl().removeClass('x-combo-noedit');
13790 onBeforeLoad : function(combo,opts){
13791 if(!this.hasFocus){
13795 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13797 this.restrictHeight();
13798 this.selectedIndex = -1;
13802 onLoad : function(){
13804 this.hasQuery = false;
13806 if(!this.hasFocus){
13810 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13811 this.loading.hide();
13814 if(this.store.getCount() > 0){
13817 this.restrictHeight();
13818 if(this.lastQuery == this.allQuery){
13819 if(this.editable && !this.tickable){
13820 this.inputEl().dom.select();
13824 !this.selectByValue(this.value, true) &&
13827 !this.store.lastOptions ||
13828 typeof(this.store.lastOptions.add) == 'undefined' ||
13829 this.store.lastOptions.add != true
13832 this.select(0, true);
13835 if(this.autoFocus){
13838 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13839 this.taTask.delay(this.typeAheadDelay);
13843 this.onEmptyResults();
13849 onLoadException : function()
13851 this.hasQuery = false;
13853 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13854 this.loading.hide();
13857 if(this.tickable && this.editable){
13862 // only causes errors at present
13863 //Roo.log(this.store.reader.jsonData);
13864 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13866 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13872 onTypeAhead : function(){
13873 if(this.store.getCount() > 0){
13874 var r = this.store.getAt(0);
13875 var newValue = r.data[this.displayField];
13876 var len = newValue.length;
13877 var selStart = this.getRawValue().length;
13879 if(selStart != len){
13880 this.setRawValue(newValue);
13881 this.selectText(selStart, newValue.length);
13887 onSelect : function(record, index){
13889 if(this.fireEvent('beforeselect', this, record, index) !== false){
13891 this.setFromData(index > -1 ? record.data : false);
13894 this.fireEvent('select', this, record, index);
13899 * Returns the currently selected field value or empty string if no value is set.
13900 * @return {String} value The selected value
13902 getValue : function()
13904 if(Roo.isIOS && this.useNativeIOS){
13905 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13909 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13912 if(this.valueField){
13913 return typeof this.value != 'undefined' ? this.value : '';
13915 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13919 getRawValue : function()
13921 if(Roo.isIOS && this.useNativeIOS){
13922 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13925 var v = this.inputEl().getValue();
13931 * Clears any text/value currently set in the field
13933 clearValue : function(){
13935 if(this.hiddenField){
13936 this.hiddenField.dom.value = '';
13939 this.setRawValue('');
13940 this.lastSelectionText = '';
13941 this.lastData = false;
13943 var close = this.closeTriggerEl();
13954 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13955 * will be displayed in the field. If the value does not match the data value of an existing item,
13956 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13957 * Otherwise the field will be blank (although the value will still be set).
13958 * @param {String} value The value to match
13960 setValue : function(v)
13962 if(Roo.isIOS && this.useNativeIOS){
13963 this.setIOSValue(v);
13973 if(this.valueField){
13974 var r = this.findRecord(this.valueField, v);
13976 text = r.data[this.displayField];
13977 }else if(this.valueNotFoundText !== undefined){
13978 text = this.valueNotFoundText;
13981 this.lastSelectionText = text;
13982 if(this.hiddenField){
13983 this.hiddenField.dom.value = v;
13985 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13988 var close = this.closeTriggerEl();
13991 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13997 * @property {Object} the last set data for the element
14002 * Sets the value of the field based on a object which is related to the record format for the store.
14003 * @param {Object} value the value to set as. or false on reset?
14005 setFromData : function(o){
14012 var dv = ''; // display value
14013 var vv = ''; // value value..
14015 if (this.displayField) {
14016 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14018 // this is an error condition!!!
14019 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14022 if(this.valueField){
14023 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14026 var close = this.closeTriggerEl();
14029 if(dv.length || vv * 1 > 0){
14031 this.blockFocus=true;
14037 if(this.hiddenField){
14038 this.hiddenField.dom.value = vv;
14040 this.lastSelectionText = dv;
14041 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14045 // no hidden field.. - we store the value in 'value', but still display
14046 // display field!!!!
14047 this.lastSelectionText = dv;
14048 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14055 reset : function(){
14056 // overridden so that last data is reset..
14063 this.setValue(this.originalValue);
14064 //this.clearInvalid();
14065 this.lastData = false;
14067 this.view.clearSelections();
14073 findRecord : function(prop, value){
14075 if(this.store.getCount() > 0){
14076 this.store.each(function(r){
14077 if(r.data[prop] == value){
14087 getName: function()
14089 // returns hidden if it's set..
14090 if (!this.rendered) {return ''};
14091 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
14095 onViewMove : function(e, t){
14096 this.inKeyMode = false;
14100 onViewOver : function(e, t){
14101 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14104 var item = this.view.findItemFromChild(t);
14107 var index = this.view.indexOf(item);
14108 this.select(index, false);
14113 onViewClick : function(view, doFocus, el, e)
14115 var index = this.view.getSelectedIndexes()[0];
14117 var r = this.store.getAt(index);
14121 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14128 Roo.each(this.tickItems, function(v,k){
14130 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14132 _this.tickItems.splice(k, 1);
14134 if(typeof(e) == 'undefined' && view == false){
14135 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14147 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14148 this.tickItems.push(r.data);
14151 if(typeof(e) == 'undefined' && view == false){
14152 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14159 this.onSelect(r, index);
14161 if(doFocus !== false && !this.blockFocus){
14162 this.inputEl().focus();
14167 restrictHeight : function(){
14168 //this.innerList.dom.style.height = '';
14169 //var inner = this.innerList.dom;
14170 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14171 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14172 //this.list.beginUpdate();
14173 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14174 this.list.alignTo(this.inputEl(), this.listAlign);
14175 this.list.alignTo(this.inputEl(), this.listAlign);
14176 //this.list.endUpdate();
14180 onEmptyResults : function(){
14182 if(this.tickable && this.editable){
14183 this.hasFocus = false;
14184 this.restrictHeight();
14192 * Returns true if the dropdown list is expanded, else false.
14194 isExpanded : function(){
14195 return this.list.isVisible();
14199 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14200 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14201 * @param {String} value The data value of the item to select
14202 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14203 * selected item if it is not currently in view (defaults to true)
14204 * @return {Boolean} True if the value matched an item in the list, else false
14206 selectByValue : function(v, scrollIntoView){
14207 if(v !== undefined && v !== null){
14208 var r = this.findRecord(this.valueField || this.displayField, v);
14210 this.select(this.store.indexOf(r), scrollIntoView);
14218 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14219 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14220 * @param {Number} index The zero-based index of the list item to select
14221 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14222 * selected item if it is not currently in view (defaults to true)
14224 select : function(index, scrollIntoView){
14225 this.selectedIndex = index;
14226 this.view.select(index);
14227 if(scrollIntoView !== false){
14228 var el = this.view.getNode(index);
14230 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14233 this.list.scrollChildIntoView(el, false);
14239 selectNext : function(){
14240 var ct = this.store.getCount();
14242 if(this.selectedIndex == -1){
14244 }else if(this.selectedIndex < ct-1){
14245 this.select(this.selectedIndex+1);
14251 selectPrev : function(){
14252 var ct = this.store.getCount();
14254 if(this.selectedIndex == -1){
14256 }else if(this.selectedIndex != 0){
14257 this.select(this.selectedIndex-1);
14263 onKeyUp : function(e){
14264 if(this.editable !== false && !e.isSpecialKey()){
14265 this.lastKey = e.getKey();
14266 this.dqTask.delay(this.queryDelay);
14271 validateBlur : function(){
14272 return !this.list || !this.list.isVisible();
14276 initQuery : function(){
14278 var v = this.getRawValue();
14280 if(this.tickable && this.editable){
14281 v = this.tickableInputEl().getValue();
14288 doForce : function(){
14289 if(this.inputEl().dom.value.length > 0){
14290 this.inputEl().dom.value =
14291 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14297 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
14298 * query allowing the query action to be canceled if needed.
14299 * @param {String} query The SQL query to execute
14300 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14301 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
14302 * saved in the current store (defaults to false)
14304 doQuery : function(q, forceAll){
14306 if(q === undefined || q === null){
14311 forceAll: forceAll,
14315 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14320 forceAll = qe.forceAll;
14321 if(forceAll === true || (q.length >= this.minChars)){
14323 this.hasQuery = true;
14325 if(this.lastQuery != q || this.alwaysQuery){
14326 this.lastQuery = q;
14327 if(this.mode == 'local'){
14328 this.selectedIndex = -1;
14330 this.store.clearFilter();
14333 if(this.specialFilter){
14334 this.fireEvent('specialfilter', this);
14339 this.store.filter(this.displayField, q);
14342 this.store.fireEvent("datachanged", this.store);
14349 this.store.baseParams[this.queryParam] = q;
14351 var options = {params : this.getParams(q)};
14354 options.add = true;
14355 options.params.start = this.page * this.pageSize;
14358 this.store.load(options);
14361 * this code will make the page width larger, at the beginning, the list not align correctly,
14362 * we should expand the list on onLoad
14363 * so command out it
14368 this.selectedIndex = -1;
14373 this.loadNext = false;
14377 getParams : function(q){
14379 //p[this.queryParam] = q;
14383 p.limit = this.pageSize;
14389 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14391 collapse : function(){
14392 if(!this.isExpanded()){
14398 this.hasFocus = false;
14402 this.cancelBtn.hide();
14403 this.trigger.show();
14406 this.tickableInputEl().dom.value = '';
14407 this.tickableInputEl().blur();
14412 Roo.get(document).un('mousedown', this.collapseIf, this);
14413 Roo.get(document).un('mousewheel', this.collapseIf, this);
14414 if (!this.editable) {
14415 Roo.get(document).un('keydown', this.listKeyPress, this);
14417 this.fireEvent('collapse', this);
14423 collapseIf : function(e){
14424 var in_combo = e.within(this.el);
14425 var in_list = e.within(this.list);
14426 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14428 if (in_combo || in_list || is_list) {
14429 //e.stopPropagation();
14434 this.onTickableFooterButtonClick(e, false, false);
14442 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14444 expand : function(){
14446 if(this.isExpanded() || !this.hasFocus){
14450 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14451 this.list.setWidth(lw);
14457 this.restrictHeight();
14461 this.tickItems = Roo.apply([], this.item);
14464 this.cancelBtn.show();
14465 this.trigger.hide();
14468 this.tickableInputEl().focus();
14473 Roo.get(document).on('mousedown', this.collapseIf, this);
14474 Roo.get(document).on('mousewheel', this.collapseIf, this);
14475 if (!this.editable) {
14476 Roo.get(document).on('keydown', this.listKeyPress, this);
14479 this.fireEvent('expand', this);
14483 // Implements the default empty TriggerField.onTriggerClick function
14484 onTriggerClick : function(e)
14486 Roo.log('trigger click');
14488 if(this.disabled || !this.triggerList){
14493 this.loadNext = false;
14495 if(this.isExpanded()){
14497 if (!this.blockFocus) {
14498 this.inputEl().focus();
14502 this.hasFocus = true;
14503 if(this.triggerAction == 'all') {
14504 this.doQuery(this.allQuery, true);
14506 this.doQuery(this.getRawValue());
14508 if (!this.blockFocus) {
14509 this.inputEl().focus();
14514 onTickableTriggerClick : function(e)
14521 this.loadNext = false;
14522 this.hasFocus = true;
14524 if(this.triggerAction == 'all') {
14525 this.doQuery(this.allQuery, true);
14527 this.doQuery(this.getRawValue());
14531 onSearchFieldClick : function(e)
14533 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14534 this.onTickableFooterButtonClick(e, false, false);
14538 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14543 this.loadNext = false;
14544 this.hasFocus = true;
14546 if(this.triggerAction == 'all') {
14547 this.doQuery(this.allQuery, true);
14549 this.doQuery(this.getRawValue());
14553 listKeyPress : function(e)
14555 //Roo.log('listkeypress');
14556 // scroll to first matching element based on key pres..
14557 if (e.isSpecialKey()) {
14560 var k = String.fromCharCode(e.getKey()).toUpperCase();
14563 var csel = this.view.getSelectedNodes();
14564 var cselitem = false;
14566 var ix = this.view.indexOf(csel[0]);
14567 cselitem = this.store.getAt(ix);
14568 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14574 this.store.each(function(v) {
14576 // start at existing selection.
14577 if (cselitem.id == v.id) {
14583 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14584 match = this.store.indexOf(v);
14590 if (match === false) {
14591 return true; // no more action?
14594 this.view.select(match);
14595 var sn = Roo.get(this.view.getSelectedNodes()[0]);
14596 sn.scrollIntoView(sn.dom.parentNode, false);
14599 onViewScroll : function(e, t){
14601 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){
14605 this.hasQuery = true;
14607 this.loading = this.list.select('.loading', true).first();
14609 if(this.loading === null){
14610 this.list.createChild({
14612 cls: 'loading roo-select2-more-results roo-select2-active',
14613 html: 'Loading more results...'
14616 this.loading = this.list.select('.loading', true).first();
14618 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14620 this.loading.hide();
14623 this.loading.show();
14628 this.loadNext = true;
14630 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14635 addItem : function(o)
14637 var dv = ''; // display value
14639 if (this.displayField) {
14640 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14642 // this is an error condition!!!
14643 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
14650 var choice = this.choices.createChild({
14652 cls: 'roo-select2-search-choice',
14661 cls: 'roo-select2-search-choice-close fa fa-times',
14666 }, this.searchField);
14668 var close = choice.select('a.roo-select2-search-choice-close', true).first();
14670 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14678 this.inputEl().dom.value = '';
14683 onRemoveItem : function(e, _self, o)
14685 e.preventDefault();
14687 this.lastItem = Roo.apply([], this.item);
14689 var index = this.item.indexOf(o.data) * 1;
14692 Roo.log('not this item?!');
14696 this.item.splice(index, 1);
14701 this.fireEvent('remove', this, e);
14707 syncValue : function()
14709 if(!this.item.length){
14716 Roo.each(this.item, function(i){
14717 if(_this.valueField){
14718 value.push(i[_this.valueField]);
14725 this.value = value.join(',');
14727 if(this.hiddenField){
14728 this.hiddenField.dom.value = this.value;
14731 this.store.fireEvent("datachanged", this.store);
14736 clearItem : function()
14738 if(!this.multiple){
14744 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14752 if(this.tickable && !Roo.isTouch){
14753 this.view.refresh();
14757 inputEl: function ()
14759 if(Roo.isIOS && this.useNativeIOS){
14760 return this.el.select('select.roo-ios-select', true).first();
14763 if(Roo.isTouch && this.mobileTouchView){
14764 return this.el.select('input.form-control',true).first();
14768 return this.searchField;
14771 return this.el.select('input.form-control',true).first();
14774 onTickableFooterButtonClick : function(e, btn, el)
14776 e.preventDefault();
14778 this.lastItem = Roo.apply([], this.item);
14780 if(btn && btn.name == 'cancel'){
14781 this.tickItems = Roo.apply([], this.item);
14790 Roo.each(this.tickItems, function(o){
14798 validate : function()
14800 if(this.getVisibilityEl().hasClass('hidden')){
14804 var v = this.getRawValue();
14807 v = this.getValue();
14810 if(this.disabled || this.allowBlank || v.length){
14815 this.markInvalid();
14819 tickableInputEl : function()
14821 if(!this.tickable || !this.editable){
14822 return this.inputEl();
14825 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14829 getAutoCreateTouchView : function()
14834 cls: 'form-group' //input-group
14840 type : this.inputType,
14841 cls : 'form-control x-combo-noedit',
14842 autocomplete: 'new-password',
14843 placeholder : this.placeholder || '',
14848 input.name = this.name;
14852 input.cls += ' input-' + this.size;
14855 if (this.disabled) {
14856 input.disabled = true;
14867 inputblock.cls += ' input-group';
14869 inputblock.cn.unshift({
14871 cls : 'input-group-addon',
14876 if(this.removable && !this.multiple){
14877 inputblock.cls += ' roo-removable';
14879 inputblock.cn.push({
14882 cls : 'roo-combo-removable-btn close'
14886 if(this.hasFeedback && !this.allowBlank){
14888 inputblock.cls += ' has-feedback';
14890 inputblock.cn.push({
14892 cls: 'glyphicon form-control-feedback'
14899 inputblock.cls += (this.before) ? '' : ' input-group';
14901 inputblock.cn.push({
14903 cls : 'input-group-addon',
14914 cls: 'form-hidden-field'
14928 cls: 'form-hidden-field'
14932 cls: 'roo-select2-choices',
14936 cls: 'roo-select2-search-field',
14949 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14955 if(!this.multiple && this.showToggleBtn){
14962 if (this.caret != false) {
14965 cls: 'fa fa-' + this.caret
14972 cls : 'input-group-addon btn dropdown-toggle',
14977 cls: 'combobox-clear',
14991 combobox.cls += ' roo-select2-container-multi';
14994 var align = this.labelAlign || this.parentLabelAlign();
14996 if (align ==='left' && this.fieldLabel.length) {
15001 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15002 tooltip : 'This field is required'
15006 cls : 'control-label',
15007 html : this.fieldLabel
15018 var labelCfg = cfg.cn[1];
15019 var contentCfg = cfg.cn[2];
15022 if(this.indicatorpos == 'right'){
15027 cls : 'control-label',
15031 html : this.fieldLabel
15035 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15036 tooltip : 'This field is required'
15049 labelCfg = cfg.cn[0];
15050 contentCfg = cfg.cn[1];
15055 if(this.labelWidth > 12){
15056 labelCfg.style = "width: " + this.labelWidth + 'px';
15059 if(this.labelWidth < 13 && this.labelmd == 0){
15060 this.labelmd = this.labelWidth;
15063 if(this.labellg > 0){
15064 labelCfg.cls += ' col-lg-' + this.labellg;
15065 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15068 if(this.labelmd > 0){
15069 labelCfg.cls += ' col-md-' + this.labelmd;
15070 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15073 if(this.labelsm > 0){
15074 labelCfg.cls += ' col-sm-' + this.labelsm;
15075 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15078 if(this.labelxs > 0){
15079 labelCfg.cls += ' col-xs-' + this.labelxs;
15080 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15084 } else if ( this.fieldLabel.length) {
15088 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15089 tooltip : 'This field is required'
15093 cls : 'control-label',
15094 html : this.fieldLabel
15105 if(this.indicatorpos == 'right'){
15109 cls : 'control-label',
15110 html : this.fieldLabel,
15114 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15115 tooltip : 'This field is required'
15132 var settings = this;
15134 ['xs','sm','md','lg'].map(function(size){
15135 if (settings[size]) {
15136 cfg.cls += ' col-' + size + '-' + settings[size];
15143 initTouchView : function()
15145 this.renderTouchView();
15147 this.touchViewEl.on('scroll', function(){
15148 this.el.dom.scrollTop = 0;
15151 this.originalValue = this.getValue();
15153 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15155 this.inputEl().on("click", this.showTouchView, this);
15156 if (this.triggerEl) {
15157 this.triggerEl.on("click", this.showTouchView, this);
15161 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15162 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15164 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15166 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15167 this.store.on('load', this.onTouchViewLoad, this);
15168 this.store.on('loadexception', this.onTouchViewLoadException, this);
15170 if(this.hiddenName){
15172 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15174 this.hiddenField.dom.value =
15175 this.hiddenValue !== undefined ? this.hiddenValue :
15176 this.value !== undefined ? this.value : '';
15178 this.el.dom.removeAttribute('name');
15179 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15183 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15184 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15187 if(this.removable && !this.multiple){
15188 var close = this.closeTriggerEl();
15190 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15191 close.on('click', this.removeBtnClick, this, close);
15195 * fix the bug in Safari iOS8
15197 this.inputEl().on("focus", function(e){
15198 document.activeElement.blur();
15206 renderTouchView : function()
15208 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15209 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15211 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15212 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15214 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15215 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15216 this.touchViewBodyEl.setStyle('overflow', 'auto');
15218 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15219 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15221 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15222 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15226 showTouchView : function()
15232 this.touchViewHeaderEl.hide();
15234 if(this.modalTitle.length){
15235 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15236 this.touchViewHeaderEl.show();
15239 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15240 this.touchViewEl.show();
15242 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15244 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15245 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15247 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15249 if(this.modalTitle.length){
15250 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15253 this.touchViewBodyEl.setHeight(bodyHeight);
15257 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15259 this.touchViewEl.addClass('in');
15262 this.doTouchViewQuery();
15266 hideTouchView : function()
15268 this.touchViewEl.removeClass('in');
15272 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15274 this.touchViewEl.setStyle('display', 'none');
15279 setTouchViewValue : function()
15286 Roo.each(this.tickItems, function(o){
15291 this.hideTouchView();
15294 doTouchViewQuery : function()
15303 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15307 if(!this.alwaysQuery || this.mode == 'local'){
15308 this.onTouchViewLoad();
15315 onTouchViewBeforeLoad : function(combo,opts)
15321 onTouchViewLoad : function()
15323 if(this.store.getCount() < 1){
15324 this.onTouchViewEmptyResults();
15328 this.clearTouchView();
15330 var rawValue = this.getRawValue();
15332 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15334 this.tickItems = [];
15336 this.store.data.each(function(d, rowIndex){
15337 var row = this.touchViewListGroup.createChild(template);
15339 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15340 row.addClass(d.data.cls);
15343 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15346 html : d.data[this.displayField]
15349 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15350 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15353 row.removeClass('selected');
15354 if(!this.multiple && this.valueField &&
15355 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15358 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15359 row.addClass('selected');
15362 if(this.multiple && this.valueField &&
15363 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15367 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15368 this.tickItems.push(d.data);
15371 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15375 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15377 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15379 if(this.modalTitle.length){
15380 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15383 var listHeight = this.touchViewListGroup.getHeight();
15387 if(firstChecked && listHeight > bodyHeight){
15388 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15393 onTouchViewLoadException : function()
15395 this.hideTouchView();
15398 onTouchViewEmptyResults : function()
15400 this.clearTouchView();
15402 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15404 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15408 clearTouchView : function()
15410 this.touchViewListGroup.dom.innerHTML = '';
15413 onTouchViewClick : function(e, el, o)
15415 e.preventDefault();
15418 var rowIndex = o.rowIndex;
15420 var r = this.store.getAt(rowIndex);
15422 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15424 if(!this.multiple){
15425 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15426 c.dom.removeAttribute('checked');
15429 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15431 this.setFromData(r.data);
15433 var close = this.closeTriggerEl();
15439 this.hideTouchView();
15441 this.fireEvent('select', this, r, rowIndex);
15446 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15447 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15448 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15452 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15453 this.addItem(r.data);
15454 this.tickItems.push(r.data);
15458 getAutoCreateNativeIOS : function()
15461 cls: 'form-group' //input-group,
15466 cls : 'roo-ios-select'
15470 combobox.name = this.name;
15473 if (this.disabled) {
15474 combobox.disabled = true;
15477 var settings = this;
15479 ['xs','sm','md','lg'].map(function(size){
15480 if (settings[size]) {
15481 cfg.cls += ' col-' + size + '-' + settings[size];
15491 initIOSView : function()
15493 this.store.on('load', this.onIOSViewLoad, this);
15498 onIOSViewLoad : function()
15500 if(this.store.getCount() < 1){
15504 this.clearIOSView();
15506 if(this.allowBlank) {
15508 var default_text = '-- SELECT --';
15510 if(this.placeholder.length){
15511 default_text = this.placeholder;
15514 if(this.emptyTitle.length){
15515 default_text += ' - ' + this.emptyTitle + ' -';
15518 var opt = this.inputEl().createChild({
15521 html : default_text
15525 o[this.valueField] = 0;
15526 o[this.displayField] = default_text;
15528 this.ios_options.push({
15535 this.store.data.each(function(d, rowIndex){
15539 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15540 html = d.data[this.displayField];
15545 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15546 value = d.data[this.valueField];
15555 if(this.value == d.data[this.valueField]){
15556 option['selected'] = true;
15559 var opt = this.inputEl().createChild(option);
15561 this.ios_options.push({
15568 this.inputEl().on('change', function(){
15569 this.fireEvent('select', this);
15574 clearIOSView: function()
15576 this.inputEl().dom.innerHTML = '';
15578 this.ios_options = [];
15581 setIOSValue: function(v)
15585 if(!this.ios_options){
15589 Roo.each(this.ios_options, function(opts){
15591 opts.el.dom.removeAttribute('selected');
15593 if(opts.data[this.valueField] != v){
15597 opts.el.dom.setAttribute('selected', true);
15603 * @cfg {Boolean} grow
15607 * @cfg {Number} growMin
15611 * @cfg {Number} growMax
15620 Roo.apply(Roo.bootstrap.ComboBox, {
15624 cls: 'modal-header',
15646 cls: 'list-group-item',
15650 cls: 'roo-combobox-list-group-item-value'
15654 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15668 listItemCheckbox : {
15670 cls: 'list-group-item',
15674 cls: 'roo-combobox-list-group-item-value'
15678 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15694 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15699 cls: 'modal-footer',
15707 cls: 'col-xs-6 text-left',
15710 cls: 'btn btn-danger roo-touch-view-cancel',
15716 cls: 'col-xs-6 text-right',
15719 cls: 'btn btn-success roo-touch-view-ok',
15730 Roo.apply(Roo.bootstrap.ComboBox, {
15732 touchViewTemplate : {
15734 cls: 'modal fade roo-combobox-touch-view',
15738 cls: 'modal-dialog',
15739 style : 'position:fixed', // we have to fix position....
15743 cls: 'modal-content',
15745 Roo.bootstrap.ComboBox.header,
15746 Roo.bootstrap.ComboBox.body,
15747 Roo.bootstrap.ComboBox.footer
15756 * Ext JS Library 1.1.1
15757 * Copyright(c) 2006-2007, Ext JS, LLC.
15759 * Originally Released Under LGPL - original licence link has changed is not relivant.
15762 * <script type="text/javascript">
15767 * @extends Roo.util.Observable
15768 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
15769 * This class also supports single and multi selection modes. <br>
15770 * Create a data model bound view:
15772 var store = new Roo.data.Store(...);
15774 var view = new Roo.View({
15776 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
15778 singleSelect: true,
15779 selectedClass: "ydataview-selected",
15783 // listen for node click?
15784 view.on("click", function(vw, index, node, e){
15785 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15789 dataModel.load("foobar.xml");
15791 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15793 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15794 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15796 * Note: old style constructor is still suported (container, template, config)
15799 * Create a new View
15800 * @param {Object} config The config object
15803 Roo.View = function(config, depreciated_tpl, depreciated_config){
15805 this.parent = false;
15807 if (typeof(depreciated_tpl) == 'undefined') {
15808 // new way.. - universal constructor.
15809 Roo.apply(this, config);
15810 this.el = Roo.get(this.el);
15813 this.el = Roo.get(config);
15814 this.tpl = depreciated_tpl;
15815 Roo.apply(this, depreciated_config);
15817 this.wrapEl = this.el.wrap().wrap();
15818 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15821 if(typeof(this.tpl) == "string"){
15822 this.tpl = new Roo.Template(this.tpl);
15824 // support xtype ctors..
15825 this.tpl = new Roo.factory(this.tpl, Roo);
15829 this.tpl.compile();
15834 * @event beforeclick
15835 * Fires before a click is processed. Returns false to cancel the default action.
15836 * @param {Roo.View} this
15837 * @param {Number} index The index of the target node
15838 * @param {HTMLElement} node The target node
15839 * @param {Roo.EventObject} e The raw event object
15841 "beforeclick" : true,
15844 * Fires when a template node is clicked.
15845 * @param {Roo.View} this
15846 * @param {Number} index The index of the target node
15847 * @param {HTMLElement} node The target node
15848 * @param {Roo.EventObject} e The raw event object
15853 * Fires when a template node is double clicked.
15854 * @param {Roo.View} this
15855 * @param {Number} index The index of the target node
15856 * @param {HTMLElement} node The target node
15857 * @param {Roo.EventObject} e The raw event object
15861 * @event contextmenu
15862 * Fires when a template node is right clicked.
15863 * @param {Roo.View} this
15864 * @param {Number} index The index of the target node
15865 * @param {HTMLElement} node The target node
15866 * @param {Roo.EventObject} e The raw event object
15868 "contextmenu" : true,
15870 * @event selectionchange
15871 * Fires when the selected nodes change.
15872 * @param {Roo.View} this
15873 * @param {Array} selections Array of the selected nodes
15875 "selectionchange" : true,
15878 * @event beforeselect
15879 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15880 * @param {Roo.View} this
15881 * @param {HTMLElement} node The node to be selected
15882 * @param {Array} selections Array of currently selected nodes
15884 "beforeselect" : true,
15886 * @event preparedata
15887 * Fires on every row to render, to allow you to change the data.
15888 * @param {Roo.View} this
15889 * @param {Object} data to be rendered (change this)
15891 "preparedata" : true
15899 "click": this.onClick,
15900 "dblclick": this.onDblClick,
15901 "contextmenu": this.onContextMenu,
15905 this.selections = [];
15907 this.cmp = new Roo.CompositeElementLite([]);
15909 this.store = Roo.factory(this.store, Roo.data);
15910 this.setStore(this.store, true);
15913 if ( this.footer && this.footer.xtype) {
15915 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15917 this.footer.dataSource = this.store;
15918 this.footer.container = fctr;
15919 this.footer = Roo.factory(this.footer, Roo);
15920 fctr.insertFirst(this.el);
15922 // this is a bit insane - as the paging toolbar seems to detach the el..
15923 // dom.parentNode.parentNode.parentNode
15924 // they get detached?
15928 Roo.View.superclass.constructor.call(this);
15933 Roo.extend(Roo.View, Roo.util.Observable, {
15936 * @cfg {Roo.data.Store} store Data store to load data from.
15941 * @cfg {String|Roo.Element} el The container element.
15946 * @cfg {String|Roo.Template} tpl The template used by this View
15950 * @cfg {String} dataName the named area of the template to use as the data area
15951 * Works with domtemplates roo-name="name"
15955 * @cfg {String} selectedClass The css class to add to selected nodes
15957 selectedClass : "x-view-selected",
15959 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15964 * @cfg {String} text to display on mask (default Loading)
15968 * @cfg {Boolean} multiSelect Allow multiple selection
15970 multiSelect : false,
15972 * @cfg {Boolean} singleSelect Allow single selection
15974 singleSelect: false,
15977 * @cfg {Boolean} toggleSelect - selecting
15979 toggleSelect : false,
15982 * @cfg {Boolean} tickable - selecting
15987 * Returns the element this view is bound to.
15988 * @return {Roo.Element}
15990 getEl : function(){
15991 return this.wrapEl;
15997 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15999 refresh : function(){
16000 //Roo.log('refresh');
16003 // if we are using something like 'domtemplate', then
16004 // the what gets used is:
16005 // t.applySubtemplate(NAME, data, wrapping data..)
16006 // the outer template then get' applied with
16007 // the store 'extra data'
16008 // and the body get's added to the
16009 // roo-name="data" node?
16010 // <span class='roo-tpl-{name}'></span> ?????
16014 this.clearSelections();
16015 this.el.update("");
16017 var records = this.store.getRange();
16018 if(records.length < 1) {
16020 // is this valid?? = should it render a template??
16022 this.el.update(this.emptyText);
16026 if (this.dataName) {
16027 this.el.update(t.apply(this.store.meta)); //????
16028 el = this.el.child('.roo-tpl-' + this.dataName);
16031 for(var i = 0, len = records.length; i < len; i++){
16032 var data = this.prepareData(records[i].data, i, records[i]);
16033 this.fireEvent("preparedata", this, data, i, records[i]);
16035 var d = Roo.apply({}, data);
16038 Roo.apply(d, {'roo-id' : Roo.id()});
16042 Roo.each(this.parent.item, function(item){
16043 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16046 Roo.apply(d, {'roo-data-checked' : 'checked'});
16050 html[html.length] = Roo.util.Format.trim(
16052 t.applySubtemplate(this.dataName, d, this.store.meta) :
16059 el.update(html.join(""));
16060 this.nodes = el.dom.childNodes;
16061 this.updateIndexes(0);
16066 * Function to override to reformat the data that is sent to
16067 * the template for each node.
16068 * DEPRICATED - use the preparedata event handler.
16069 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16070 * a JSON object for an UpdateManager bound view).
16072 prepareData : function(data, index, record)
16074 this.fireEvent("preparedata", this, data, index, record);
16078 onUpdate : function(ds, record){
16079 // Roo.log('on update');
16080 this.clearSelections();
16081 var index = this.store.indexOf(record);
16082 var n = this.nodes[index];
16083 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16084 n.parentNode.removeChild(n);
16085 this.updateIndexes(index, index);
16091 onAdd : function(ds, records, index)
16093 //Roo.log(['on Add', ds, records, index] );
16094 this.clearSelections();
16095 if(this.nodes.length == 0){
16099 var n = this.nodes[index];
16100 for(var i = 0, len = records.length; i < len; i++){
16101 var d = this.prepareData(records[i].data, i, records[i]);
16103 this.tpl.insertBefore(n, d);
16106 this.tpl.append(this.el, d);
16109 this.updateIndexes(index);
16112 onRemove : function(ds, record, index){
16113 // Roo.log('onRemove');
16114 this.clearSelections();
16115 var el = this.dataName ?
16116 this.el.child('.roo-tpl-' + this.dataName) :
16119 el.dom.removeChild(this.nodes[index]);
16120 this.updateIndexes(index);
16124 * Refresh an individual node.
16125 * @param {Number} index
16127 refreshNode : function(index){
16128 this.onUpdate(this.store, this.store.getAt(index));
16131 updateIndexes : function(startIndex, endIndex){
16132 var ns = this.nodes;
16133 startIndex = startIndex || 0;
16134 endIndex = endIndex || ns.length - 1;
16135 for(var i = startIndex; i <= endIndex; i++){
16136 ns[i].nodeIndex = i;
16141 * Changes the data store this view uses and refresh the view.
16142 * @param {Store} store
16144 setStore : function(store, initial){
16145 if(!initial && this.store){
16146 this.store.un("datachanged", this.refresh);
16147 this.store.un("add", this.onAdd);
16148 this.store.un("remove", this.onRemove);
16149 this.store.un("update", this.onUpdate);
16150 this.store.un("clear", this.refresh);
16151 this.store.un("beforeload", this.onBeforeLoad);
16152 this.store.un("load", this.onLoad);
16153 this.store.un("loadexception", this.onLoad);
16157 store.on("datachanged", this.refresh, this);
16158 store.on("add", this.onAdd, this);
16159 store.on("remove", this.onRemove, this);
16160 store.on("update", this.onUpdate, this);
16161 store.on("clear", this.refresh, this);
16162 store.on("beforeload", this.onBeforeLoad, this);
16163 store.on("load", this.onLoad, this);
16164 store.on("loadexception", this.onLoad, this);
16172 * onbeforeLoad - masks the loading area.
16175 onBeforeLoad : function(store,opts)
16177 //Roo.log('onBeforeLoad');
16179 this.el.update("");
16181 this.el.mask(this.mask ? this.mask : "Loading" );
16183 onLoad : function ()
16190 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16191 * @param {HTMLElement} node
16192 * @return {HTMLElement} The template node
16194 findItemFromChild : function(node){
16195 var el = this.dataName ?
16196 this.el.child('.roo-tpl-' + this.dataName,true) :
16199 if(!node || node.parentNode == el){
16202 var p = node.parentNode;
16203 while(p && p != el){
16204 if(p.parentNode == el){
16213 onClick : function(e){
16214 var item = this.findItemFromChild(e.getTarget());
16216 var index = this.indexOf(item);
16217 if(this.onItemClick(item, index, e) !== false){
16218 this.fireEvent("click", this, index, item, e);
16221 this.clearSelections();
16226 onContextMenu : function(e){
16227 var item = this.findItemFromChild(e.getTarget());
16229 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16234 onDblClick : function(e){
16235 var item = this.findItemFromChild(e.getTarget());
16237 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16241 onItemClick : function(item, index, e)
16243 if(this.fireEvent("beforeclick", this, index, item, e) === false){
16246 if (this.toggleSelect) {
16247 var m = this.isSelected(item) ? 'unselect' : 'select';
16250 _t[m](item, true, false);
16253 if(this.multiSelect || this.singleSelect){
16254 if(this.multiSelect && e.shiftKey && this.lastSelection){
16255 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16257 this.select(item, this.multiSelect && e.ctrlKey);
16258 this.lastSelection = item;
16261 if(!this.tickable){
16262 e.preventDefault();
16270 * Get the number of selected nodes.
16273 getSelectionCount : function(){
16274 return this.selections.length;
16278 * Get the currently selected nodes.
16279 * @return {Array} An array of HTMLElements
16281 getSelectedNodes : function(){
16282 return this.selections;
16286 * Get the indexes of the selected nodes.
16289 getSelectedIndexes : function(){
16290 var indexes = [], s = this.selections;
16291 for(var i = 0, len = s.length; i < len; i++){
16292 indexes.push(s[i].nodeIndex);
16298 * Clear all selections
16299 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16301 clearSelections : function(suppressEvent){
16302 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16303 this.cmp.elements = this.selections;
16304 this.cmp.removeClass(this.selectedClass);
16305 this.selections = [];
16306 if(!suppressEvent){
16307 this.fireEvent("selectionchange", this, this.selections);
16313 * Returns true if the passed node is selected
16314 * @param {HTMLElement/Number} node The node or node index
16315 * @return {Boolean}
16317 isSelected : function(node){
16318 var s = this.selections;
16322 node = this.getNode(node);
16323 return s.indexOf(node) !== -1;
16328 * @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
16329 * @param {Boolean} keepExisting (optional) true to keep existing selections
16330 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16332 select : function(nodeInfo, keepExisting, suppressEvent){
16333 if(nodeInfo instanceof Array){
16335 this.clearSelections(true);
16337 for(var i = 0, len = nodeInfo.length; i < len; i++){
16338 this.select(nodeInfo[i], true, true);
16342 var node = this.getNode(nodeInfo);
16343 if(!node || this.isSelected(node)){
16344 return; // already selected.
16347 this.clearSelections(true);
16350 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16351 Roo.fly(node).addClass(this.selectedClass);
16352 this.selections.push(node);
16353 if(!suppressEvent){
16354 this.fireEvent("selectionchange", this, this.selections);
16362 * @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
16363 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16364 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16366 unselect : function(nodeInfo, keepExisting, suppressEvent)
16368 if(nodeInfo instanceof Array){
16369 Roo.each(this.selections, function(s) {
16370 this.unselect(s, nodeInfo);
16374 var node = this.getNode(nodeInfo);
16375 if(!node || !this.isSelected(node)){
16376 //Roo.log("not selected");
16377 return; // not selected.
16381 Roo.each(this.selections, function(s) {
16383 Roo.fly(node).removeClass(this.selectedClass);
16390 this.selections= ns;
16391 this.fireEvent("selectionchange", this, this.selections);
16395 * Gets a template node.
16396 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16397 * @return {HTMLElement} The node or null if it wasn't found
16399 getNode : function(nodeInfo){
16400 if(typeof nodeInfo == "string"){
16401 return document.getElementById(nodeInfo);
16402 }else if(typeof nodeInfo == "number"){
16403 return this.nodes[nodeInfo];
16409 * Gets a range template nodes.
16410 * @param {Number} startIndex
16411 * @param {Number} endIndex
16412 * @return {Array} An array of nodes
16414 getNodes : function(start, end){
16415 var ns = this.nodes;
16416 start = start || 0;
16417 end = typeof end == "undefined" ? ns.length - 1 : end;
16420 for(var i = start; i <= end; i++){
16424 for(var i = start; i >= end; i--){
16432 * Finds the index of the passed node
16433 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16434 * @return {Number} The index of the node or -1
16436 indexOf : function(node){
16437 node = this.getNode(node);
16438 if(typeof node.nodeIndex == "number"){
16439 return node.nodeIndex;
16441 var ns = this.nodes;
16442 for(var i = 0, len = ns.length; i < len; i++){
16453 * based on jquery fullcalendar
16457 Roo.bootstrap = Roo.bootstrap || {};
16459 * @class Roo.bootstrap.Calendar
16460 * @extends Roo.bootstrap.Component
16461 * Bootstrap Calendar class
16462 * @cfg {Boolean} loadMask (true|false) default false
16463 * @cfg {Object} header generate the user specific header of the calendar, default false
16466 * Create a new Container
16467 * @param {Object} config The config object
16472 Roo.bootstrap.Calendar = function(config){
16473 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16477 * Fires when a date is selected
16478 * @param {DatePicker} this
16479 * @param {Date} date The selected date
16483 * @event monthchange
16484 * Fires when the displayed month changes
16485 * @param {DatePicker} this
16486 * @param {Date} date The selected month
16488 'monthchange': true,
16490 * @event evententer
16491 * Fires when mouse over an event
16492 * @param {Calendar} this
16493 * @param {event} Event
16495 'evententer': true,
16497 * @event eventleave
16498 * Fires when the mouse leaves an
16499 * @param {Calendar} this
16502 'eventleave': true,
16504 * @event eventclick
16505 * Fires when the mouse click an
16506 * @param {Calendar} this
16515 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
16518 * @cfg {Number} startDay
16519 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16527 getAutoCreate : function(){
16530 var fc_button = function(name, corner, style, content ) {
16531 return Roo.apply({},{
16533 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
16535 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16538 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16549 style : 'width:100%',
16556 cls : 'fc-header-left',
16558 fc_button('prev', 'left', 'arrow', '‹' ),
16559 fc_button('next', 'right', 'arrow', '›' ),
16560 { tag: 'span', cls: 'fc-header-space' },
16561 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
16569 cls : 'fc-header-center',
16573 cls: 'fc-header-title',
16576 html : 'month / year'
16584 cls : 'fc-header-right',
16586 /* fc_button('month', 'left', '', 'month' ),
16587 fc_button('week', '', '', 'week' ),
16588 fc_button('day', 'right', '', 'day' )
16600 header = this.header;
16603 var cal_heads = function() {
16605 // fixme - handle this.
16607 for (var i =0; i < Date.dayNames.length; i++) {
16608 var d = Date.dayNames[i];
16611 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16612 html : d.substring(0,3)
16616 ret[0].cls += ' fc-first';
16617 ret[6].cls += ' fc-last';
16620 var cal_cell = function(n) {
16623 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16628 cls: 'fc-day-number',
16632 cls: 'fc-day-content',
16636 style: 'position: relative;' // height: 17px;
16648 var cal_rows = function() {
16651 for (var r = 0; r < 6; r++) {
16658 for (var i =0; i < Date.dayNames.length; i++) {
16659 var d = Date.dayNames[i];
16660 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16663 row.cn[0].cls+=' fc-first';
16664 row.cn[0].cn[0].style = 'min-height:90px';
16665 row.cn[6].cls+=' fc-last';
16669 ret[0].cls += ' fc-first';
16670 ret[4].cls += ' fc-prev-last';
16671 ret[5].cls += ' fc-last';
16678 cls: 'fc-border-separate',
16679 style : 'width:100%',
16687 cls : 'fc-first fc-last',
16705 cls : 'fc-content',
16706 style : "position: relative;",
16709 cls : 'fc-view fc-view-month fc-grid',
16710 style : 'position: relative',
16711 unselectable : 'on',
16714 cls : 'fc-event-container',
16715 style : 'position:absolute;z-index:8;top:0;left:0;'
16733 initEvents : function()
16736 throw "can not find store for calendar";
16742 style: "text-align:center",
16746 style: "background-color:white;width:50%;margin:250 auto",
16750 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
16761 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16763 var size = this.el.select('.fc-content', true).first().getSize();
16764 this.maskEl.setSize(size.width, size.height);
16765 this.maskEl.enableDisplayMode("block");
16766 if(!this.loadMask){
16767 this.maskEl.hide();
16770 this.store = Roo.factory(this.store, Roo.data);
16771 this.store.on('load', this.onLoad, this);
16772 this.store.on('beforeload', this.onBeforeLoad, this);
16776 this.cells = this.el.select('.fc-day',true);
16777 //Roo.log(this.cells);
16778 this.textNodes = this.el.query('.fc-day-number');
16779 this.cells.addClassOnOver('fc-state-hover');
16781 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16782 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16783 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16784 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16786 this.on('monthchange', this.onMonthChange, this);
16788 this.update(new Date().clearTime());
16791 resize : function() {
16792 var sz = this.el.getSize();
16794 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16795 this.el.select('.fc-day-content div',true).setHeight(34);
16800 showPrevMonth : function(e){
16801 this.update(this.activeDate.add("mo", -1));
16803 showToday : function(e){
16804 this.update(new Date().clearTime());
16807 showNextMonth : function(e){
16808 this.update(this.activeDate.add("mo", 1));
16812 showPrevYear : function(){
16813 this.update(this.activeDate.add("y", -1));
16817 showNextYear : function(){
16818 this.update(this.activeDate.add("y", 1));
16823 update : function(date)
16825 var vd = this.activeDate;
16826 this.activeDate = date;
16827 // if(vd && this.el){
16828 // var t = date.getTime();
16829 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16830 // Roo.log('using add remove');
16832 // this.fireEvent('monthchange', this, date);
16834 // this.cells.removeClass("fc-state-highlight");
16835 // this.cells.each(function(c){
16836 // if(c.dateValue == t){
16837 // c.addClass("fc-state-highlight");
16838 // setTimeout(function(){
16839 // try{c.dom.firstChild.focus();}catch(e){}
16849 var days = date.getDaysInMonth();
16851 var firstOfMonth = date.getFirstDateOfMonth();
16852 var startingPos = firstOfMonth.getDay()-this.startDay;
16854 if(startingPos < this.startDay){
16858 var pm = date.add(Date.MONTH, -1);
16859 var prevStart = pm.getDaysInMonth()-startingPos;
16861 this.cells = this.el.select('.fc-day',true);
16862 this.textNodes = this.el.query('.fc-day-number');
16863 this.cells.addClassOnOver('fc-state-hover');
16865 var cells = this.cells.elements;
16866 var textEls = this.textNodes;
16868 Roo.each(cells, function(cell){
16869 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16872 days += startingPos;
16874 // convert everything to numbers so it's fast
16875 var day = 86400000;
16876 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16879 //Roo.log(prevStart);
16881 var today = new Date().clearTime().getTime();
16882 var sel = date.clearTime().getTime();
16883 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16884 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16885 var ddMatch = this.disabledDatesRE;
16886 var ddText = this.disabledDatesText;
16887 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16888 var ddaysText = this.disabledDaysText;
16889 var format = this.format;
16891 var setCellClass = function(cal, cell){
16895 //Roo.log('set Cell Class');
16897 var t = d.getTime();
16901 cell.dateValue = t;
16903 cell.className += " fc-today";
16904 cell.className += " fc-state-highlight";
16905 cell.title = cal.todayText;
16908 // disable highlight in other month..
16909 //cell.className += " fc-state-highlight";
16914 cell.className = " fc-state-disabled";
16915 cell.title = cal.minText;
16919 cell.className = " fc-state-disabled";
16920 cell.title = cal.maxText;
16924 if(ddays.indexOf(d.getDay()) != -1){
16925 cell.title = ddaysText;
16926 cell.className = " fc-state-disabled";
16929 if(ddMatch && format){
16930 var fvalue = d.dateFormat(format);
16931 if(ddMatch.test(fvalue)){
16932 cell.title = ddText.replace("%0", fvalue);
16933 cell.className = " fc-state-disabled";
16937 if (!cell.initialClassName) {
16938 cell.initialClassName = cell.dom.className;
16941 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16946 for(; i < startingPos; i++) {
16947 textEls[i].innerHTML = (++prevStart);
16948 d.setDate(d.getDate()+1);
16950 cells[i].className = "fc-past fc-other-month";
16951 setCellClass(this, cells[i]);
16956 for(; i < days; i++){
16957 intDay = i - startingPos + 1;
16958 textEls[i].innerHTML = (intDay);
16959 d.setDate(d.getDate()+1);
16961 cells[i].className = ''; // "x-date-active";
16962 setCellClass(this, cells[i]);
16966 for(; i < 42; i++) {
16967 textEls[i].innerHTML = (++extraDays);
16968 d.setDate(d.getDate()+1);
16970 cells[i].className = "fc-future fc-other-month";
16971 setCellClass(this, cells[i]);
16974 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16976 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16978 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16979 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16981 if(totalRows != 6){
16982 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16983 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16986 this.fireEvent('monthchange', this, date);
16990 if(!this.internalRender){
16991 var main = this.el.dom.firstChild;
16992 var w = main.offsetWidth;
16993 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16994 Roo.fly(main).setWidth(w);
16995 this.internalRender = true;
16996 // opera does not respect the auto grow header center column
16997 // then, after it gets a width opera refuses to recalculate
16998 // without a second pass
16999 if(Roo.isOpera && !this.secondPass){
17000 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17001 this.secondPass = true;
17002 this.update.defer(10, this, [date]);
17009 findCell : function(dt) {
17010 dt = dt.clearTime().getTime();
17012 this.cells.each(function(c){
17013 //Roo.log("check " +c.dateValue + '?=' + dt);
17014 if(c.dateValue == dt){
17024 findCells : function(ev) {
17025 var s = ev.start.clone().clearTime().getTime();
17027 var e= ev.end.clone().clearTime().getTime();
17030 this.cells.each(function(c){
17031 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17033 if(c.dateValue > e){
17036 if(c.dateValue < s){
17045 // findBestRow: function(cells)
17049 // for (var i =0 ; i < cells.length;i++) {
17050 // ret = Math.max(cells[i].rows || 0,ret);
17057 addItem : function(ev)
17059 // look for vertical location slot in
17060 var cells = this.findCells(ev);
17062 // ev.row = this.findBestRow(cells);
17064 // work out the location.
17068 for(var i =0; i < cells.length; i++) {
17070 cells[i].row = cells[0].row;
17073 cells[i].row = cells[i].row + 1;
17083 if (crow.start.getY() == cells[i].getY()) {
17085 crow.end = cells[i];
17102 cells[0].events.push(ev);
17104 this.calevents.push(ev);
17107 clearEvents: function() {
17109 if(!this.calevents){
17113 Roo.each(this.cells.elements, function(c){
17119 Roo.each(this.calevents, function(e) {
17120 Roo.each(e.els, function(el) {
17121 el.un('mouseenter' ,this.onEventEnter, this);
17122 el.un('mouseleave' ,this.onEventLeave, this);
17127 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17133 renderEvents: function()
17137 this.cells.each(function(c) {
17146 if(c.row != c.events.length){
17147 r = 4 - (4 - (c.row - c.events.length));
17150 c.events = ev.slice(0, r);
17151 c.more = ev.slice(r);
17153 if(c.more.length && c.more.length == 1){
17154 c.events.push(c.more.pop());
17157 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17161 this.cells.each(function(c) {
17163 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17166 for (var e = 0; e < c.events.length; e++){
17167 var ev = c.events[e];
17168 var rows = ev.rows;
17170 for(var i = 0; i < rows.length; i++) {
17172 // how many rows should it span..
17175 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17176 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17178 unselectable : "on",
17181 cls: 'fc-event-inner',
17185 // cls: 'fc-event-time',
17186 // html : cells.length > 1 ? '' : ev.time
17190 cls: 'fc-event-title',
17191 html : String.format('{0}', ev.title)
17198 cls: 'ui-resizable-handle ui-resizable-e',
17199 html : '  '
17206 cfg.cls += ' fc-event-start';
17208 if ((i+1) == rows.length) {
17209 cfg.cls += ' fc-event-end';
17212 var ctr = _this.el.select('.fc-event-container',true).first();
17213 var cg = ctr.createChild(cfg);
17215 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17216 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17218 var r = (c.more.length) ? 1 : 0;
17219 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
17220 cg.setWidth(ebox.right - sbox.x -2);
17222 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17223 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17224 cg.on('click', _this.onEventClick, _this, ev);
17235 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17236 style : 'position: absolute',
17237 unselectable : "on",
17240 cls: 'fc-event-inner',
17244 cls: 'fc-event-title',
17252 cls: 'ui-resizable-handle ui-resizable-e',
17253 html : '  '
17259 var ctr = _this.el.select('.fc-event-container',true).first();
17260 var cg = ctr.createChild(cfg);
17262 var sbox = c.select('.fc-day-content',true).first().getBox();
17263 var ebox = c.select('.fc-day-content',true).first().getBox();
17265 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
17266 cg.setWidth(ebox.right - sbox.x -2);
17268 cg.on('click', _this.onMoreEventClick, _this, c.more);
17278 onEventEnter: function (e, el,event,d) {
17279 this.fireEvent('evententer', this, el, event);
17282 onEventLeave: function (e, el,event,d) {
17283 this.fireEvent('eventleave', this, el, event);
17286 onEventClick: function (e, el,event,d) {
17287 this.fireEvent('eventclick', this, el, event);
17290 onMonthChange: function () {
17294 onMoreEventClick: function(e, el, more)
17298 this.calpopover.placement = 'right';
17299 this.calpopover.setTitle('More');
17301 this.calpopover.setContent('');
17303 var ctr = this.calpopover.el.select('.popover-content', true).first();
17305 Roo.each(more, function(m){
17307 cls : 'fc-event-hori fc-event-draggable',
17310 var cg = ctr.createChild(cfg);
17312 cg.on('click', _this.onEventClick, _this, m);
17315 this.calpopover.show(el);
17320 onLoad: function ()
17322 this.calevents = [];
17325 if(this.store.getCount() > 0){
17326 this.store.data.each(function(d){
17329 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17330 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17331 time : d.data.start_time,
17332 title : d.data.title,
17333 description : d.data.description,
17334 venue : d.data.venue
17339 this.renderEvents();
17341 if(this.calevents.length && this.loadMask){
17342 this.maskEl.hide();
17346 onBeforeLoad: function()
17348 this.clearEvents();
17350 this.maskEl.show();
17364 * @class Roo.bootstrap.Popover
17365 * @extends Roo.bootstrap.Component
17366 * Bootstrap Popover class
17367 * @cfg {String} html contents of the popover (or false to use children..)
17368 * @cfg {String} title of popover (or false to hide)
17369 * @cfg {String} placement how it is placed
17370 * @cfg {String} trigger click || hover (or false to trigger manually)
17371 * @cfg {String} over what (parent or false to trigger manually.)
17372 * @cfg {Number} delay - delay before showing
17375 * Create a new Popover
17376 * @param {Object} config The config object
17379 Roo.bootstrap.Popover = function(config){
17380 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17386 * After the popover show
17388 * @param {Roo.bootstrap.Popover} this
17393 * After the popover hide
17395 * @param {Roo.bootstrap.Popover} this
17401 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
17403 title: 'Fill in a title',
17406 placement : 'right',
17407 trigger : 'hover', // hover
17413 can_build_overlaid : false,
17415 getChildContainer : function()
17417 return this.el.select('.popover-content',true).first();
17420 getAutoCreate : function(){
17423 cls : 'popover roo-dynamic',
17424 style: 'display:block',
17430 cls : 'popover-inner',
17434 cls: 'popover-title',
17438 cls : 'popover-content',
17449 setTitle: function(str)
17452 this.el.select('.popover-title',true).first().dom.innerHTML = str;
17454 setContent: function(str)
17457 this.el.select('.popover-content',true).first().dom.innerHTML = str;
17459 // as it get's added to the bottom of the page.
17460 onRender : function(ct, position)
17462 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17464 var cfg = Roo.apply({}, this.getAutoCreate());
17468 cfg.cls += ' ' + this.cls;
17471 cfg.style = this.style;
17473 //Roo.log("adding to ");
17474 this.el = Roo.get(document.body).createChild(cfg, position);
17475 // Roo.log(this.el);
17480 initEvents : function()
17482 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17483 this.el.enableDisplayMode('block');
17485 if (this.over === false) {
17488 if (this.triggers === false) {
17491 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17492 var triggers = this.trigger ? this.trigger.split(' ') : [];
17493 Roo.each(triggers, function(trigger) {
17495 if (trigger == 'click') {
17496 on_el.on('click', this.toggle, this);
17497 } else if (trigger != 'manual') {
17498 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
17499 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17501 on_el.on(eventIn ,this.enter, this);
17502 on_el.on(eventOut, this.leave, this);
17513 toggle : function () {
17514 this.hoverState == 'in' ? this.leave() : this.enter();
17517 enter : function () {
17519 clearTimeout(this.timeout);
17521 this.hoverState = 'in';
17523 if (!this.delay || !this.delay.show) {
17528 this.timeout = setTimeout(function () {
17529 if (_t.hoverState == 'in') {
17532 }, this.delay.show)
17535 leave : function() {
17536 clearTimeout(this.timeout);
17538 this.hoverState = 'out';
17540 if (!this.delay || !this.delay.hide) {
17545 this.timeout = setTimeout(function () {
17546 if (_t.hoverState == 'out') {
17549 }, this.delay.hide)
17552 show : function (on_el)
17555 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17559 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17560 if (this.html !== false) {
17561 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17563 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17564 if (!this.title.length) {
17565 this.el.select('.popover-title',true).hide();
17568 var placement = typeof this.placement == 'function' ?
17569 this.placement.call(this, this.el, on_el) :
17572 var autoToken = /\s?auto?\s?/i;
17573 var autoPlace = autoToken.test(placement);
17575 placement = placement.replace(autoToken, '') || 'top';
17579 //this.el.setXY([0,0]);
17581 this.el.dom.style.display='block';
17582 this.el.addClass(placement);
17584 //this.el.appendTo(on_el);
17586 var p = this.getPosition();
17587 var box = this.el.getBox();
17592 var align = Roo.bootstrap.Popover.alignment[placement];
17595 this.el.alignTo(on_el, align[0],align[1]);
17596 //var arrow = this.el.select('.arrow',true).first();
17597 //arrow.set(align[2],
17599 this.el.addClass('in');
17602 if (this.el.hasClass('fade')) {
17606 this.hoverState = 'in';
17608 this.fireEvent('show', this);
17613 this.el.setXY([0,0]);
17614 this.el.removeClass('in');
17616 this.hoverState = null;
17618 this.fireEvent('hide', this);
17623 Roo.bootstrap.Popover.alignment = {
17624 'left' : ['r-l', [-10,0], 'right'],
17625 'right' : ['l-r', [10,0], 'left'],
17626 'bottom' : ['t-b', [0,10], 'top'],
17627 'top' : [ 'b-t', [0,-10], 'bottom']
17638 * @class Roo.bootstrap.Progress
17639 * @extends Roo.bootstrap.Component
17640 * Bootstrap Progress class
17641 * @cfg {Boolean} striped striped of the progress bar
17642 * @cfg {Boolean} active animated of the progress bar
17646 * Create a new Progress
17647 * @param {Object} config The config object
17650 Roo.bootstrap.Progress = function(config){
17651 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17654 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
17659 getAutoCreate : function(){
17667 cfg.cls += ' progress-striped';
17671 cfg.cls += ' active';
17690 * @class Roo.bootstrap.ProgressBar
17691 * @extends Roo.bootstrap.Component
17692 * Bootstrap ProgressBar class
17693 * @cfg {Number} aria_valuenow aria-value now
17694 * @cfg {Number} aria_valuemin aria-value min
17695 * @cfg {Number} aria_valuemax aria-value max
17696 * @cfg {String} label label for the progress bar
17697 * @cfg {String} panel (success | info | warning | danger )
17698 * @cfg {String} role role of the progress bar
17699 * @cfg {String} sr_only text
17703 * Create a new ProgressBar
17704 * @param {Object} config The config object
17707 Roo.bootstrap.ProgressBar = function(config){
17708 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17711 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
17715 aria_valuemax : 100,
17721 getAutoCreate : function()
17726 cls: 'progress-bar',
17727 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17739 cfg.role = this.role;
17742 if(this.aria_valuenow){
17743 cfg['aria-valuenow'] = this.aria_valuenow;
17746 if(this.aria_valuemin){
17747 cfg['aria-valuemin'] = this.aria_valuemin;
17750 if(this.aria_valuemax){
17751 cfg['aria-valuemax'] = this.aria_valuemax;
17754 if(this.label && !this.sr_only){
17755 cfg.html = this.label;
17759 cfg.cls += ' progress-bar-' + this.panel;
17765 update : function(aria_valuenow)
17767 this.aria_valuenow = aria_valuenow;
17769 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17784 * @class Roo.bootstrap.TabGroup
17785 * @extends Roo.bootstrap.Column
17786 * Bootstrap Column class
17787 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17788 * @cfg {Boolean} carousel true to make the group behave like a carousel
17789 * @cfg {Boolean} bullets show bullets for the panels
17790 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17791 * @cfg {Number} timer auto slide timer .. default 0 millisecond
17792 * @cfg {Boolean} showarrow (true|false) show arrow default true
17795 * Create a new TabGroup
17796 * @param {Object} config The config object
17799 Roo.bootstrap.TabGroup = function(config){
17800 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17802 this.navId = Roo.id();
17805 Roo.bootstrap.TabGroup.register(this);
17809 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
17812 transition : false,
17817 slideOnTouch : false,
17820 getAutoCreate : function()
17822 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17824 cfg.cls += ' tab-content';
17826 if (this.carousel) {
17827 cfg.cls += ' carousel slide';
17830 cls : 'carousel-inner',
17834 if(this.bullets && !Roo.isTouch){
17837 cls : 'carousel-bullets',
17841 if(this.bullets_cls){
17842 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17849 cfg.cn[0].cn.push(bullets);
17852 if(this.showarrow){
17853 cfg.cn[0].cn.push({
17855 class : 'carousel-arrow',
17859 class : 'carousel-prev',
17863 class : 'fa fa-chevron-left'
17869 class : 'carousel-next',
17873 class : 'fa fa-chevron-right'
17886 initEvents: function()
17888 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17889 // this.el.on("touchstart", this.onTouchStart, this);
17892 if(this.autoslide){
17895 this.slideFn = window.setInterval(function() {
17896 _this.showPanelNext();
17900 if(this.showarrow){
17901 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17902 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17908 // onTouchStart : function(e, el, o)
17910 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17914 // this.showPanelNext();
17918 getChildContainer : function()
17920 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17924 * register a Navigation item
17925 * @param {Roo.bootstrap.NavItem} the navitem to add
17927 register : function(item)
17929 this.tabs.push( item);
17930 item.navId = this.navId; // not really needed..
17935 getActivePanel : function()
17938 Roo.each(this.tabs, function(t) {
17948 getPanelByName : function(n)
17951 Roo.each(this.tabs, function(t) {
17952 if (t.tabId == n) {
17960 indexOfPanel : function(p)
17963 Roo.each(this.tabs, function(t,i) {
17964 if (t.tabId == p.tabId) {
17973 * show a specific panel
17974 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17975 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17977 showPanel : function (pan)
17979 if(this.transition || typeof(pan) == 'undefined'){
17980 Roo.log("waiting for the transitionend");
17984 if (typeof(pan) == 'number') {
17985 pan = this.tabs[pan];
17988 if (typeof(pan) == 'string') {
17989 pan = this.getPanelByName(pan);
17992 var cur = this.getActivePanel();
17995 Roo.log('pan or acitve pan is undefined');
17999 if (pan.tabId == this.getActivePanel().tabId) {
18003 if (false === cur.fireEvent('beforedeactivate')) {
18007 if(this.bullets > 0 && !Roo.isTouch){
18008 this.setActiveBullet(this.indexOfPanel(pan));
18011 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18013 this.transition = true;
18014 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
18015 var lr = dir == 'next' ? 'left' : 'right';
18016 pan.el.addClass(dir); // or prev
18017 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18018 cur.el.addClass(lr); // or right
18019 pan.el.addClass(lr);
18022 cur.el.on('transitionend', function() {
18023 Roo.log("trans end?");
18025 pan.el.removeClass([lr,dir]);
18026 pan.setActive(true);
18028 cur.el.removeClass([lr]);
18029 cur.setActive(false);
18031 _this.transition = false;
18033 }, this, { single: true } );
18038 cur.setActive(false);
18039 pan.setActive(true);
18044 showPanelNext : function()
18046 var i = this.indexOfPanel(this.getActivePanel());
18048 if (i >= this.tabs.length - 1 && !this.autoslide) {
18052 if (i >= this.tabs.length - 1 && this.autoslide) {
18056 this.showPanel(this.tabs[i+1]);
18059 showPanelPrev : function()
18061 var i = this.indexOfPanel(this.getActivePanel());
18063 if (i < 1 && !this.autoslide) {
18067 if (i < 1 && this.autoslide) {
18068 i = this.tabs.length;
18071 this.showPanel(this.tabs[i-1]);
18075 addBullet: function()
18077 if(!this.bullets || Roo.isTouch){
18080 var ctr = this.el.select('.carousel-bullets',true).first();
18081 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18082 var bullet = ctr.createChild({
18083 cls : 'bullet bullet-' + i
18084 },ctr.dom.lastChild);
18089 bullet.on('click', (function(e, el, o, ii, t){
18091 e.preventDefault();
18093 this.showPanel(ii);
18095 if(this.autoslide && this.slideFn){
18096 clearInterval(this.slideFn);
18097 this.slideFn = window.setInterval(function() {
18098 _this.showPanelNext();
18102 }).createDelegate(this, [i, bullet], true));
18107 setActiveBullet : function(i)
18113 Roo.each(this.el.select('.bullet', true).elements, function(el){
18114 el.removeClass('selected');
18117 var bullet = this.el.select('.bullet-' + i, true).first();
18123 bullet.addClass('selected');
18134 Roo.apply(Roo.bootstrap.TabGroup, {
18138 * register a Navigation Group
18139 * @param {Roo.bootstrap.NavGroup} the navgroup to add
18141 register : function(navgrp)
18143 this.groups[navgrp.navId] = navgrp;
18147 * fetch a Navigation Group based on the navigation ID
18148 * if one does not exist , it will get created.
18149 * @param {string} the navgroup to add
18150 * @returns {Roo.bootstrap.NavGroup} the navgroup
18152 get: function(navId) {
18153 if (typeof(this.groups[navId]) == 'undefined') {
18154 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18156 return this.groups[navId] ;
18171 * @class Roo.bootstrap.TabPanel
18172 * @extends Roo.bootstrap.Component
18173 * Bootstrap TabPanel class
18174 * @cfg {Boolean} active panel active
18175 * @cfg {String} html panel content
18176 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18177 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18178 * @cfg {String} href click to link..
18182 * Create a new TabPanel
18183 * @param {Object} config The config object
18186 Roo.bootstrap.TabPanel = function(config){
18187 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18191 * Fires when the active status changes
18192 * @param {Roo.bootstrap.TabPanel} this
18193 * @param {Boolean} state the new state
18198 * @event beforedeactivate
18199 * Fires before a tab is de-activated - can be used to do validation on a form.
18200 * @param {Roo.bootstrap.TabPanel} this
18201 * @return {Boolean} false if there is an error
18204 'beforedeactivate': true
18207 this.tabId = this.tabId || Roo.id();
18211 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
18219 getAutoCreate : function(){
18222 // item is needed for carousel - not sure if it has any effect otherwise
18223 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18224 html: this.html || ''
18228 cfg.cls += ' active';
18232 cfg.tabId = this.tabId;
18239 initEvents: function()
18241 var p = this.parent();
18243 this.navId = this.navId || p.navId;
18245 if (typeof(this.navId) != 'undefined') {
18246 // not really needed.. but just in case.. parent should be a NavGroup.
18247 var tg = Roo.bootstrap.TabGroup.get(this.navId);
18251 var i = tg.tabs.length - 1;
18253 if(this.active && tg.bullets > 0 && i < tg.bullets){
18254 tg.setActiveBullet(i);
18258 this.el.on('click', this.onClick, this);
18261 this.el.on("touchstart", this.onTouchStart, this);
18262 this.el.on("touchmove", this.onTouchMove, this);
18263 this.el.on("touchend", this.onTouchEnd, this);
18268 onRender : function(ct, position)
18270 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18273 setActive : function(state)
18275 Roo.log("panel - set active " + this.tabId + "=" + state);
18277 this.active = state;
18279 this.el.removeClass('active');
18281 } else if (!this.el.hasClass('active')) {
18282 this.el.addClass('active');
18285 this.fireEvent('changed', this, state);
18288 onClick : function(e)
18290 e.preventDefault();
18292 if(!this.href.length){
18296 window.location.href = this.href;
18305 onTouchStart : function(e)
18307 this.swiping = false;
18309 this.startX = e.browserEvent.touches[0].clientX;
18310 this.startY = e.browserEvent.touches[0].clientY;
18313 onTouchMove : function(e)
18315 this.swiping = true;
18317 this.endX = e.browserEvent.touches[0].clientX;
18318 this.endY = e.browserEvent.touches[0].clientY;
18321 onTouchEnd : function(e)
18328 var tabGroup = this.parent();
18330 if(this.endX > this.startX){ // swiping right
18331 tabGroup.showPanelPrev();
18335 if(this.startX > this.endX){ // swiping left
18336 tabGroup.showPanelNext();
18355 * @class Roo.bootstrap.DateField
18356 * @extends Roo.bootstrap.Input
18357 * Bootstrap DateField class
18358 * @cfg {Number} weekStart default 0
18359 * @cfg {String} viewMode default empty, (months|years)
18360 * @cfg {String} minViewMode default empty, (months|years)
18361 * @cfg {Number} startDate default -Infinity
18362 * @cfg {Number} endDate default Infinity
18363 * @cfg {Boolean} todayHighlight default false
18364 * @cfg {Boolean} todayBtn default false
18365 * @cfg {Boolean} calendarWeeks default false
18366 * @cfg {Object} daysOfWeekDisabled default empty
18367 * @cfg {Boolean} singleMode default false (true | false)
18369 * @cfg {Boolean} keyboardNavigation default true
18370 * @cfg {String} language default en
18373 * Create a new DateField
18374 * @param {Object} config The config object
18377 Roo.bootstrap.DateField = function(config){
18378 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18382 * Fires when this field show.
18383 * @param {Roo.bootstrap.DateField} this
18384 * @param {Mixed} date The date value
18389 * Fires when this field hide.
18390 * @param {Roo.bootstrap.DateField} this
18391 * @param {Mixed} date The date value
18396 * Fires when select a date.
18397 * @param {Roo.bootstrap.DateField} this
18398 * @param {Mixed} date The date value
18402 * @event beforeselect
18403 * Fires when before select a date.
18404 * @param {Roo.bootstrap.DateField} this
18405 * @param {Mixed} date The date value
18407 beforeselect : true
18411 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
18414 * @cfg {String} format
18415 * The default date format string which can be overriden for localization support. The format must be
18416 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18420 * @cfg {String} altFormats
18421 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18422 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18424 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18432 todayHighlight : false,
18438 keyboardNavigation: true,
18440 calendarWeeks: false,
18442 startDate: -Infinity,
18446 daysOfWeekDisabled: [],
18450 singleMode : false,
18452 UTCDate: function()
18454 return new Date(Date.UTC.apply(Date, arguments));
18457 UTCToday: function()
18459 var today = new Date();
18460 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18463 getDate: function() {
18464 var d = this.getUTCDate();
18465 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18468 getUTCDate: function() {
18472 setDate: function(d) {
18473 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18476 setUTCDate: function(d) {
18478 this.setValue(this.formatDate(this.date));
18481 onRender: function(ct, position)
18484 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18486 this.language = this.language || 'en';
18487 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18488 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18490 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18491 this.format = this.format || 'm/d/y';
18492 this.isInline = false;
18493 this.isInput = true;
18494 this.component = this.el.select('.add-on', true).first() || false;
18495 this.component = (this.component && this.component.length === 0) ? false : this.component;
18496 this.hasInput = this.component && this.inputEl().length;
18498 if (typeof(this.minViewMode === 'string')) {
18499 switch (this.minViewMode) {
18501 this.minViewMode = 1;
18504 this.minViewMode = 2;
18507 this.minViewMode = 0;
18512 if (typeof(this.viewMode === 'string')) {
18513 switch (this.viewMode) {
18526 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18528 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18530 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18532 this.picker().on('mousedown', this.onMousedown, this);
18533 this.picker().on('click', this.onClick, this);
18535 this.picker().addClass('datepicker-dropdown');
18537 this.startViewMode = this.viewMode;
18539 if(this.singleMode){
18540 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18541 v.setVisibilityMode(Roo.Element.DISPLAY);
18545 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18546 v.setStyle('width', '189px');
18550 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18551 if(!this.calendarWeeks){
18556 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18557 v.attr('colspan', function(i, val){
18558 return parseInt(val) + 1;
18563 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18565 this.setStartDate(this.startDate);
18566 this.setEndDate(this.endDate);
18568 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18575 if(this.isInline) {
18580 picker : function()
18582 return this.pickerEl;
18583 // return this.el.select('.datepicker', true).first();
18586 fillDow: function()
18588 var dowCnt = this.weekStart;
18597 if(this.calendarWeeks){
18605 while (dowCnt < this.weekStart + 7) {
18609 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18613 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18616 fillMonths: function()
18619 var months = this.picker().select('>.datepicker-months td', true).first();
18621 months.dom.innerHTML = '';
18627 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18630 months.createChild(month);
18637 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;
18639 if (this.date < this.startDate) {
18640 this.viewDate = new Date(this.startDate);
18641 } else if (this.date > this.endDate) {
18642 this.viewDate = new Date(this.endDate);
18644 this.viewDate = new Date(this.date);
18652 var d = new Date(this.viewDate),
18653 year = d.getUTCFullYear(),
18654 month = d.getUTCMonth(),
18655 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18656 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18657 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18658 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18659 currentDate = this.date && this.date.valueOf(),
18660 today = this.UTCToday();
18662 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18664 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18666 // this.picker.select('>tfoot th.today').
18667 // .text(dates[this.language].today)
18668 // .toggle(this.todayBtn !== false);
18670 this.updateNavArrows();
18673 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18675 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18677 prevMonth.setUTCDate(day);
18679 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18681 var nextMonth = new Date(prevMonth);
18683 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18685 nextMonth = nextMonth.valueOf();
18687 var fillMonths = false;
18689 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18691 while(prevMonth.valueOf() <= nextMonth) {
18694 if (prevMonth.getUTCDay() === this.weekStart) {
18696 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18704 if(this.calendarWeeks){
18705 // ISO 8601: First week contains first thursday.
18706 // ISO also states week starts on Monday, but we can be more abstract here.
18708 // Start of current week: based on weekstart/current date
18709 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18710 // Thursday of this week
18711 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18712 // First Thursday of year, year from thursday
18713 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18714 // Calendar week: ms between thursdays, div ms per day, div 7 days
18715 calWeek = (th - yth) / 864e5 / 7 + 1;
18717 fillMonths.cn.push({
18725 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18727 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18730 if (this.todayHighlight &&
18731 prevMonth.getUTCFullYear() == today.getFullYear() &&
18732 prevMonth.getUTCMonth() == today.getMonth() &&
18733 prevMonth.getUTCDate() == today.getDate()) {
18734 clsName += ' today';
18737 if (currentDate && prevMonth.valueOf() === currentDate) {
18738 clsName += ' active';
18741 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18742 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18743 clsName += ' disabled';
18746 fillMonths.cn.push({
18748 cls: 'day ' + clsName,
18749 html: prevMonth.getDate()
18752 prevMonth.setDate(prevMonth.getDate()+1);
18755 var currentYear = this.date && this.date.getUTCFullYear();
18756 var currentMonth = this.date && this.date.getUTCMonth();
18758 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18760 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18761 v.removeClass('active');
18763 if(currentYear === year && k === currentMonth){
18764 v.addClass('active');
18767 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18768 v.addClass('disabled');
18774 year = parseInt(year/10, 10) * 10;
18776 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18778 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18781 for (var i = -1; i < 11; i++) {
18782 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18784 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18792 showMode: function(dir)
18795 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18798 Roo.each(this.picker().select('>div',true).elements, function(v){
18799 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18802 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18807 if(this.isInline) {
18811 this.picker().removeClass(['bottom', 'top']);
18813 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18815 * place to the top of element!
18819 this.picker().addClass('top');
18820 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18825 this.picker().addClass('bottom');
18827 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18830 parseDate : function(value)
18832 if(!value || value instanceof Date){
18835 var v = Date.parseDate(value, this.format);
18836 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18837 v = Date.parseDate(value, 'Y-m-d');
18839 if(!v && this.altFormats){
18840 if(!this.altFormatsArray){
18841 this.altFormatsArray = this.altFormats.split("|");
18843 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18844 v = Date.parseDate(value, this.altFormatsArray[i]);
18850 formatDate : function(date, fmt)
18852 return (!date || !(date instanceof Date)) ?
18853 date : date.dateFormat(fmt || this.format);
18856 onFocus : function()
18858 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18862 onBlur : function()
18864 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18866 var d = this.inputEl().getValue();
18873 showPopup : function()
18875 this.picker().show();
18879 this.fireEvent('showpopup', this, this.date);
18882 hidePopup : function()
18884 if(this.isInline) {
18887 this.picker().hide();
18888 this.viewMode = this.startViewMode;
18891 this.fireEvent('hidepopup', this, this.date);
18895 onMousedown: function(e)
18897 e.stopPropagation();
18898 e.preventDefault();
18903 Roo.bootstrap.DateField.superclass.keyup.call(this);
18907 setValue: function(v)
18909 if(this.fireEvent('beforeselect', this, v) !== false){
18910 var d = new Date(this.parseDate(v) ).clearTime();
18912 if(isNaN(d.getTime())){
18913 this.date = this.viewDate = '';
18914 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18918 v = this.formatDate(d);
18920 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18922 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18926 this.fireEvent('select', this, this.date);
18930 getValue: function()
18932 return this.formatDate(this.date);
18935 fireKey: function(e)
18937 if (!this.picker().isVisible()){
18938 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18944 var dateChanged = false,
18946 newDate, newViewDate;
18951 e.preventDefault();
18955 if (!this.keyboardNavigation) {
18958 dir = e.keyCode == 37 ? -1 : 1;
18961 newDate = this.moveYear(this.date, dir);
18962 newViewDate = this.moveYear(this.viewDate, dir);
18963 } else if (e.shiftKey){
18964 newDate = this.moveMonth(this.date, dir);
18965 newViewDate = this.moveMonth(this.viewDate, dir);
18967 newDate = new Date(this.date);
18968 newDate.setUTCDate(this.date.getUTCDate() + dir);
18969 newViewDate = new Date(this.viewDate);
18970 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18972 if (this.dateWithinRange(newDate)){
18973 this.date = newDate;
18974 this.viewDate = newViewDate;
18975 this.setValue(this.formatDate(this.date));
18977 e.preventDefault();
18978 dateChanged = true;
18983 if (!this.keyboardNavigation) {
18986 dir = e.keyCode == 38 ? -1 : 1;
18988 newDate = this.moveYear(this.date, dir);
18989 newViewDate = this.moveYear(this.viewDate, dir);
18990 } else if (e.shiftKey){
18991 newDate = this.moveMonth(this.date, dir);
18992 newViewDate = this.moveMonth(this.viewDate, dir);
18994 newDate = new Date(this.date);
18995 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18996 newViewDate = new Date(this.viewDate);
18997 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18999 if (this.dateWithinRange(newDate)){
19000 this.date = newDate;
19001 this.viewDate = newViewDate;
19002 this.setValue(this.formatDate(this.date));
19004 e.preventDefault();
19005 dateChanged = true;
19009 this.setValue(this.formatDate(this.date));
19011 e.preventDefault();
19014 this.setValue(this.formatDate(this.date));
19028 onClick: function(e)
19030 e.stopPropagation();
19031 e.preventDefault();
19033 var target = e.getTarget();
19035 if(target.nodeName.toLowerCase() === 'i'){
19036 target = Roo.get(target).dom.parentNode;
19039 var nodeName = target.nodeName;
19040 var className = target.className;
19041 var html = target.innerHTML;
19042 //Roo.log(nodeName);
19044 switch(nodeName.toLowerCase()) {
19046 switch(className) {
19052 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19053 switch(this.viewMode){
19055 this.viewDate = this.moveMonth(this.viewDate, dir);
19059 this.viewDate = this.moveYear(this.viewDate, dir);
19065 var date = new Date();
19066 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19068 this.setValue(this.formatDate(this.date));
19075 if (className.indexOf('disabled') < 0) {
19076 this.viewDate.setUTCDate(1);
19077 if (className.indexOf('month') > -1) {
19078 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19080 var year = parseInt(html, 10) || 0;
19081 this.viewDate.setUTCFullYear(year);
19085 if(this.singleMode){
19086 this.setValue(this.formatDate(this.viewDate));
19097 //Roo.log(className);
19098 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19099 var day = parseInt(html, 10) || 1;
19100 var year = this.viewDate.getUTCFullYear(),
19101 month = this.viewDate.getUTCMonth();
19103 if (className.indexOf('old') > -1) {
19110 } else if (className.indexOf('new') > -1) {
19118 //Roo.log([year,month,day]);
19119 this.date = this.UTCDate(year, month, day,0,0,0,0);
19120 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19122 //Roo.log(this.formatDate(this.date));
19123 this.setValue(this.formatDate(this.date));
19130 setStartDate: function(startDate)
19132 this.startDate = startDate || -Infinity;
19133 if (this.startDate !== -Infinity) {
19134 this.startDate = this.parseDate(this.startDate);
19137 this.updateNavArrows();
19140 setEndDate: function(endDate)
19142 this.endDate = endDate || Infinity;
19143 if (this.endDate !== Infinity) {
19144 this.endDate = this.parseDate(this.endDate);
19147 this.updateNavArrows();
19150 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19152 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19153 if (typeof(this.daysOfWeekDisabled) !== 'object') {
19154 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19156 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19157 return parseInt(d, 10);
19160 this.updateNavArrows();
19163 updateNavArrows: function()
19165 if(this.singleMode){
19169 var d = new Date(this.viewDate),
19170 year = d.getUTCFullYear(),
19171 month = d.getUTCMonth();
19173 Roo.each(this.picker().select('.prev', true).elements, function(v){
19175 switch (this.viewMode) {
19178 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19184 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19191 Roo.each(this.picker().select('.next', true).elements, function(v){
19193 switch (this.viewMode) {
19196 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19202 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19210 moveMonth: function(date, dir)
19215 var new_date = new Date(date.valueOf()),
19216 day = new_date.getUTCDate(),
19217 month = new_date.getUTCMonth(),
19218 mag = Math.abs(dir),
19220 dir = dir > 0 ? 1 : -1;
19223 // If going back one month, make sure month is not current month
19224 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19226 return new_date.getUTCMonth() == month;
19228 // If going forward one month, make sure month is as expected
19229 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19231 return new_date.getUTCMonth() != new_month;
19233 new_month = month + dir;
19234 new_date.setUTCMonth(new_month);
19235 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19236 if (new_month < 0 || new_month > 11) {
19237 new_month = (new_month + 12) % 12;
19240 // For magnitudes >1, move one month at a time...
19241 for (var i=0; i<mag; i++) {
19242 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19243 new_date = this.moveMonth(new_date, dir);
19245 // ...then reset the day, keeping it in the new month
19246 new_month = new_date.getUTCMonth();
19247 new_date.setUTCDate(day);
19249 return new_month != new_date.getUTCMonth();
19252 // Common date-resetting loop -- if date is beyond end of month, make it
19255 new_date.setUTCDate(--day);
19256 new_date.setUTCMonth(new_month);
19261 moveYear: function(date, dir)
19263 return this.moveMonth(date, dir*12);
19266 dateWithinRange: function(date)
19268 return date >= this.startDate && date <= this.endDate;
19274 this.picker().remove();
19277 validateValue : function(value)
19279 if(this.getVisibilityEl().hasClass('hidden')){
19283 if(value.length < 1) {
19284 if(this.allowBlank){
19290 if(value.length < this.minLength){
19293 if(value.length > this.maxLength){
19297 var vt = Roo.form.VTypes;
19298 if(!vt[this.vtype](value, this)){
19302 if(typeof this.validator == "function"){
19303 var msg = this.validator(value);
19309 if(this.regex && !this.regex.test(value)){
19313 if(typeof(this.parseDate(value)) == 'undefined'){
19317 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19321 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19331 this.date = this.viewDate = '';
19333 Roo.bootstrap.DateField.superclass.setValue.call(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()){
26226 align = this.alignment[placement];
26229 this.el.alignTo(this.bindEl, align[0],align[1]);
26230 //var arrow = this.el.select('.arrow',true).first();
26231 //arrow.set(align[2],
26233 this.el.addClass(placement);
26235 this.el.addClass('in fade');
26237 this.hoverState = null;
26239 if (this.el.hasClass('fade')) {
26250 //this.el.setXY([0,0]);
26251 this.el.removeClass('in');
26267 * @class Roo.bootstrap.LocationPicker
26268 * @extends Roo.bootstrap.Component
26269 * Bootstrap LocationPicker class
26270 * @cfg {Number} latitude Position when init default 0
26271 * @cfg {Number} longitude Position when init default 0
26272 * @cfg {Number} zoom default 15
26273 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26274 * @cfg {Boolean} mapTypeControl default false
26275 * @cfg {Boolean} disableDoubleClickZoom default false
26276 * @cfg {Boolean} scrollwheel default true
26277 * @cfg {Boolean} streetViewControl default false
26278 * @cfg {Number} radius default 0
26279 * @cfg {String} locationName
26280 * @cfg {Boolean} draggable default true
26281 * @cfg {Boolean} enableAutocomplete default false
26282 * @cfg {Boolean} enableReverseGeocode default true
26283 * @cfg {String} markerTitle
26286 * Create a new LocationPicker
26287 * @param {Object} config The config object
26291 Roo.bootstrap.LocationPicker = function(config){
26293 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26298 * Fires when the picker initialized.
26299 * @param {Roo.bootstrap.LocationPicker} this
26300 * @param {Google Location} location
26304 * @event positionchanged
26305 * Fires when the picker position changed.
26306 * @param {Roo.bootstrap.LocationPicker} this
26307 * @param {Google Location} location
26309 positionchanged : true,
26312 * Fires when the map resize.
26313 * @param {Roo.bootstrap.LocationPicker} this
26318 * Fires when the map show.
26319 * @param {Roo.bootstrap.LocationPicker} this
26324 * Fires when the map hide.
26325 * @param {Roo.bootstrap.LocationPicker} this
26330 * Fires when click the map.
26331 * @param {Roo.bootstrap.LocationPicker} this
26332 * @param {Map event} e
26336 * @event mapRightClick
26337 * Fires when right click the map.
26338 * @param {Roo.bootstrap.LocationPicker} this
26339 * @param {Map event} e
26341 mapRightClick : true,
26343 * @event markerClick
26344 * Fires when click the marker.
26345 * @param {Roo.bootstrap.LocationPicker} this
26346 * @param {Map event} e
26348 markerClick : true,
26350 * @event markerRightClick
26351 * Fires when right click the marker.
26352 * @param {Roo.bootstrap.LocationPicker} this
26353 * @param {Map event} e
26355 markerRightClick : true,
26357 * @event OverlayViewDraw
26358 * Fires when OverlayView Draw
26359 * @param {Roo.bootstrap.LocationPicker} this
26361 OverlayViewDraw : true,
26363 * @event OverlayViewOnAdd
26364 * Fires when OverlayView Draw
26365 * @param {Roo.bootstrap.LocationPicker} this
26367 OverlayViewOnAdd : true,
26369 * @event OverlayViewOnRemove
26370 * Fires when OverlayView Draw
26371 * @param {Roo.bootstrap.LocationPicker} this
26373 OverlayViewOnRemove : true,
26375 * @event OverlayViewShow
26376 * Fires when OverlayView Draw
26377 * @param {Roo.bootstrap.LocationPicker} this
26378 * @param {Pixel} cpx
26380 OverlayViewShow : true,
26382 * @event OverlayViewHide
26383 * Fires when OverlayView Draw
26384 * @param {Roo.bootstrap.LocationPicker} this
26386 OverlayViewHide : true,
26388 * @event loadexception
26389 * Fires when load google lib failed.
26390 * @param {Roo.bootstrap.LocationPicker} this
26392 loadexception : true
26397 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
26399 gMapContext: false,
26405 mapTypeControl: false,
26406 disableDoubleClickZoom: false,
26408 streetViewControl: false,
26412 enableAutocomplete: false,
26413 enableReverseGeocode: true,
26416 getAutoCreate: function()
26421 cls: 'roo-location-picker'
26427 initEvents: function(ct, position)
26429 if(!this.el.getWidth() || this.isApplied()){
26433 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26438 initial: function()
26440 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26441 this.fireEvent('loadexception', this);
26445 if(!this.mapTypeId){
26446 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26449 this.gMapContext = this.GMapContext();
26451 this.initOverlayView();
26453 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26457 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26458 _this.setPosition(_this.gMapContext.marker.position);
26461 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26462 _this.fireEvent('mapClick', this, event);
26466 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26467 _this.fireEvent('mapRightClick', this, event);
26471 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26472 _this.fireEvent('markerClick', this, event);
26476 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26477 _this.fireEvent('markerRightClick', this, event);
26481 this.setPosition(this.gMapContext.location);
26483 this.fireEvent('initial', this, this.gMapContext.location);
26486 initOverlayView: function()
26490 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26494 _this.fireEvent('OverlayViewDraw', _this);
26499 _this.fireEvent('OverlayViewOnAdd', _this);
26502 onRemove: function()
26504 _this.fireEvent('OverlayViewOnRemove', _this);
26507 show: function(cpx)
26509 _this.fireEvent('OverlayViewShow', _this, cpx);
26514 _this.fireEvent('OverlayViewHide', _this);
26520 fromLatLngToContainerPixel: function(event)
26522 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26525 isApplied: function()
26527 return this.getGmapContext() == false ? false : true;
26530 getGmapContext: function()
26532 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26535 GMapContext: function()
26537 var position = new google.maps.LatLng(this.latitude, this.longitude);
26539 var _map = new google.maps.Map(this.el.dom, {
26542 mapTypeId: this.mapTypeId,
26543 mapTypeControl: this.mapTypeControl,
26544 disableDoubleClickZoom: this.disableDoubleClickZoom,
26545 scrollwheel: this.scrollwheel,
26546 streetViewControl: this.streetViewControl,
26547 locationName: this.locationName,
26548 draggable: this.draggable,
26549 enableAutocomplete: this.enableAutocomplete,
26550 enableReverseGeocode: this.enableReverseGeocode
26553 var _marker = new google.maps.Marker({
26554 position: position,
26556 title: this.markerTitle,
26557 draggable: this.draggable
26564 location: position,
26565 radius: this.radius,
26566 locationName: this.locationName,
26567 addressComponents: {
26568 formatted_address: null,
26569 addressLine1: null,
26570 addressLine2: null,
26572 streetNumber: null,
26576 stateOrProvince: null
26579 domContainer: this.el.dom,
26580 geodecoder: new google.maps.Geocoder()
26584 drawCircle: function(center, radius, options)
26586 if (this.gMapContext.circle != null) {
26587 this.gMapContext.circle.setMap(null);
26591 options = Roo.apply({}, options, {
26592 strokeColor: "#0000FF",
26593 strokeOpacity: .35,
26595 fillColor: "#0000FF",
26599 options.map = this.gMapContext.map;
26600 options.radius = radius;
26601 options.center = center;
26602 this.gMapContext.circle = new google.maps.Circle(options);
26603 return this.gMapContext.circle;
26609 setPosition: function(location)
26611 this.gMapContext.location = location;
26612 this.gMapContext.marker.setPosition(location);
26613 this.gMapContext.map.panTo(location);
26614 this.drawCircle(location, this.gMapContext.radius, {});
26618 if (this.gMapContext.settings.enableReverseGeocode) {
26619 this.gMapContext.geodecoder.geocode({
26620 latLng: this.gMapContext.location
26621 }, function(results, status) {
26623 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26624 _this.gMapContext.locationName = results[0].formatted_address;
26625 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26627 _this.fireEvent('positionchanged', this, location);
26634 this.fireEvent('positionchanged', this, location);
26639 google.maps.event.trigger(this.gMapContext.map, "resize");
26641 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26643 this.fireEvent('resize', this);
26646 setPositionByLatLng: function(latitude, longitude)
26648 this.setPosition(new google.maps.LatLng(latitude, longitude));
26651 getCurrentPosition: function()
26654 latitude: this.gMapContext.location.lat(),
26655 longitude: this.gMapContext.location.lng()
26659 getAddressName: function()
26661 return this.gMapContext.locationName;
26664 getAddressComponents: function()
26666 return this.gMapContext.addressComponents;
26669 address_component_from_google_geocode: function(address_components)
26673 for (var i = 0; i < address_components.length; i++) {
26674 var component = address_components[i];
26675 if (component.types.indexOf("postal_code") >= 0) {
26676 result.postalCode = component.short_name;
26677 } else if (component.types.indexOf("street_number") >= 0) {
26678 result.streetNumber = component.short_name;
26679 } else if (component.types.indexOf("route") >= 0) {
26680 result.streetName = component.short_name;
26681 } else if (component.types.indexOf("neighborhood") >= 0) {
26682 result.city = component.short_name;
26683 } else if (component.types.indexOf("locality") >= 0) {
26684 result.city = component.short_name;
26685 } else if (component.types.indexOf("sublocality") >= 0) {
26686 result.district = component.short_name;
26687 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26688 result.stateOrProvince = component.short_name;
26689 } else if (component.types.indexOf("country") >= 0) {
26690 result.country = component.short_name;
26694 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26695 result.addressLine2 = "";
26699 setZoomLevel: function(zoom)
26701 this.gMapContext.map.setZoom(zoom);
26714 this.fireEvent('show', this);
26725 this.fireEvent('hide', this);
26730 Roo.apply(Roo.bootstrap.LocationPicker, {
26732 OverlayView : function(map, options)
26734 options = options || {};
26748 * @class Roo.bootstrap.Alert
26749 * @extends Roo.bootstrap.Component
26750 * Bootstrap Alert class
26751 * @cfg {String} title The title of alert
26752 * @cfg {String} html The content of alert
26753 * @cfg {String} weight ( success | info | warning | danger )
26754 * @cfg {String} faicon font-awesomeicon
26757 * Create a new alert
26758 * @param {Object} config The config object
26762 Roo.bootstrap.Alert = function(config){
26763 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26767 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
26774 getAutoCreate : function()
26783 cls : 'roo-alert-icon'
26788 cls : 'roo-alert-title',
26793 cls : 'roo-alert-text',
26800 cfg.cn[0].cls += ' fa ' + this.faicon;
26804 cfg.cls += ' alert-' + this.weight;
26810 initEvents: function()
26812 this.el.setVisibilityMode(Roo.Element.DISPLAY);
26815 setTitle : function(str)
26817 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26820 setText : function(str)
26822 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26825 setWeight : function(weight)
26828 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26831 this.weight = weight;
26833 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26836 setIcon : function(icon)
26839 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26842 this.faicon = icon;
26844 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26865 * @class Roo.bootstrap.UploadCropbox
26866 * @extends Roo.bootstrap.Component
26867 * Bootstrap UploadCropbox class
26868 * @cfg {String} emptyText show when image has been loaded
26869 * @cfg {String} rotateNotify show when image too small to rotate
26870 * @cfg {Number} errorTimeout default 3000
26871 * @cfg {Number} minWidth default 300
26872 * @cfg {Number} minHeight default 300
26873 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26874 * @cfg {Boolean} isDocument (true|false) default false
26875 * @cfg {String} url action url
26876 * @cfg {String} paramName default 'imageUpload'
26877 * @cfg {String} method default POST
26878 * @cfg {Boolean} loadMask (true|false) default true
26879 * @cfg {Boolean} loadingText default 'Loading...'
26882 * Create a new UploadCropbox
26883 * @param {Object} config The config object
26886 Roo.bootstrap.UploadCropbox = function(config){
26887 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26891 * @event beforeselectfile
26892 * Fire before select file
26893 * @param {Roo.bootstrap.UploadCropbox} this
26895 "beforeselectfile" : true,
26898 * Fire after initEvent
26899 * @param {Roo.bootstrap.UploadCropbox} this
26904 * Fire after initEvent
26905 * @param {Roo.bootstrap.UploadCropbox} this
26906 * @param {String} data
26911 * Fire when preparing the file data
26912 * @param {Roo.bootstrap.UploadCropbox} this
26913 * @param {Object} file
26918 * Fire when get exception
26919 * @param {Roo.bootstrap.UploadCropbox} this
26920 * @param {XMLHttpRequest} xhr
26922 "exception" : true,
26924 * @event beforeloadcanvas
26925 * Fire before load the canvas
26926 * @param {Roo.bootstrap.UploadCropbox} this
26927 * @param {String} src
26929 "beforeloadcanvas" : true,
26932 * Fire when trash image
26933 * @param {Roo.bootstrap.UploadCropbox} this
26938 * Fire when download the image
26939 * @param {Roo.bootstrap.UploadCropbox} this
26943 * @event footerbuttonclick
26944 * Fire when footerbuttonclick
26945 * @param {Roo.bootstrap.UploadCropbox} this
26946 * @param {String} type
26948 "footerbuttonclick" : true,
26952 * @param {Roo.bootstrap.UploadCropbox} this
26957 * Fire when rotate the image
26958 * @param {Roo.bootstrap.UploadCropbox} this
26959 * @param {String} pos
26964 * Fire when inspect the file
26965 * @param {Roo.bootstrap.UploadCropbox} this
26966 * @param {Object} file
26971 * Fire when xhr upload the file
26972 * @param {Roo.bootstrap.UploadCropbox} this
26973 * @param {Object} data
26978 * Fire when arrange the file data
26979 * @param {Roo.bootstrap.UploadCropbox} this
26980 * @param {Object} formData
26985 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26988 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
26990 emptyText : 'Click to upload image',
26991 rotateNotify : 'Image is too small to rotate',
26992 errorTimeout : 3000,
27006 cropType : 'image/jpeg',
27008 canvasLoaded : false,
27009 isDocument : false,
27011 paramName : 'imageUpload',
27013 loadingText : 'Loading...',
27016 getAutoCreate : function()
27020 cls : 'roo-upload-cropbox',
27024 cls : 'roo-upload-cropbox-selector',
27029 cls : 'roo-upload-cropbox-body',
27030 style : 'cursor:pointer',
27034 cls : 'roo-upload-cropbox-preview'
27038 cls : 'roo-upload-cropbox-thumb'
27042 cls : 'roo-upload-cropbox-empty-notify',
27043 html : this.emptyText
27047 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27048 html : this.rotateNotify
27054 cls : 'roo-upload-cropbox-footer',
27057 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27067 onRender : function(ct, position)
27069 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27071 if (this.buttons.length) {
27073 Roo.each(this.buttons, function(bb) {
27075 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27077 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27083 this.maskEl = this.el;
27087 initEvents : function()
27089 this.urlAPI = (window.createObjectURL && window) ||
27090 (window.URL && URL.revokeObjectURL && URL) ||
27091 (window.webkitURL && webkitURL);
27093 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27094 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27096 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27097 this.selectorEl.hide();
27099 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27100 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27102 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27103 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104 this.thumbEl.hide();
27106 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27107 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27109 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27110 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27111 this.errorEl.hide();
27113 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27114 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27115 this.footerEl.hide();
27117 this.setThumbBoxSize();
27123 this.fireEvent('initial', this);
27130 window.addEventListener("resize", function() { _this.resize(); } );
27132 this.bodyEl.on('click', this.beforeSelectFile, this);
27135 this.bodyEl.on('touchstart', this.onTouchStart, this);
27136 this.bodyEl.on('touchmove', this.onTouchMove, this);
27137 this.bodyEl.on('touchend', this.onTouchEnd, this);
27141 this.bodyEl.on('mousedown', this.onMouseDown, this);
27142 this.bodyEl.on('mousemove', this.onMouseMove, this);
27143 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27144 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27145 Roo.get(document).on('mouseup', this.onMouseUp, this);
27148 this.selectorEl.on('change', this.onFileSelected, this);
27154 this.baseScale = 1;
27156 this.baseRotate = 1;
27157 this.dragable = false;
27158 this.pinching = false;
27161 this.cropData = false;
27162 this.notifyEl.dom.innerHTML = this.emptyText;
27164 this.selectorEl.dom.value = '';
27168 resize : function()
27170 if(this.fireEvent('resize', this) != false){
27171 this.setThumbBoxPosition();
27172 this.setCanvasPosition();
27176 onFooterButtonClick : function(e, el, o, type)
27179 case 'rotate-left' :
27180 this.onRotateLeft(e);
27182 case 'rotate-right' :
27183 this.onRotateRight(e);
27186 this.beforeSelectFile(e);
27201 this.fireEvent('footerbuttonclick', this, type);
27204 beforeSelectFile : function(e)
27206 e.preventDefault();
27208 if(this.fireEvent('beforeselectfile', this) != false){
27209 this.selectorEl.dom.click();
27213 onFileSelected : function(e)
27215 e.preventDefault();
27217 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27221 var file = this.selectorEl.dom.files[0];
27223 if(this.fireEvent('inspect', this, file) != false){
27224 this.prepare(file);
27229 trash : function(e)
27231 this.fireEvent('trash', this);
27234 download : function(e)
27236 this.fireEvent('download', this);
27239 loadCanvas : function(src)
27241 if(this.fireEvent('beforeloadcanvas', this, src) != false){
27245 this.imageEl = document.createElement('img');
27249 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27251 this.imageEl.src = src;
27255 onLoadCanvas : function()
27257 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27258 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27260 this.bodyEl.un('click', this.beforeSelectFile, this);
27262 this.notifyEl.hide();
27263 this.thumbEl.show();
27264 this.footerEl.show();
27266 this.baseRotateLevel();
27268 if(this.isDocument){
27269 this.setThumbBoxSize();
27272 this.setThumbBoxPosition();
27274 this.baseScaleLevel();
27280 this.canvasLoaded = true;
27283 this.maskEl.unmask();
27288 setCanvasPosition : function()
27290 if(!this.canvasEl){
27294 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27295 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27297 this.previewEl.setLeft(pw);
27298 this.previewEl.setTop(ph);
27302 onMouseDown : function(e)
27306 this.dragable = true;
27307 this.pinching = false;
27309 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27310 this.dragable = false;
27314 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27315 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27319 onMouseMove : function(e)
27323 if(!this.canvasLoaded){
27327 if (!this.dragable){
27331 var minX = Math.ceil(this.thumbEl.getLeft(true));
27332 var minY = Math.ceil(this.thumbEl.getTop(true));
27334 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27335 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27337 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27338 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27340 x = x - this.mouseX;
27341 y = y - this.mouseY;
27343 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27344 var bgY = Math.ceil(y + this.previewEl.getTop(true));
27346 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27347 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27349 this.previewEl.setLeft(bgX);
27350 this.previewEl.setTop(bgY);
27352 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27353 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27356 onMouseUp : function(e)
27360 this.dragable = false;
27363 onMouseWheel : function(e)
27367 this.startScale = this.scale;
27369 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27371 if(!this.zoomable()){
27372 this.scale = this.startScale;
27381 zoomable : function()
27383 var minScale = this.thumbEl.getWidth() / this.minWidth;
27385 if(this.minWidth < this.minHeight){
27386 minScale = this.thumbEl.getHeight() / this.minHeight;
27389 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27390 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27394 (this.rotate == 0 || this.rotate == 180) &&
27396 width > this.imageEl.OriginWidth ||
27397 height > this.imageEl.OriginHeight ||
27398 (width < this.minWidth && height < this.minHeight)
27406 (this.rotate == 90 || this.rotate == 270) &&
27408 width > this.imageEl.OriginWidth ||
27409 height > this.imageEl.OriginHeight ||
27410 (width < this.minHeight && height < this.minWidth)
27417 !this.isDocument &&
27418 (this.rotate == 0 || this.rotate == 180) &&
27420 width < this.minWidth ||
27421 width > this.imageEl.OriginWidth ||
27422 height < this.minHeight ||
27423 height > this.imageEl.OriginHeight
27430 !this.isDocument &&
27431 (this.rotate == 90 || this.rotate == 270) &&
27433 width < this.minHeight ||
27434 width > this.imageEl.OriginWidth ||
27435 height < this.minWidth ||
27436 height > this.imageEl.OriginHeight
27446 onRotateLeft : function(e)
27448 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27450 var minScale = this.thumbEl.getWidth() / this.minWidth;
27452 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27453 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27455 this.startScale = this.scale;
27457 while (this.getScaleLevel() < minScale){
27459 this.scale = this.scale + 1;
27461 if(!this.zoomable()){
27466 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27467 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27472 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27479 this.scale = this.startScale;
27481 this.onRotateFail();
27486 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27488 if(this.isDocument){
27489 this.setThumbBoxSize();
27490 this.setThumbBoxPosition();
27491 this.setCanvasPosition();
27496 this.fireEvent('rotate', this, 'left');
27500 onRotateRight : function(e)
27502 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27504 var minScale = this.thumbEl.getWidth() / this.minWidth;
27506 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27507 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27509 this.startScale = this.scale;
27511 while (this.getScaleLevel() < minScale){
27513 this.scale = this.scale + 1;
27515 if(!this.zoomable()){
27520 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27521 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27526 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27533 this.scale = this.startScale;
27535 this.onRotateFail();
27540 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27542 if(this.isDocument){
27543 this.setThumbBoxSize();
27544 this.setThumbBoxPosition();
27545 this.setCanvasPosition();
27550 this.fireEvent('rotate', this, 'right');
27553 onRotateFail : function()
27555 this.errorEl.show(true);
27559 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27564 this.previewEl.dom.innerHTML = '';
27566 var canvasEl = document.createElement("canvas");
27568 var contextEl = canvasEl.getContext("2d");
27570 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27571 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27572 var center = this.imageEl.OriginWidth / 2;
27574 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27575 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27576 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27577 center = this.imageEl.OriginHeight / 2;
27580 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27582 contextEl.translate(center, center);
27583 contextEl.rotate(this.rotate * Math.PI / 180);
27585 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27587 this.canvasEl = document.createElement("canvas");
27589 this.contextEl = this.canvasEl.getContext("2d");
27591 switch (this.rotate) {
27594 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27595 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27597 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27602 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27603 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27605 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27606 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);
27610 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27615 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27616 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27618 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27619 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);
27623 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27628 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27629 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27631 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27632 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27636 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);
27643 this.previewEl.appendChild(this.canvasEl);
27645 this.setCanvasPosition();
27650 if(!this.canvasLoaded){
27654 var imageCanvas = document.createElement("canvas");
27656 var imageContext = imageCanvas.getContext("2d");
27658 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27659 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27661 var center = imageCanvas.width / 2;
27663 imageContext.translate(center, center);
27665 imageContext.rotate(this.rotate * Math.PI / 180);
27667 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27669 var canvas = document.createElement("canvas");
27671 var context = canvas.getContext("2d");
27673 canvas.width = this.minWidth;
27674 canvas.height = this.minHeight;
27676 switch (this.rotate) {
27679 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27680 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27682 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27683 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27685 var targetWidth = this.minWidth - 2 * x;
27686 var targetHeight = this.minHeight - 2 * y;
27690 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27691 scale = targetWidth / width;
27694 if(x > 0 && y == 0){
27695 scale = targetHeight / height;
27698 if(x > 0 && y > 0){
27699 scale = targetWidth / width;
27701 if(width < height){
27702 scale = targetHeight / height;
27706 context.scale(scale, scale);
27708 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27709 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27711 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27712 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27714 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27719 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27720 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27722 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27723 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27725 var targetWidth = this.minWidth - 2 * x;
27726 var targetHeight = this.minHeight - 2 * y;
27730 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27731 scale = targetWidth / width;
27734 if(x > 0 && y == 0){
27735 scale = targetHeight / height;
27738 if(x > 0 && y > 0){
27739 scale = targetWidth / width;
27741 if(width < height){
27742 scale = targetHeight / height;
27746 context.scale(scale, scale);
27748 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27749 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27751 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27752 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27754 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27756 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27761 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27762 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27764 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27765 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27767 var targetWidth = this.minWidth - 2 * x;
27768 var targetHeight = this.minHeight - 2 * y;
27772 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27773 scale = targetWidth / width;
27776 if(x > 0 && y == 0){
27777 scale = targetHeight / height;
27780 if(x > 0 && y > 0){
27781 scale = targetWidth / width;
27783 if(width < height){
27784 scale = targetHeight / height;
27788 context.scale(scale, scale);
27790 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27791 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27793 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27794 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27796 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27797 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27799 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27804 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27805 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27807 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27808 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27810 var targetWidth = this.minWidth - 2 * x;
27811 var targetHeight = this.minHeight - 2 * y;
27815 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27816 scale = targetWidth / width;
27819 if(x > 0 && y == 0){
27820 scale = targetHeight / height;
27823 if(x > 0 && y > 0){
27824 scale = targetWidth / width;
27826 if(width < height){
27827 scale = targetHeight / height;
27831 context.scale(scale, scale);
27833 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27834 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27836 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27837 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27839 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27841 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27848 this.cropData = canvas.toDataURL(this.cropType);
27850 if(this.fireEvent('crop', this, this.cropData) !== false){
27851 this.process(this.file, this.cropData);
27858 setThumbBoxSize : function()
27862 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27863 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27864 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27866 this.minWidth = width;
27867 this.minHeight = height;
27869 if(this.rotate == 90 || this.rotate == 270){
27870 this.minWidth = height;
27871 this.minHeight = width;
27876 width = Math.ceil(this.minWidth * height / this.minHeight);
27878 if(this.minWidth > this.minHeight){
27880 height = Math.ceil(this.minHeight * width / this.minWidth);
27883 this.thumbEl.setStyle({
27884 width : width + 'px',
27885 height : height + 'px'
27892 setThumbBoxPosition : function()
27894 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27895 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27897 this.thumbEl.setLeft(x);
27898 this.thumbEl.setTop(y);
27902 baseRotateLevel : function()
27904 this.baseRotate = 1;
27907 typeof(this.exif) != 'undefined' &&
27908 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27909 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27911 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27914 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27918 baseScaleLevel : function()
27922 if(this.isDocument){
27924 if(this.baseRotate == 6 || this.baseRotate == 8){
27926 height = this.thumbEl.getHeight();
27927 this.baseScale = height / this.imageEl.OriginWidth;
27929 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27930 width = this.thumbEl.getWidth();
27931 this.baseScale = width / this.imageEl.OriginHeight;
27937 height = this.thumbEl.getHeight();
27938 this.baseScale = height / this.imageEl.OriginHeight;
27940 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27941 width = this.thumbEl.getWidth();
27942 this.baseScale = width / this.imageEl.OriginWidth;
27948 if(this.baseRotate == 6 || this.baseRotate == 8){
27950 width = this.thumbEl.getHeight();
27951 this.baseScale = width / this.imageEl.OriginHeight;
27953 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27954 height = this.thumbEl.getWidth();
27955 this.baseScale = height / this.imageEl.OriginHeight;
27958 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27959 height = this.thumbEl.getWidth();
27960 this.baseScale = height / this.imageEl.OriginHeight;
27962 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27963 width = this.thumbEl.getHeight();
27964 this.baseScale = width / this.imageEl.OriginWidth;
27971 width = this.thumbEl.getWidth();
27972 this.baseScale = width / this.imageEl.OriginWidth;
27974 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27975 height = this.thumbEl.getHeight();
27976 this.baseScale = height / this.imageEl.OriginHeight;
27979 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27981 height = this.thumbEl.getHeight();
27982 this.baseScale = height / this.imageEl.OriginHeight;
27984 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27985 width = this.thumbEl.getWidth();
27986 this.baseScale = width / this.imageEl.OriginWidth;
27994 getScaleLevel : function()
27996 return this.baseScale * Math.pow(1.1, this.scale);
27999 onTouchStart : function(e)
28001 if(!this.canvasLoaded){
28002 this.beforeSelectFile(e);
28006 var touches = e.browserEvent.touches;
28012 if(touches.length == 1){
28013 this.onMouseDown(e);
28017 if(touches.length != 2){
28023 for(var i = 0, finger; finger = touches[i]; i++){
28024 coords.push(finger.pageX, finger.pageY);
28027 var x = Math.pow(coords[0] - coords[2], 2);
28028 var y = Math.pow(coords[1] - coords[3], 2);
28030 this.startDistance = Math.sqrt(x + y);
28032 this.startScale = this.scale;
28034 this.pinching = true;
28035 this.dragable = false;
28039 onTouchMove : function(e)
28041 if(!this.pinching && !this.dragable){
28045 var touches = e.browserEvent.touches;
28052 this.onMouseMove(e);
28058 for(var i = 0, finger; finger = touches[i]; i++){
28059 coords.push(finger.pageX, finger.pageY);
28062 var x = Math.pow(coords[0] - coords[2], 2);
28063 var y = Math.pow(coords[1] - coords[3], 2);
28065 this.endDistance = Math.sqrt(x + y);
28067 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28069 if(!this.zoomable()){
28070 this.scale = this.startScale;
28078 onTouchEnd : function(e)
28080 this.pinching = false;
28081 this.dragable = false;
28085 process : function(file, crop)
28088 this.maskEl.mask(this.loadingText);
28091 this.xhr = new XMLHttpRequest();
28093 file.xhr = this.xhr;
28095 this.xhr.open(this.method, this.url, true);
28098 "Accept": "application/json",
28099 "Cache-Control": "no-cache",
28100 "X-Requested-With": "XMLHttpRequest"
28103 for (var headerName in headers) {
28104 var headerValue = headers[headerName];
28106 this.xhr.setRequestHeader(headerName, headerValue);
28112 this.xhr.onload = function()
28114 _this.xhrOnLoad(_this.xhr);
28117 this.xhr.onerror = function()
28119 _this.xhrOnError(_this.xhr);
28122 var formData = new FormData();
28124 formData.append('returnHTML', 'NO');
28127 formData.append('crop', crop);
28130 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28131 formData.append(this.paramName, file, file.name);
28134 if(typeof(file.filename) != 'undefined'){
28135 formData.append('filename', file.filename);
28138 if(typeof(file.mimetype) != 'undefined'){
28139 formData.append('mimetype', file.mimetype);
28142 if(this.fireEvent('arrange', this, formData) != false){
28143 this.xhr.send(formData);
28147 xhrOnLoad : function(xhr)
28150 this.maskEl.unmask();
28153 if (xhr.readyState !== 4) {
28154 this.fireEvent('exception', this, xhr);
28158 var response = Roo.decode(xhr.responseText);
28160 if(!response.success){
28161 this.fireEvent('exception', this, xhr);
28165 var response = Roo.decode(xhr.responseText);
28167 this.fireEvent('upload', this, response);
28171 xhrOnError : function()
28174 this.maskEl.unmask();
28177 Roo.log('xhr on error');
28179 var response = Roo.decode(xhr.responseText);
28185 prepare : function(file)
28188 this.maskEl.mask(this.loadingText);
28194 if(typeof(file) === 'string'){
28195 this.loadCanvas(file);
28199 if(!file || !this.urlAPI){
28204 this.cropType = file.type;
28208 if(this.fireEvent('prepare', this, this.file) != false){
28210 var reader = new FileReader();
28212 reader.onload = function (e) {
28213 if (e.target.error) {
28214 Roo.log(e.target.error);
28218 var buffer = e.target.result,
28219 dataView = new DataView(buffer),
28221 maxOffset = dataView.byteLength - 4,
28225 if (dataView.getUint16(0) === 0xffd8) {
28226 while (offset < maxOffset) {
28227 markerBytes = dataView.getUint16(offset);
28229 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28230 markerLength = dataView.getUint16(offset + 2) + 2;
28231 if (offset + markerLength > dataView.byteLength) {
28232 Roo.log('Invalid meta data: Invalid segment size.');
28236 if(markerBytes == 0xffe1){
28237 _this.parseExifData(
28244 offset += markerLength;
28254 var url = _this.urlAPI.createObjectURL(_this.file);
28256 _this.loadCanvas(url);
28261 reader.readAsArrayBuffer(this.file);
28267 parseExifData : function(dataView, offset, length)
28269 var tiffOffset = offset + 10,
28273 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28274 // No Exif data, might be XMP data instead
28278 // Check for the ASCII code for "Exif" (0x45786966):
28279 if (dataView.getUint32(offset + 4) !== 0x45786966) {
28280 // No Exif data, might be XMP data instead
28283 if (tiffOffset + 8 > dataView.byteLength) {
28284 Roo.log('Invalid Exif data: Invalid segment size.');
28287 // Check for the two null bytes:
28288 if (dataView.getUint16(offset + 8) !== 0x0000) {
28289 Roo.log('Invalid Exif data: Missing byte alignment offset.');
28292 // Check the byte alignment:
28293 switch (dataView.getUint16(tiffOffset)) {
28295 littleEndian = true;
28298 littleEndian = false;
28301 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28304 // Check for the TIFF tag marker (0x002A):
28305 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28306 Roo.log('Invalid Exif data: Missing TIFF marker.');
28309 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28310 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28312 this.parseExifTags(
28315 tiffOffset + dirOffset,
28320 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28325 if (dirOffset + 6 > dataView.byteLength) {
28326 Roo.log('Invalid Exif data: Invalid directory offset.');
28329 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28330 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28331 if (dirEndOffset + 4 > dataView.byteLength) {
28332 Roo.log('Invalid Exif data: Invalid directory size.');
28335 for (i = 0; i < tagsNumber; i += 1) {
28339 dirOffset + 2 + 12 * i, // tag offset
28343 // Return the offset to the next directory:
28344 return dataView.getUint32(dirEndOffset, littleEndian);
28347 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
28349 var tag = dataView.getUint16(offset, littleEndian);
28351 this.exif[tag] = this.getExifValue(
28355 dataView.getUint16(offset + 2, littleEndian), // tag type
28356 dataView.getUint32(offset + 4, littleEndian), // tag length
28361 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28363 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28372 Roo.log('Invalid Exif data: Invalid tag type.');
28376 tagSize = tagType.size * length;
28377 // Determine if the value is contained in the dataOffset bytes,
28378 // or if the value at the dataOffset is a pointer to the actual data:
28379 dataOffset = tagSize > 4 ?
28380 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28381 if (dataOffset + tagSize > dataView.byteLength) {
28382 Roo.log('Invalid Exif data: Invalid data offset.');
28385 if (length === 1) {
28386 return tagType.getValue(dataView, dataOffset, littleEndian);
28389 for (i = 0; i < length; i += 1) {
28390 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28393 if (tagType.ascii) {
28395 // Concatenate the chars:
28396 for (i = 0; i < values.length; i += 1) {
28398 // Ignore the terminating NULL byte(s):
28399 if (c === '\u0000') {
28411 Roo.apply(Roo.bootstrap.UploadCropbox, {
28413 'Orientation': 0x0112
28417 1: 0, //'top-left',
28419 3: 180, //'bottom-right',
28420 // 4: 'bottom-left',
28422 6: 90, //'right-top',
28423 // 7: 'right-bottom',
28424 8: 270 //'left-bottom'
28428 // byte, 8-bit unsigned int:
28430 getValue: function (dataView, dataOffset) {
28431 return dataView.getUint8(dataOffset);
28435 // ascii, 8-bit byte:
28437 getValue: function (dataView, dataOffset) {
28438 return String.fromCharCode(dataView.getUint8(dataOffset));
28443 // short, 16 bit int:
28445 getValue: function (dataView, dataOffset, littleEndian) {
28446 return dataView.getUint16(dataOffset, littleEndian);
28450 // long, 32 bit int:
28452 getValue: function (dataView, dataOffset, littleEndian) {
28453 return dataView.getUint32(dataOffset, littleEndian);
28457 // rational = two long values, first is numerator, second is denominator:
28459 getValue: function (dataView, dataOffset, littleEndian) {
28460 return dataView.getUint32(dataOffset, littleEndian) /
28461 dataView.getUint32(dataOffset + 4, littleEndian);
28465 // slong, 32 bit signed int:
28467 getValue: function (dataView, dataOffset, littleEndian) {
28468 return dataView.getInt32(dataOffset, littleEndian);
28472 // srational, two slongs, first is numerator, second is denominator:
28474 getValue: function (dataView, dataOffset, littleEndian) {
28475 return dataView.getInt32(dataOffset, littleEndian) /
28476 dataView.getInt32(dataOffset + 4, littleEndian);
28486 cls : 'btn-group roo-upload-cropbox-rotate-left',
28487 action : 'rotate-left',
28491 cls : 'btn btn-default',
28492 html : '<i class="fa fa-undo"></i>'
28498 cls : 'btn-group roo-upload-cropbox-picture',
28499 action : 'picture',
28503 cls : 'btn btn-default',
28504 html : '<i class="fa fa-picture-o"></i>'
28510 cls : 'btn-group roo-upload-cropbox-rotate-right',
28511 action : 'rotate-right',
28515 cls : 'btn btn-default',
28516 html : '<i class="fa fa-repeat"></i>'
28524 cls : 'btn-group roo-upload-cropbox-rotate-left',
28525 action : 'rotate-left',
28529 cls : 'btn btn-default',
28530 html : '<i class="fa fa-undo"></i>'
28536 cls : 'btn-group roo-upload-cropbox-download',
28537 action : 'download',
28541 cls : 'btn btn-default',
28542 html : '<i class="fa fa-download"></i>'
28548 cls : 'btn-group roo-upload-cropbox-crop',
28553 cls : 'btn btn-default',
28554 html : '<i class="fa fa-crop"></i>'
28560 cls : 'btn-group roo-upload-cropbox-trash',
28565 cls : 'btn btn-default',
28566 html : '<i class="fa fa-trash"></i>'
28572 cls : 'btn-group roo-upload-cropbox-rotate-right',
28573 action : 'rotate-right',
28577 cls : 'btn btn-default',
28578 html : '<i class="fa fa-repeat"></i>'
28586 cls : 'btn-group roo-upload-cropbox-rotate-left',
28587 action : 'rotate-left',
28591 cls : 'btn btn-default',
28592 html : '<i class="fa fa-undo"></i>'
28598 cls : 'btn-group roo-upload-cropbox-rotate-right',
28599 action : 'rotate-right',
28603 cls : 'btn btn-default',
28604 html : '<i class="fa fa-repeat"></i>'
28617 * @class Roo.bootstrap.DocumentManager
28618 * @extends Roo.bootstrap.Component
28619 * Bootstrap DocumentManager class
28620 * @cfg {String} paramName default 'imageUpload'
28621 * @cfg {String} toolTipName default 'filename'
28622 * @cfg {String} method default POST
28623 * @cfg {String} url action url
28624 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28625 * @cfg {Boolean} multiple multiple upload default true
28626 * @cfg {Number} thumbSize default 300
28627 * @cfg {String} fieldLabel
28628 * @cfg {Number} labelWidth default 4
28629 * @cfg {String} labelAlign (left|top) default left
28630 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28631 * @cfg {Number} labellg set the width of label (1-12)
28632 * @cfg {Number} labelmd set the width of label (1-12)
28633 * @cfg {Number} labelsm set the width of label (1-12)
28634 * @cfg {Number} labelxs set the width of label (1-12)
28637 * Create a new DocumentManager
28638 * @param {Object} config The config object
28641 Roo.bootstrap.DocumentManager = function(config){
28642 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28645 this.delegates = [];
28650 * Fire when initial the DocumentManager
28651 * @param {Roo.bootstrap.DocumentManager} this
28656 * inspect selected file
28657 * @param {Roo.bootstrap.DocumentManager} this
28658 * @param {File} file
28663 * Fire when xhr load exception
28664 * @param {Roo.bootstrap.DocumentManager} this
28665 * @param {XMLHttpRequest} xhr
28667 "exception" : true,
28669 * @event afterupload
28670 * Fire when xhr load exception
28671 * @param {Roo.bootstrap.DocumentManager} this
28672 * @param {XMLHttpRequest} xhr
28674 "afterupload" : true,
28677 * prepare the form data
28678 * @param {Roo.bootstrap.DocumentManager} this
28679 * @param {Object} formData
28684 * Fire when remove the file
28685 * @param {Roo.bootstrap.DocumentManager} this
28686 * @param {Object} file
28691 * Fire after refresh the file
28692 * @param {Roo.bootstrap.DocumentManager} this
28697 * Fire after click the image
28698 * @param {Roo.bootstrap.DocumentManager} this
28699 * @param {Object} file
28704 * Fire when upload a image and editable set to true
28705 * @param {Roo.bootstrap.DocumentManager} this
28706 * @param {Object} file
28710 * @event beforeselectfile
28711 * Fire before select file
28712 * @param {Roo.bootstrap.DocumentManager} this
28714 "beforeselectfile" : true,
28717 * Fire before process file
28718 * @param {Roo.bootstrap.DocumentManager} this
28719 * @param {Object} file
28723 * @event previewrendered
28724 * Fire when preview rendered
28725 * @param {Roo.bootstrap.DocumentManager} this
28726 * @param {Object} file
28728 "previewrendered" : true,
28731 "previewResize" : true
28736 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
28745 paramName : 'imageUpload',
28746 toolTipName : 'filename',
28749 labelAlign : 'left',
28759 getAutoCreate : function()
28761 var managerWidget = {
28763 cls : 'roo-document-manager',
28767 cls : 'roo-document-manager-selector',
28772 cls : 'roo-document-manager-uploader',
28776 cls : 'roo-document-manager-upload-btn',
28777 html : '<i class="fa fa-plus"></i>'
28788 cls : 'column col-md-12',
28793 if(this.fieldLabel.length){
28798 cls : 'column col-md-12',
28799 html : this.fieldLabel
28803 cls : 'column col-md-12',
28808 if(this.labelAlign == 'left'){
28813 html : this.fieldLabel
28822 if(this.labelWidth > 12){
28823 content[0].style = "width: " + this.labelWidth + 'px';
28826 if(this.labelWidth < 13 && this.labelmd == 0){
28827 this.labelmd = this.labelWidth;
28830 if(this.labellg > 0){
28831 content[0].cls += ' col-lg-' + this.labellg;
28832 content[1].cls += ' col-lg-' + (12 - this.labellg);
28835 if(this.labelmd > 0){
28836 content[0].cls += ' col-md-' + this.labelmd;
28837 content[1].cls += ' col-md-' + (12 - this.labelmd);
28840 if(this.labelsm > 0){
28841 content[0].cls += ' col-sm-' + this.labelsm;
28842 content[1].cls += ' col-sm-' + (12 - this.labelsm);
28845 if(this.labelxs > 0){
28846 content[0].cls += ' col-xs-' + this.labelxs;
28847 content[1].cls += ' col-xs-' + (12 - this.labelxs);
28855 cls : 'row clearfix',
28863 initEvents : function()
28865 this.managerEl = this.el.select('.roo-document-manager', true).first();
28866 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28868 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28869 this.selectorEl.hide();
28872 this.selectorEl.attr('multiple', 'multiple');
28875 this.selectorEl.on('change', this.onFileSelected, this);
28877 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28878 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28880 this.uploader.on('click', this.onUploaderClick, this);
28882 this.renderProgressDialog();
28886 window.addEventListener("resize", function() { _this.refresh(); } );
28888 this.fireEvent('initial', this);
28891 renderProgressDialog : function()
28895 this.progressDialog = new Roo.bootstrap.Modal({
28896 cls : 'roo-document-manager-progress-dialog',
28897 allow_close : false,
28907 btnclick : function() {
28908 _this.uploadCancel();
28914 this.progressDialog.render(Roo.get(document.body));
28916 this.progress = new Roo.bootstrap.Progress({
28917 cls : 'roo-document-manager-progress',
28922 this.progress.render(this.progressDialog.getChildContainer());
28924 this.progressBar = new Roo.bootstrap.ProgressBar({
28925 cls : 'roo-document-manager-progress-bar',
28928 aria_valuemax : 12,
28932 this.progressBar.render(this.progress.getChildContainer());
28935 onUploaderClick : function(e)
28937 e.preventDefault();
28939 if(this.fireEvent('beforeselectfile', this) != false){
28940 this.selectorEl.dom.click();
28945 onFileSelected : function(e)
28947 e.preventDefault();
28949 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28953 Roo.each(this.selectorEl.dom.files, function(file){
28954 if(this.fireEvent('inspect', this, file) != false){
28955 this.files.push(file);
28965 this.selectorEl.dom.value = '';
28967 if(!this.files || !this.files.length){
28971 if(this.boxes > 0 && this.files.length > this.boxes){
28972 this.files = this.files.slice(0, this.boxes);
28975 this.uploader.show();
28977 if(this.boxes > 0 && this.files.length > this.boxes - 1){
28978 this.uploader.hide();
28987 Roo.each(this.files, function(file){
28989 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28990 var f = this.renderPreview(file);
28995 if(file.type.indexOf('image') != -1){
28996 this.delegates.push(
28998 _this.process(file);
28999 }).createDelegate(this)
29007 _this.process(file);
29008 }).createDelegate(this)
29013 this.files = files;
29015 this.delegates = this.delegates.concat(docs);
29017 if(!this.delegates.length){
29022 this.progressBar.aria_valuemax = this.delegates.length;
29029 arrange : function()
29031 if(!this.delegates.length){
29032 this.progressDialog.hide();
29037 var delegate = this.delegates.shift();
29039 this.progressDialog.show();
29041 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29043 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29048 refresh : function()
29050 this.uploader.show();
29052 if(this.boxes > 0 && this.files.length > this.boxes - 1){
29053 this.uploader.hide();
29056 Roo.isTouch ? this.closable(false) : this.closable(true);
29058 this.fireEvent('refresh', this);
29061 onRemove : function(e, el, o)
29063 e.preventDefault();
29065 this.fireEvent('remove', this, o);
29069 remove : function(o)
29073 Roo.each(this.files, function(file){
29074 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29083 this.files = files;
29090 Roo.each(this.files, function(file){
29095 file.target.remove();
29104 onClick : function(e, el, o)
29106 e.preventDefault();
29108 this.fireEvent('click', this, o);
29112 closable : function(closable)
29114 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29116 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29128 xhrOnLoad : function(xhr)
29130 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29134 if (xhr.readyState !== 4) {
29136 this.fireEvent('exception', this, xhr);
29140 var response = Roo.decode(xhr.responseText);
29142 if(!response.success){
29144 this.fireEvent('exception', this, xhr);
29148 var file = this.renderPreview(response.data);
29150 this.files.push(file);
29154 this.fireEvent('afterupload', this, xhr);
29158 xhrOnError : function(xhr)
29160 Roo.log('xhr on error');
29162 var response = Roo.decode(xhr.responseText);
29169 process : function(file)
29171 if(this.fireEvent('process', this, file) !== false){
29172 if(this.editable && file.type.indexOf('image') != -1){
29173 this.fireEvent('edit', this, file);
29177 this.uploadStart(file, false);
29184 uploadStart : function(file, crop)
29186 this.xhr = new XMLHttpRequest();
29188 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29193 file.xhr = this.xhr;
29195 this.managerEl.createChild({
29197 cls : 'roo-document-manager-loading',
29201 tooltip : file.name,
29202 cls : 'roo-document-manager-thumb',
29203 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29209 this.xhr.open(this.method, this.url, true);
29212 "Accept": "application/json",
29213 "Cache-Control": "no-cache",
29214 "X-Requested-With": "XMLHttpRequest"
29217 for (var headerName in headers) {
29218 var headerValue = headers[headerName];
29220 this.xhr.setRequestHeader(headerName, headerValue);
29226 this.xhr.onload = function()
29228 _this.xhrOnLoad(_this.xhr);
29231 this.xhr.onerror = function()
29233 _this.xhrOnError(_this.xhr);
29236 var formData = new FormData();
29238 formData.append('returnHTML', 'NO');
29241 formData.append('crop', crop);
29244 formData.append(this.paramName, file, file.name);
29251 if(this.fireEvent('prepare', this, formData, options) != false){
29253 if(options.manually){
29257 this.xhr.send(formData);
29261 this.uploadCancel();
29264 uploadCancel : function()
29270 this.delegates = [];
29272 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29279 renderPreview : function(file)
29281 if(typeof(file.target) != 'undefined' && file.target){
29285 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29287 var previewEl = this.managerEl.createChild({
29289 cls : 'roo-document-manager-preview',
29293 tooltip : file[this.toolTipName],
29294 cls : 'roo-document-manager-thumb',
29295 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29300 html : '<i class="fa fa-times-circle"></i>'
29305 var close = previewEl.select('button.close', true).first();
29307 close.on('click', this.onRemove, this, file);
29309 file.target = previewEl;
29311 var image = previewEl.select('img', true).first();
29315 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29317 image.on('click', this.onClick, this, file);
29319 this.fireEvent('previewrendered', this, file);
29325 onPreviewLoad : function(file, image)
29327 if(typeof(file.target) == 'undefined' || !file.target){
29331 var width = image.dom.naturalWidth || image.dom.width;
29332 var height = image.dom.naturalHeight || image.dom.height;
29334 if(!this.previewResize) {
29338 if(width > height){
29339 file.target.addClass('wide');
29343 file.target.addClass('tall');
29348 uploadFromSource : function(file, crop)
29350 this.xhr = new XMLHttpRequest();
29352 this.managerEl.createChild({
29354 cls : 'roo-document-manager-loading',
29358 tooltip : file.name,
29359 cls : 'roo-document-manager-thumb',
29360 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29366 this.xhr.open(this.method, this.url, true);
29369 "Accept": "application/json",
29370 "Cache-Control": "no-cache",
29371 "X-Requested-With": "XMLHttpRequest"
29374 for (var headerName in headers) {
29375 var headerValue = headers[headerName];
29377 this.xhr.setRequestHeader(headerName, headerValue);
29383 this.xhr.onload = function()
29385 _this.xhrOnLoad(_this.xhr);
29388 this.xhr.onerror = function()
29390 _this.xhrOnError(_this.xhr);
29393 var formData = new FormData();
29395 formData.append('returnHTML', 'NO');
29397 formData.append('crop', crop);
29399 if(typeof(file.filename) != 'undefined'){
29400 formData.append('filename', file.filename);
29403 if(typeof(file.mimetype) != 'undefined'){
29404 formData.append('mimetype', file.mimetype);
29409 if(this.fireEvent('prepare', this, formData) != false){
29410 this.xhr.send(formData);
29420 * @class Roo.bootstrap.DocumentViewer
29421 * @extends Roo.bootstrap.Component
29422 * Bootstrap DocumentViewer class
29423 * @cfg {Boolean} showDownload (true|false) show download button (default true)
29424 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29427 * Create a new DocumentViewer
29428 * @param {Object} config The config object
29431 Roo.bootstrap.DocumentViewer = function(config){
29432 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29437 * Fire after initEvent
29438 * @param {Roo.bootstrap.DocumentViewer} this
29444 * @param {Roo.bootstrap.DocumentViewer} this
29449 * Fire after download button
29450 * @param {Roo.bootstrap.DocumentViewer} this
29455 * Fire after trash button
29456 * @param {Roo.bootstrap.DocumentViewer} this
29463 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
29465 showDownload : true,
29469 getAutoCreate : function()
29473 cls : 'roo-document-viewer',
29477 cls : 'roo-document-viewer-body',
29481 cls : 'roo-document-viewer-thumb',
29485 cls : 'roo-document-viewer-image'
29493 cls : 'roo-document-viewer-footer',
29496 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29500 cls : 'btn-group roo-document-viewer-download',
29504 cls : 'btn btn-default',
29505 html : '<i class="fa fa-download"></i>'
29511 cls : 'btn-group roo-document-viewer-trash',
29515 cls : 'btn btn-default',
29516 html : '<i class="fa fa-trash"></i>'
29529 initEvents : function()
29531 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29532 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29534 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29535 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29537 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29538 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29540 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29541 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29543 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29544 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29546 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29547 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29549 this.bodyEl.on('click', this.onClick, this);
29550 this.downloadBtn.on('click', this.onDownload, this);
29551 this.trashBtn.on('click', this.onTrash, this);
29553 this.downloadBtn.hide();
29554 this.trashBtn.hide();
29556 if(this.showDownload){
29557 this.downloadBtn.show();
29560 if(this.showTrash){
29561 this.trashBtn.show();
29564 if(!this.showDownload && !this.showTrash) {
29565 this.footerEl.hide();
29570 initial : function()
29572 this.fireEvent('initial', this);
29576 onClick : function(e)
29578 e.preventDefault();
29580 this.fireEvent('click', this);
29583 onDownload : function(e)
29585 e.preventDefault();
29587 this.fireEvent('download', this);
29590 onTrash : function(e)
29592 e.preventDefault();
29594 this.fireEvent('trash', this);
29606 * @class Roo.bootstrap.NavProgressBar
29607 * @extends Roo.bootstrap.Component
29608 * Bootstrap NavProgressBar class
29611 * Create a new nav progress bar
29612 * @param {Object} config The config object
29615 Roo.bootstrap.NavProgressBar = function(config){
29616 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29618 this.bullets = this.bullets || [];
29620 // Roo.bootstrap.NavProgressBar.register(this);
29624 * Fires when the active item changes
29625 * @param {Roo.bootstrap.NavProgressBar} this
29626 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29627 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
29634 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
29639 getAutoCreate : function()
29641 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29645 cls : 'roo-navigation-bar-group',
29649 cls : 'roo-navigation-top-bar'
29653 cls : 'roo-navigation-bullets-bar',
29657 cls : 'roo-navigation-bar'
29664 cls : 'roo-navigation-bottom-bar'
29674 initEvents: function()
29679 onRender : function(ct, position)
29681 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29683 if(this.bullets.length){
29684 Roo.each(this.bullets, function(b){
29693 addItem : function(cfg)
29695 var item = new Roo.bootstrap.NavProgressItem(cfg);
29697 item.parentId = this.id;
29698 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29701 var top = new Roo.bootstrap.Element({
29703 cls : 'roo-navigation-bar-text'
29706 var bottom = new Roo.bootstrap.Element({
29708 cls : 'roo-navigation-bar-text'
29711 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29712 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29714 var topText = new Roo.bootstrap.Element({
29716 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29719 var bottomText = new Roo.bootstrap.Element({
29721 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29724 topText.onRender(top.el, null);
29725 bottomText.onRender(bottom.el, null);
29728 item.bottomEl = bottom;
29731 this.barItems.push(item);
29736 getActive : function()
29738 var active = false;
29740 Roo.each(this.barItems, function(v){
29742 if (!v.isActive()) {
29754 setActiveItem : function(item)
29758 Roo.each(this.barItems, function(v){
29759 if (v.rid == item.rid) {
29763 if (v.isActive()) {
29764 v.setActive(false);
29769 item.setActive(true);
29771 this.fireEvent('changed', this, item, prev);
29774 getBarItem: function(rid)
29778 Roo.each(this.barItems, function(e) {
29779 if (e.rid != rid) {
29790 indexOfItem : function(item)
29794 Roo.each(this.barItems, function(v, i){
29796 if (v.rid != item.rid) {
29807 setActiveNext : function()
29809 var i = this.indexOfItem(this.getActive());
29811 if (i > this.barItems.length) {
29815 this.setActiveItem(this.barItems[i+1]);
29818 setActivePrev : function()
29820 var i = this.indexOfItem(this.getActive());
29826 this.setActiveItem(this.barItems[i-1]);
29829 format : function()
29831 if(!this.barItems.length){
29835 var width = 100 / this.barItems.length;
29837 Roo.each(this.barItems, function(i){
29838 i.el.setStyle('width', width + '%');
29839 i.topEl.el.setStyle('width', width + '%');
29840 i.bottomEl.el.setStyle('width', width + '%');
29849 * Nav Progress Item
29854 * @class Roo.bootstrap.NavProgressItem
29855 * @extends Roo.bootstrap.Component
29856 * Bootstrap NavProgressItem class
29857 * @cfg {String} rid the reference id
29858 * @cfg {Boolean} active (true|false) Is item active default false
29859 * @cfg {Boolean} disabled (true|false) Is item active default false
29860 * @cfg {String} html
29861 * @cfg {String} position (top|bottom) text position default bottom
29862 * @cfg {String} icon show icon instead of number
29865 * Create a new NavProgressItem
29866 * @param {Object} config The config object
29868 Roo.bootstrap.NavProgressItem = function(config){
29869 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29874 * The raw click event for the entire grid.
29875 * @param {Roo.bootstrap.NavProgressItem} this
29876 * @param {Roo.EventObject} e
29883 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
29889 position : 'bottom',
29892 getAutoCreate : function()
29894 var iconCls = 'roo-navigation-bar-item-icon';
29896 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29900 cls: 'roo-navigation-bar-item',
29910 cfg.cls += ' active';
29913 cfg.cls += ' disabled';
29919 disable : function()
29921 this.setDisabled(true);
29924 enable : function()
29926 this.setDisabled(false);
29929 initEvents: function()
29931 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29933 this.iconEl.on('click', this.onClick, this);
29936 onClick : function(e)
29938 e.preventDefault();
29944 if(this.fireEvent('click', this, e) === false){
29948 this.parent().setActiveItem(this);
29951 isActive: function ()
29953 return this.active;
29956 setActive : function(state)
29958 if(this.active == state){
29962 this.active = state;
29965 this.el.addClass('active');
29969 this.el.removeClass('active');
29974 setDisabled : function(state)
29976 if(this.disabled == state){
29980 this.disabled = state;
29983 this.el.addClass('disabled');
29987 this.el.removeClass('disabled');
29990 tooltipEl : function()
29992 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30005 * @class Roo.bootstrap.FieldLabel
30006 * @extends Roo.bootstrap.Component
30007 * Bootstrap FieldLabel class
30008 * @cfg {String} html contents of the element
30009 * @cfg {String} tag tag of the element default label
30010 * @cfg {String} cls class of the element
30011 * @cfg {String} target label target
30012 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30013 * @cfg {String} invalidClass default "text-warning"
30014 * @cfg {String} validClass default "text-success"
30015 * @cfg {String} iconTooltip default "This field is required"
30016 * @cfg {String} indicatorpos (left|right) default left
30019 * Create a new FieldLabel
30020 * @param {Object} config The config object
30023 Roo.bootstrap.FieldLabel = function(config){
30024 Roo.bootstrap.Element.superclass.constructor.call(this, config);
30029 * Fires after the field has been marked as invalid.
30030 * @param {Roo.form.FieldLabel} this
30031 * @param {String} msg The validation message
30036 * Fires after the field has been validated with no errors.
30037 * @param {Roo.form.FieldLabel} this
30043 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
30050 invalidClass : 'has-warning',
30051 validClass : 'has-success',
30052 iconTooltip : 'This field is required',
30053 indicatorpos : 'left',
30055 getAutoCreate : function(){
30058 if (!this.allowBlank) {
30064 cls : 'roo-bootstrap-field-label ' + this.cls,
30069 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30070 tooltip : this.iconTooltip
30079 if(this.indicatorpos == 'right'){
30082 cls : 'roo-bootstrap-field-label ' + this.cls,
30091 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30092 tooltip : this.iconTooltip
30101 initEvents: function()
30103 Roo.bootstrap.Element.superclass.initEvents.call(this);
30105 this.indicator = this.indicatorEl();
30107 if(this.indicator){
30108 this.indicator.removeClass('visible');
30109 this.indicator.addClass('invisible');
30112 Roo.bootstrap.FieldLabel.register(this);
30115 indicatorEl : function()
30117 var indicator = this.el.select('i.roo-required-indicator',true).first();
30128 * Mark this field as valid
30130 markValid : function()
30132 if(this.indicator){
30133 this.indicator.removeClass('visible');
30134 this.indicator.addClass('invisible');
30137 this.el.removeClass(this.invalidClass);
30139 this.el.addClass(this.validClass);
30141 this.fireEvent('valid', this);
30145 * Mark this field as invalid
30146 * @param {String} msg The validation message
30148 markInvalid : function(msg)
30150 if(this.indicator){
30151 this.indicator.removeClass('invisible');
30152 this.indicator.addClass('visible');
30155 this.el.removeClass(this.validClass);
30157 this.el.addClass(this.invalidClass);
30159 this.fireEvent('invalid', this, msg);
30165 Roo.apply(Roo.bootstrap.FieldLabel, {
30170 * register a FieldLabel Group
30171 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30173 register : function(label)
30175 if(this.groups.hasOwnProperty(label.target)){
30179 this.groups[label.target] = label;
30183 * fetch a FieldLabel Group based on the target
30184 * @param {string} target
30185 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30187 get: function(target) {
30188 if (typeof(this.groups[target]) == 'undefined') {
30192 return this.groups[target] ;
30201 * page DateSplitField.
30207 * @class Roo.bootstrap.DateSplitField
30208 * @extends Roo.bootstrap.Component
30209 * Bootstrap DateSplitField class
30210 * @cfg {string} fieldLabel - the label associated
30211 * @cfg {Number} labelWidth set the width of label (0-12)
30212 * @cfg {String} labelAlign (top|left)
30213 * @cfg {Boolean} dayAllowBlank (true|false) default false
30214 * @cfg {Boolean} monthAllowBlank (true|false) default false
30215 * @cfg {Boolean} yearAllowBlank (true|false) default false
30216 * @cfg {string} dayPlaceholder
30217 * @cfg {string} monthPlaceholder
30218 * @cfg {string} yearPlaceholder
30219 * @cfg {string} dayFormat default 'd'
30220 * @cfg {string} monthFormat default 'm'
30221 * @cfg {string} yearFormat default 'Y'
30222 * @cfg {Number} labellg set the width of label (1-12)
30223 * @cfg {Number} labelmd set the width of label (1-12)
30224 * @cfg {Number} labelsm set the width of label (1-12)
30225 * @cfg {Number} labelxs set the width of label (1-12)
30229 * Create a new DateSplitField
30230 * @param {Object} config The config object
30233 Roo.bootstrap.DateSplitField = function(config){
30234 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30240 * getting the data of years
30241 * @param {Roo.bootstrap.DateSplitField} this
30242 * @param {Object} years
30247 * getting the data of days
30248 * @param {Roo.bootstrap.DateSplitField} this
30249 * @param {Object} days
30254 * Fires after the field has been marked as invalid.
30255 * @param {Roo.form.Field} this
30256 * @param {String} msg The validation message
30261 * Fires after the field has been validated with no errors.
30262 * @param {Roo.form.Field} this
30268 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
30271 labelAlign : 'top',
30273 dayAllowBlank : false,
30274 monthAllowBlank : false,
30275 yearAllowBlank : false,
30276 dayPlaceholder : '',
30277 monthPlaceholder : '',
30278 yearPlaceholder : '',
30282 isFormField : true,
30288 getAutoCreate : function()
30292 cls : 'row roo-date-split-field-group',
30297 cls : 'form-hidden-field roo-date-split-field-group-value',
30303 var labelCls = 'col-md-12';
30304 var contentCls = 'col-md-4';
30306 if(this.fieldLabel){
30310 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30314 html : this.fieldLabel
30319 if(this.labelAlign == 'left'){
30321 if(this.labelWidth > 12){
30322 label.style = "width: " + this.labelWidth + 'px';
30325 if(this.labelWidth < 13 && this.labelmd == 0){
30326 this.labelmd = this.labelWidth;
30329 if(this.labellg > 0){
30330 labelCls = ' col-lg-' + this.labellg;
30331 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30334 if(this.labelmd > 0){
30335 labelCls = ' col-md-' + this.labelmd;
30336 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30339 if(this.labelsm > 0){
30340 labelCls = ' col-sm-' + this.labelsm;
30341 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30344 if(this.labelxs > 0){
30345 labelCls = ' col-xs-' + this.labelxs;
30346 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30350 label.cls += ' ' + labelCls;
30352 cfg.cn.push(label);
30355 Roo.each(['day', 'month', 'year'], function(t){
30358 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30365 inputEl: function ()
30367 return this.el.select('.roo-date-split-field-group-value', true).first();
30370 onRender : function(ct, position)
30374 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30376 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30378 this.dayField = new Roo.bootstrap.ComboBox({
30379 allowBlank : this.dayAllowBlank,
30380 alwaysQuery : true,
30381 displayField : 'value',
30384 forceSelection : true,
30386 placeholder : this.dayPlaceholder,
30387 selectOnFocus : true,
30388 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30389 triggerAction : 'all',
30391 valueField : 'value',
30392 store : new Roo.data.SimpleStore({
30393 data : (function() {
30395 _this.fireEvent('days', _this, days);
30398 fields : [ 'value' ]
30401 select : function (_self, record, index)
30403 _this.setValue(_this.getValue());
30408 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30410 this.monthField = new Roo.bootstrap.MonthField({
30411 after : '<i class=\"fa fa-calendar\"></i>',
30412 allowBlank : this.monthAllowBlank,
30413 placeholder : this.monthPlaceholder,
30416 render : function (_self)
30418 this.el.select('span.input-group-addon', true).first().on('click', function(e){
30419 e.preventDefault();
30423 select : function (_self, oldvalue, newvalue)
30425 _this.setValue(_this.getValue());
30430 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30432 this.yearField = new Roo.bootstrap.ComboBox({
30433 allowBlank : this.yearAllowBlank,
30434 alwaysQuery : true,
30435 displayField : 'value',
30438 forceSelection : true,
30440 placeholder : this.yearPlaceholder,
30441 selectOnFocus : true,
30442 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30443 triggerAction : 'all',
30445 valueField : 'value',
30446 store : new Roo.data.SimpleStore({
30447 data : (function() {
30449 _this.fireEvent('years', _this, years);
30452 fields : [ 'value' ]
30455 select : function (_self, record, index)
30457 _this.setValue(_this.getValue());
30462 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30465 setValue : function(v, format)
30467 this.inputEl.dom.value = v;
30469 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30471 var d = Date.parseDate(v, f);
30478 this.setDay(d.format(this.dayFormat));
30479 this.setMonth(d.format(this.monthFormat));
30480 this.setYear(d.format(this.yearFormat));
30487 setDay : function(v)
30489 this.dayField.setValue(v);
30490 this.inputEl.dom.value = this.getValue();
30495 setMonth : function(v)
30497 this.monthField.setValue(v, true);
30498 this.inputEl.dom.value = this.getValue();
30503 setYear : function(v)
30505 this.yearField.setValue(v);
30506 this.inputEl.dom.value = this.getValue();
30511 getDay : function()
30513 return this.dayField.getValue();
30516 getMonth : function()
30518 return this.monthField.getValue();
30521 getYear : function()
30523 return this.yearField.getValue();
30526 getValue : function()
30528 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30530 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30540 this.inputEl.dom.value = '';
30545 validate : function()
30547 var d = this.dayField.validate();
30548 var m = this.monthField.validate();
30549 var y = this.yearField.validate();
30554 (!this.dayAllowBlank && !d) ||
30555 (!this.monthAllowBlank && !m) ||
30556 (!this.yearAllowBlank && !y)
30561 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30570 this.markInvalid();
30575 markValid : function()
30578 var label = this.el.select('label', true).first();
30579 var icon = this.el.select('i.fa-star', true).first();
30585 this.fireEvent('valid', this);
30589 * Mark this field as invalid
30590 * @param {String} msg The validation message
30592 markInvalid : function(msg)
30595 var label = this.el.select('label', true).first();
30596 var icon = this.el.select('i.fa-star', true).first();
30598 if(label && !icon){
30599 this.el.select('.roo-date-split-field-label', true).createChild({
30601 cls : 'text-danger fa fa-lg fa-star',
30602 tooltip : 'This field is required',
30603 style : 'margin-right:5px;'
30607 this.fireEvent('invalid', this, msg);
30610 clearInvalid : function()
30612 var label = this.el.select('label', true).first();
30613 var icon = this.el.select('i.fa-star', true).first();
30619 this.fireEvent('valid', this);
30622 getName: function()
30632 * http://masonry.desandro.com
30634 * The idea is to render all the bricks based on vertical width...
30636 * The original code extends 'outlayer' - we might need to use that....
30642 * @class Roo.bootstrap.LayoutMasonry
30643 * @extends Roo.bootstrap.Component
30644 * Bootstrap Layout Masonry class
30647 * Create a new Element
30648 * @param {Object} config The config object
30651 Roo.bootstrap.LayoutMasonry = function(config){
30653 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30657 Roo.bootstrap.LayoutMasonry.register(this);
30663 * Fire after layout the items
30664 * @param {Roo.bootstrap.LayoutMasonry} this
30665 * @param {Roo.EventObject} e
30672 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
30675 * @cfg {Boolean} isLayoutInstant = no animation?
30677 isLayoutInstant : false, // needed?
30680 * @cfg {Number} boxWidth width of the columns
30685 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
30690 * @cfg {Number} padWidth padding below box..
30695 * @cfg {Number} gutter gutter width..
30700 * @cfg {Number} maxCols maximum number of columns
30706 * @cfg {Boolean} isAutoInitial defalut true
30708 isAutoInitial : true,
30713 * @cfg {Boolean} isHorizontal defalut false
30715 isHorizontal : false,
30717 currentSize : null,
30723 bricks: null, //CompositeElement
30727 _isLayoutInited : false,
30729 // isAlternative : false, // only use for vertical layout...
30732 * @cfg {Number} alternativePadWidth padding below box..
30734 alternativePadWidth : 50,
30736 selectedBrick : [],
30738 getAutoCreate : function(){
30740 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30744 cls: 'blog-masonary-wrapper ' + this.cls,
30746 cls : 'mas-boxes masonary'
30753 getChildContainer: function( )
30755 if (this.boxesEl) {
30756 return this.boxesEl;
30759 this.boxesEl = this.el.select('.mas-boxes').first();
30761 return this.boxesEl;
30765 initEvents : function()
30769 if(this.isAutoInitial){
30770 Roo.log('hook children rendered');
30771 this.on('childrenrendered', function() {
30772 Roo.log('children rendered');
30778 initial : function()
30780 this.selectedBrick = [];
30782 this.currentSize = this.el.getBox(true);
30784 Roo.EventManager.onWindowResize(this.resize, this);
30786 if(!this.isAutoInitial){
30794 //this.layout.defer(500,this);
30798 resize : function()
30800 var cs = this.el.getBox(true);
30803 this.currentSize.width == cs.width &&
30804 this.currentSize.x == cs.x &&
30805 this.currentSize.height == cs.height &&
30806 this.currentSize.y == cs.y
30808 Roo.log("no change in with or X or Y");
30812 this.currentSize = cs;
30818 layout : function()
30820 this._resetLayout();
30822 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30824 this.layoutItems( isInstant );
30826 this._isLayoutInited = true;
30828 this.fireEvent('layout', this);
30832 _resetLayout : function()
30834 if(this.isHorizontal){
30835 this.horizontalMeasureColumns();
30839 this.verticalMeasureColumns();
30843 verticalMeasureColumns : function()
30845 this.getContainerWidth();
30847 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30848 // this.colWidth = Math.floor(this.containerWidth * 0.8);
30852 var boxWidth = this.boxWidth + this.padWidth;
30854 if(this.containerWidth < this.boxWidth){
30855 boxWidth = this.containerWidth
30858 var containerWidth = this.containerWidth;
30860 var cols = Math.floor(containerWidth / boxWidth);
30862 this.cols = Math.max( cols, 1 );
30864 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30866 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30868 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30870 this.colWidth = boxWidth + avail - this.padWidth;
30872 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30873 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
30876 horizontalMeasureColumns : function()
30878 this.getContainerWidth();
30880 var boxWidth = this.boxWidth;
30882 if(this.containerWidth < boxWidth){
30883 boxWidth = this.containerWidth;
30886 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30888 this.el.setHeight(boxWidth);
30892 getContainerWidth : function()
30894 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30897 layoutItems : function( isInstant )
30899 Roo.log(this.bricks);
30901 var items = Roo.apply([], this.bricks);
30903 if(this.isHorizontal){
30904 this._horizontalLayoutItems( items , isInstant );
30908 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30909 // this._verticalAlternativeLayoutItems( items , isInstant );
30913 this._verticalLayoutItems( items , isInstant );
30917 _verticalLayoutItems : function ( items , isInstant)
30919 if ( !items || !items.length ) {
30924 ['xs', 'xs', 'xs', 'tall'],
30925 ['xs', 'xs', 'tall'],
30926 ['xs', 'xs', 'sm'],
30927 ['xs', 'xs', 'xs'],
30933 ['sm', 'xs', 'xs'],
30937 ['tall', 'xs', 'xs', 'xs'],
30938 ['tall', 'xs', 'xs'],
30950 Roo.each(items, function(item, k){
30952 switch (item.size) {
30953 // these layouts take up a full box,
30964 boxes.push([item]);
30987 var filterPattern = function(box, length)
30995 var pattern = box.slice(0, length);
30999 Roo.each(pattern, function(i){
31000 format.push(i.size);
31003 Roo.each(standard, function(s){
31005 if(String(s) != String(format)){
31014 if(!match && length == 1){
31019 filterPattern(box, length - 1);
31023 queue.push(pattern);
31025 box = box.slice(length, box.length);
31027 filterPattern(box, 4);
31033 Roo.each(boxes, function(box, k){
31039 if(box.length == 1){
31044 filterPattern(box, 4);
31048 this._processVerticalLayoutQueue( queue, isInstant );
31052 // _verticalAlternativeLayoutItems : function( items , isInstant )
31054 // if ( !items || !items.length ) {
31058 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
31062 _horizontalLayoutItems : function ( items , isInstant)
31064 if ( !items || !items.length || items.length < 3) {
31070 var eItems = items.slice(0, 3);
31072 items = items.slice(3, items.length);
31075 ['xs', 'xs', 'xs', 'wide'],
31076 ['xs', 'xs', 'wide'],
31077 ['xs', 'xs', 'sm'],
31078 ['xs', 'xs', 'xs'],
31084 ['sm', 'xs', 'xs'],
31088 ['wide', 'xs', 'xs', 'xs'],
31089 ['wide', 'xs', 'xs'],
31102 Roo.each(items, function(item, k){
31104 switch (item.size) {
31115 boxes.push([item]);
31139 var filterPattern = function(box, length)
31147 var pattern = box.slice(0, length);
31151 Roo.each(pattern, function(i){
31152 format.push(i.size);
31155 Roo.each(standard, function(s){
31157 if(String(s) != String(format)){
31166 if(!match && length == 1){
31171 filterPattern(box, length - 1);
31175 queue.push(pattern);
31177 box = box.slice(length, box.length);
31179 filterPattern(box, 4);
31185 Roo.each(boxes, function(box, k){
31191 if(box.length == 1){
31196 filterPattern(box, 4);
31203 var pos = this.el.getBox(true);
31207 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31209 var hit_end = false;
31211 Roo.each(queue, function(box){
31215 Roo.each(box, function(b){
31217 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31227 Roo.each(box, function(b){
31229 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31232 mx = Math.max(mx, b.x);
31236 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31240 Roo.each(box, function(b){
31242 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31256 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31259 /** Sets position of item in DOM
31260 * @param {Element} item
31261 * @param {Number} x - horizontal position
31262 * @param {Number} y - vertical position
31263 * @param {Boolean} isInstant - disables transitions
31265 _processVerticalLayoutQueue : function( queue, isInstant )
31267 var pos = this.el.getBox(true);
31272 for (var i = 0; i < this.cols; i++){
31276 Roo.each(queue, function(box, k){
31278 var col = k % this.cols;
31280 Roo.each(box, function(b,kk){
31282 b.el.position('absolute');
31284 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31285 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31287 if(b.size == 'md-left' || b.size == 'md-right'){
31288 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31289 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31292 b.el.setWidth(width);
31293 b.el.setHeight(height);
31295 b.el.select('iframe',true).setSize(width,height);
31299 for (var i = 0; i < this.cols; i++){
31301 if(maxY[i] < maxY[col]){
31306 col = Math.min(col, i);
31310 x = pos.x + col * (this.colWidth + this.padWidth);
31314 var positions = [];
31316 switch (box.length){
31318 positions = this.getVerticalOneBoxColPositions(x, y, box);
31321 positions = this.getVerticalTwoBoxColPositions(x, y, box);
31324 positions = this.getVerticalThreeBoxColPositions(x, y, box);
31327 positions = this.getVerticalFourBoxColPositions(x, y, box);
31333 Roo.each(box, function(b,kk){
31335 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31337 var sz = b.el.getSize();
31339 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31347 for (var i = 0; i < this.cols; i++){
31348 mY = Math.max(mY, maxY[i]);
31351 this.el.setHeight(mY - pos.y);
31355 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31357 // var pos = this.el.getBox(true);
31360 // var maxX = pos.right;
31362 // var maxHeight = 0;
31364 // Roo.each(items, function(item, k){
31368 // item.el.position('absolute');
31370 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31372 // item.el.setWidth(width);
31374 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31376 // item.el.setHeight(height);
31379 // item.el.setXY([x, y], isInstant ? false : true);
31381 // item.el.setXY([maxX - width, y], isInstant ? false : true);
31384 // y = y + height + this.alternativePadWidth;
31386 // maxHeight = maxHeight + height + this.alternativePadWidth;
31390 // this.el.setHeight(maxHeight);
31394 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31396 var pos = this.el.getBox(true);
31401 var maxX = pos.right;
31403 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31405 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31407 Roo.each(queue, function(box, k){
31409 Roo.each(box, function(b, kk){
31411 b.el.position('absolute');
31413 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31414 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31416 if(b.size == 'md-left' || b.size == 'md-right'){
31417 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31418 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31421 b.el.setWidth(width);
31422 b.el.setHeight(height);
31430 var positions = [];
31432 switch (box.length){
31434 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31437 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31440 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31443 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31449 Roo.each(box, function(b,kk){
31451 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31453 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31461 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31463 Roo.each(eItems, function(b,k){
31465 b.size = (k == 0) ? 'sm' : 'xs';
31466 b.x = (k == 0) ? 2 : 1;
31467 b.y = (k == 0) ? 2 : 1;
31469 b.el.position('absolute');
31471 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31473 b.el.setWidth(width);
31475 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31477 b.el.setHeight(height);
31481 var positions = [];
31484 x : maxX - this.unitWidth * 2 - this.gutter,
31489 x : maxX - this.unitWidth,
31490 y : minY + (this.unitWidth + this.gutter) * 2
31494 x : maxX - this.unitWidth * 3 - this.gutter * 2,
31498 Roo.each(eItems, function(b,k){
31500 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31506 getVerticalOneBoxColPositions : function(x, y, box)
31510 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31512 if(box[0].size == 'md-left'){
31516 if(box[0].size == 'md-right'){
31521 x : x + (this.unitWidth + this.gutter) * rand,
31528 getVerticalTwoBoxColPositions : function(x, y, box)
31532 if(box[0].size == 'xs'){
31536 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31540 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31554 x : x + (this.unitWidth + this.gutter) * 2,
31555 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31562 getVerticalThreeBoxColPositions : function(x, y, box)
31566 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31574 x : x + (this.unitWidth + this.gutter) * 1,
31579 x : x + (this.unitWidth + this.gutter) * 2,
31587 if(box[0].size == 'xs' && box[1].size == 'xs'){
31596 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31600 x : x + (this.unitWidth + this.gutter) * 1,
31614 x : x + (this.unitWidth + this.gutter) * 2,
31619 x : x + (this.unitWidth + this.gutter) * 2,
31620 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31627 getVerticalFourBoxColPositions : function(x, y, box)
31631 if(box[0].size == 'xs'){
31640 y : y + (this.unitHeight + this.gutter) * 1
31645 y : y + (this.unitHeight + this.gutter) * 2
31649 x : x + (this.unitWidth + this.gutter) * 1,
31663 x : x + (this.unitWidth + this.gutter) * 2,
31668 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31669 y : y + (this.unitHeight + this.gutter) * 1
31673 x : x + (this.unitWidth + this.gutter) * 2,
31674 y : y + (this.unitWidth + this.gutter) * 2
31681 getHorizontalOneBoxColPositions : function(maxX, minY, box)
31685 if(box[0].size == 'md-left'){
31687 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31694 if(box[0].size == 'md-right'){
31696 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31697 y : minY + (this.unitWidth + this.gutter) * 1
31703 var rand = Math.floor(Math.random() * (4 - box[0].y));
31706 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31707 y : minY + (this.unitWidth + this.gutter) * rand
31714 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31718 if(box[0].size == 'xs'){
31721 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31726 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31727 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31735 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31740 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31741 y : minY + (this.unitWidth + this.gutter) * 2
31748 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31752 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31755 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31760 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31761 y : minY + (this.unitWidth + this.gutter) * 1
31765 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31766 y : minY + (this.unitWidth + this.gutter) * 2
31773 if(box[0].size == 'xs' && box[1].size == 'xs'){
31776 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31781 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31786 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31787 y : minY + (this.unitWidth + this.gutter) * 1
31795 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31800 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31801 y : minY + (this.unitWidth + this.gutter) * 2
31805 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31806 y : minY + (this.unitWidth + this.gutter) * 2
31813 getHorizontalFourBoxColPositions : function(maxX, minY, box)
31817 if(box[0].size == 'xs'){
31820 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31825 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31830 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),
31835 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31836 y : minY + (this.unitWidth + this.gutter) * 1
31844 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31849 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31850 y : minY + (this.unitWidth + this.gutter) * 2
31854 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31855 y : minY + (this.unitWidth + this.gutter) * 2
31859 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),
31860 y : minY + (this.unitWidth + this.gutter) * 2
31868 * remove a Masonry Brick
31869 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31871 removeBrick : function(brick_id)
31877 for (var i = 0; i<this.bricks.length; i++) {
31878 if (this.bricks[i].id == brick_id) {
31879 this.bricks.splice(i,1);
31880 this.el.dom.removeChild(Roo.get(brick_id).dom);
31887 * adds a Masonry Brick
31888 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31890 addBrick : function(cfg)
31892 var cn = new Roo.bootstrap.MasonryBrick(cfg);
31893 //this.register(cn);
31894 cn.parentId = this.id;
31895 cn.onRender(this.el, null);
31900 * register a Masonry Brick
31901 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31904 register : function(brick)
31906 this.bricks.push(brick);
31907 brick.masonryId = this.id;
31911 * clear all the Masonry Brick
31913 clearAll : function()
31916 //this.getChildContainer().dom.innerHTML = "";
31917 this.el.dom.innerHTML = '';
31920 getSelected : function()
31922 if (!this.selectedBrick) {
31926 return this.selectedBrick;
31930 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31934 * register a Masonry Layout
31935 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31938 register : function(layout)
31940 this.groups[layout.id] = layout;
31943 * fetch a Masonry Layout based on the masonry layout ID
31944 * @param {string} the masonry layout to add
31945 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31948 get: function(layout_id) {
31949 if (typeof(this.groups[layout_id]) == 'undefined') {
31952 return this.groups[layout_id] ;
31964 * http://masonry.desandro.com
31966 * The idea is to render all the bricks based on vertical width...
31968 * The original code extends 'outlayer' - we might need to use that....
31974 * @class Roo.bootstrap.LayoutMasonryAuto
31975 * @extends Roo.bootstrap.Component
31976 * Bootstrap Layout Masonry class
31979 * Create a new Element
31980 * @param {Object} config The config object
31983 Roo.bootstrap.LayoutMasonryAuto = function(config){
31984 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31987 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
31990 * @cfg {Boolean} isFitWidth - resize the width..
31992 isFitWidth : false, // options..
31994 * @cfg {Boolean} isOriginLeft = left align?
31996 isOriginLeft : true,
31998 * @cfg {Boolean} isOriginTop = top align?
32000 isOriginTop : false,
32002 * @cfg {Boolean} isLayoutInstant = no animation?
32004 isLayoutInstant : false, // needed?
32006 * @cfg {Boolean} isResizingContainer = not sure if this is used..
32008 isResizingContainer : true,
32010 * @cfg {Number} columnWidth width of the columns
32016 * @cfg {Number} maxCols maximum number of columns
32021 * @cfg {Number} padHeight padding below box..
32027 * @cfg {Boolean} isAutoInitial defalut true
32030 isAutoInitial : true,
32036 initialColumnWidth : 0,
32037 currentSize : null,
32039 colYs : null, // array.
32046 bricks: null, //CompositeElement
32047 cols : 0, // array?
32048 // element : null, // wrapped now this.el
32049 _isLayoutInited : null,
32052 getAutoCreate : function(){
32056 cls: 'blog-masonary-wrapper ' + this.cls,
32058 cls : 'mas-boxes masonary'
32065 getChildContainer: function( )
32067 if (this.boxesEl) {
32068 return this.boxesEl;
32071 this.boxesEl = this.el.select('.mas-boxes').first();
32073 return this.boxesEl;
32077 initEvents : function()
32081 if(this.isAutoInitial){
32082 Roo.log('hook children rendered');
32083 this.on('childrenrendered', function() {
32084 Roo.log('children rendered');
32091 initial : function()
32093 this.reloadItems();
32095 this.currentSize = this.el.getBox(true);
32097 /// was window resize... - let's see if this works..
32098 Roo.EventManager.onWindowResize(this.resize, this);
32100 if(!this.isAutoInitial){
32105 this.layout.defer(500,this);
32108 reloadItems: function()
32110 this.bricks = this.el.select('.masonry-brick', true);
32112 this.bricks.each(function(b) {
32113 //Roo.log(b.getSize());
32114 if (!b.attr('originalwidth')) {
32115 b.attr('originalwidth', b.getSize().width);
32120 Roo.log(this.bricks.elements.length);
32123 resize : function()
32126 var cs = this.el.getBox(true);
32128 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32129 Roo.log("no change in with or X");
32132 this.currentSize = cs;
32136 layout : function()
32139 this._resetLayout();
32140 //this._manageStamps();
32142 // don't animate first layout
32143 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32144 this.layoutItems( isInstant );
32146 // flag for initalized
32147 this._isLayoutInited = true;
32150 layoutItems : function( isInstant )
32152 //var items = this._getItemsForLayout( this.items );
32153 // original code supports filtering layout items.. we just ignore it..
32155 this._layoutItems( this.bricks , isInstant );
32157 this._postLayout();
32159 _layoutItems : function ( items , isInstant)
32161 //this.fireEvent( 'layout', this, items );
32164 if ( !items || !items.elements.length ) {
32165 // no items, emit event with empty array
32170 items.each(function(item) {
32171 Roo.log("layout item");
32173 // get x/y object from method
32174 var position = this._getItemLayoutPosition( item );
32176 position.item = item;
32177 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32178 queue.push( position );
32181 this._processLayoutQueue( queue );
32183 /** Sets position of item in DOM
32184 * @param {Element} item
32185 * @param {Number} x - horizontal position
32186 * @param {Number} y - vertical position
32187 * @param {Boolean} isInstant - disables transitions
32189 _processLayoutQueue : function( queue )
32191 for ( var i=0, len = queue.length; i < len; i++ ) {
32192 var obj = queue[i];
32193 obj.item.position('absolute');
32194 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32200 * Any logic you want to do after each layout,
32201 * i.e. size the container
32203 _postLayout : function()
32205 this.resizeContainer();
32208 resizeContainer : function()
32210 if ( !this.isResizingContainer ) {
32213 var size = this._getContainerSize();
32215 this.el.setSize(size.width,size.height);
32216 this.boxesEl.setSize(size.width,size.height);
32222 _resetLayout : function()
32224 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32225 this.colWidth = this.el.getWidth();
32226 //this.gutter = this.el.getWidth();
32228 this.measureColumns();
32234 this.colYs.push( 0 );
32240 measureColumns : function()
32242 this.getContainerWidth();
32243 // if columnWidth is 0, default to outerWidth of first item
32244 if ( !this.columnWidth ) {
32245 var firstItem = this.bricks.first();
32246 Roo.log(firstItem);
32247 this.columnWidth = this.containerWidth;
32248 if (firstItem && firstItem.attr('originalwidth') ) {
32249 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32251 // columnWidth fall back to item of first element
32252 Roo.log("set column width?");
32253 this.initialColumnWidth = this.columnWidth ;
32255 // if first elem has no width, default to size of container
32260 if (this.initialColumnWidth) {
32261 this.columnWidth = this.initialColumnWidth;
32266 // column width is fixed at the top - however if container width get's smaller we should
32269 // this bit calcs how man columns..
32271 var columnWidth = this.columnWidth += this.gutter;
32273 // calculate columns
32274 var containerWidth = this.containerWidth + this.gutter;
32276 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32277 // fix rounding errors, typically with gutters
32278 var excess = columnWidth - containerWidth % columnWidth;
32281 // if overshoot is less than a pixel, round up, otherwise floor it
32282 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32283 cols = Math[ mathMethod ]( cols );
32284 this.cols = Math.max( cols, 1 );
32285 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32287 // padding positioning..
32288 var totalColWidth = this.cols * this.columnWidth;
32289 var padavail = this.containerWidth - totalColWidth;
32290 // so for 2 columns - we need 3 'pads'
32292 var padNeeded = (1+this.cols) * this.padWidth;
32294 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32296 this.columnWidth += padExtra
32297 //this.padWidth = Math.floor(padavail / ( this.cols));
32299 // adjust colum width so that padding is fixed??
32301 // we have 3 columns ... total = width * 3
32302 // we have X left over... that should be used by
32304 //if (this.expandC) {
32312 getContainerWidth : function()
32314 /* // container is parent if fit width
32315 var container = this.isFitWidth ? this.element.parentNode : this.element;
32316 // check that this.size and size are there
32317 // IE8 triggers resize on body size change, so they might not be
32319 var size = getSize( container ); //FIXME
32320 this.containerWidth = size && size.innerWidth; //FIXME
32323 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
32327 _getItemLayoutPosition : function( item ) // what is item?
32329 // we resize the item to our columnWidth..
32331 item.setWidth(this.columnWidth);
32332 item.autoBoxAdjust = false;
32334 var sz = item.getSize();
32336 // how many columns does this brick span
32337 var remainder = this.containerWidth % this.columnWidth;
32339 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32340 // round if off by 1 pixel, otherwise use ceil
32341 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
32342 colSpan = Math.min( colSpan, this.cols );
32344 // normally this should be '1' as we dont' currently allow multi width columns..
32346 var colGroup = this._getColGroup( colSpan );
32347 // get the minimum Y value from the columns
32348 var minimumY = Math.min.apply( Math, colGroup );
32349 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32351 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
32353 // position the brick
32355 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32356 y: this.currentSize.y + minimumY + this.padHeight
32360 // apply setHeight to necessary columns
32361 var setHeight = minimumY + sz.height + this.padHeight;
32362 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
32364 var setSpan = this.cols + 1 - colGroup.length;
32365 for ( var i = 0; i < setSpan; i++ ) {
32366 this.colYs[ shortColIndex + i ] = setHeight ;
32373 * @param {Number} colSpan - number of columns the element spans
32374 * @returns {Array} colGroup
32376 _getColGroup : function( colSpan )
32378 if ( colSpan < 2 ) {
32379 // if brick spans only one column, use all the column Ys
32384 // how many different places could this brick fit horizontally
32385 var groupCount = this.cols + 1 - colSpan;
32386 // for each group potential horizontal position
32387 for ( var i = 0; i < groupCount; i++ ) {
32388 // make an array of colY values for that one group
32389 var groupColYs = this.colYs.slice( i, i + colSpan );
32390 // and get the max value of the array
32391 colGroup[i] = Math.max.apply( Math, groupColYs );
32396 _manageStamp : function( stamp )
32398 var stampSize = stamp.getSize();
32399 var offset = stamp.getBox();
32400 // get the columns that this stamp affects
32401 var firstX = this.isOriginLeft ? offset.x : offset.right;
32402 var lastX = firstX + stampSize.width;
32403 var firstCol = Math.floor( firstX / this.columnWidth );
32404 firstCol = Math.max( 0, firstCol );
32406 var lastCol = Math.floor( lastX / this.columnWidth );
32407 // lastCol should not go over if multiple of columnWidth #425
32408 lastCol -= lastX % this.columnWidth ? 0 : 1;
32409 lastCol = Math.min( this.cols - 1, lastCol );
32411 // set colYs to bottom of the stamp
32412 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32415 for ( var i = firstCol; i <= lastCol; i++ ) {
32416 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32421 _getContainerSize : function()
32423 this.maxY = Math.max.apply( Math, this.colYs );
32428 if ( this.isFitWidth ) {
32429 size.width = this._getContainerFitWidth();
32435 _getContainerFitWidth : function()
32437 var unusedCols = 0;
32438 // count unused columns
32441 if ( this.colYs[i] !== 0 ) {
32446 // fit container to columns that have been used
32447 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32450 needsResizeLayout : function()
32452 var previousWidth = this.containerWidth;
32453 this.getContainerWidth();
32454 return previousWidth !== this.containerWidth;
32469 * @class Roo.bootstrap.MasonryBrick
32470 * @extends Roo.bootstrap.Component
32471 * Bootstrap MasonryBrick class
32474 * Create a new MasonryBrick
32475 * @param {Object} config The config object
32478 Roo.bootstrap.MasonryBrick = function(config){
32480 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32482 Roo.bootstrap.MasonryBrick.register(this);
32488 * When a MasonryBrick is clcik
32489 * @param {Roo.bootstrap.MasonryBrick} this
32490 * @param {Roo.EventObject} e
32496 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
32499 * @cfg {String} title
32503 * @cfg {String} html
32507 * @cfg {String} bgimage
32511 * @cfg {String} videourl
32515 * @cfg {String} cls
32519 * @cfg {String} href
32523 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32528 * @cfg {String} placetitle (center|bottom)
32533 * @cfg {Boolean} isFitContainer defalut true
32535 isFitContainer : true,
32538 * @cfg {Boolean} preventDefault defalut false
32540 preventDefault : false,
32543 * @cfg {Boolean} inverse defalut false
32545 maskInverse : false,
32547 getAutoCreate : function()
32549 if(!this.isFitContainer){
32550 return this.getSplitAutoCreate();
32553 var cls = 'masonry-brick masonry-brick-full';
32555 if(this.href.length){
32556 cls += ' masonry-brick-link';
32559 if(this.bgimage.length){
32560 cls += ' masonry-brick-image';
32563 if(this.maskInverse){
32564 cls += ' mask-inverse';
32567 if(!this.html.length && !this.maskInverse && !this.videourl.length){
32568 cls += ' enable-mask';
32572 cls += ' masonry-' + this.size + '-brick';
32575 if(this.placetitle.length){
32577 switch (this.placetitle) {
32579 cls += ' masonry-center-title';
32582 cls += ' masonry-bottom-title';
32589 if(!this.html.length && !this.bgimage.length){
32590 cls += ' masonry-center-title';
32593 if(!this.html.length && this.bgimage.length){
32594 cls += ' masonry-bottom-title';
32599 cls += ' ' + this.cls;
32603 tag: (this.href.length) ? 'a' : 'div',
32608 cls: 'masonry-brick-mask'
32612 cls: 'masonry-brick-paragraph',
32618 if(this.href.length){
32619 cfg.href = this.href;
32622 var cn = cfg.cn[1].cn;
32624 if(this.title.length){
32627 cls: 'masonry-brick-title',
32632 if(this.html.length){
32635 cls: 'masonry-brick-text',
32640 if (!this.title.length && !this.html.length) {
32641 cfg.cn[1].cls += ' hide';
32644 if(this.bgimage.length){
32647 cls: 'masonry-brick-image-view',
32652 if(this.videourl.length){
32653 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32654 // youtube support only?
32657 cls: 'masonry-brick-image-view',
32660 allowfullscreen : true
32668 getSplitAutoCreate : function()
32670 var cls = 'masonry-brick masonry-brick-split';
32672 if(this.href.length){
32673 cls += ' masonry-brick-link';
32676 if(this.bgimage.length){
32677 cls += ' masonry-brick-image';
32681 cls += ' masonry-' + this.size + '-brick';
32684 switch (this.placetitle) {
32686 cls += ' masonry-center-title';
32689 cls += ' masonry-bottom-title';
32692 if(!this.bgimage.length){
32693 cls += ' masonry-center-title';
32696 if(this.bgimage.length){
32697 cls += ' masonry-bottom-title';
32703 cls += ' ' + this.cls;
32707 tag: (this.href.length) ? 'a' : 'div',
32712 cls: 'masonry-brick-split-head',
32716 cls: 'masonry-brick-paragraph',
32723 cls: 'masonry-brick-split-body',
32729 if(this.href.length){
32730 cfg.href = this.href;
32733 if(this.title.length){
32734 cfg.cn[0].cn[0].cn.push({
32736 cls: 'masonry-brick-title',
32741 if(this.html.length){
32742 cfg.cn[1].cn.push({
32744 cls: 'masonry-brick-text',
32749 if(this.bgimage.length){
32750 cfg.cn[0].cn.push({
32752 cls: 'masonry-brick-image-view',
32757 if(this.videourl.length){
32758 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32759 // youtube support only?
32760 cfg.cn[0].cn.cn.push({
32762 cls: 'masonry-brick-image-view',
32765 allowfullscreen : true
32772 initEvents: function()
32774 switch (this.size) {
32807 this.el.on('touchstart', this.onTouchStart, this);
32808 this.el.on('touchmove', this.onTouchMove, this);
32809 this.el.on('touchend', this.onTouchEnd, this);
32810 this.el.on('contextmenu', this.onContextMenu, this);
32812 this.el.on('mouseenter' ,this.enter, this);
32813 this.el.on('mouseleave', this.leave, this);
32814 this.el.on('click', this.onClick, this);
32817 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32818 this.parent().bricks.push(this);
32823 onClick: function(e, el)
32825 var time = this.endTimer - this.startTimer;
32826 // Roo.log(e.preventDefault());
32829 e.preventDefault();
32834 if(!this.preventDefault){
32838 e.preventDefault();
32840 if (this.activeClass != '') {
32841 this.selectBrick();
32844 this.fireEvent('click', this, e);
32847 enter: function(e, el)
32849 e.preventDefault();
32851 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32855 if(this.bgimage.length && this.html.length){
32856 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32860 leave: function(e, el)
32862 e.preventDefault();
32864 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32868 if(this.bgimage.length && this.html.length){
32869 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32873 onTouchStart: function(e, el)
32875 // e.preventDefault();
32877 this.touchmoved = false;
32879 if(!this.isFitContainer){
32883 if(!this.bgimage.length || !this.html.length){
32887 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32889 this.timer = new Date().getTime();
32893 onTouchMove: function(e, el)
32895 this.touchmoved = true;
32898 onContextMenu : function(e,el)
32900 e.preventDefault();
32901 e.stopPropagation();
32905 onTouchEnd: function(e, el)
32907 // e.preventDefault();
32909 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32916 if(!this.bgimage.length || !this.html.length){
32918 if(this.href.length){
32919 window.location.href = this.href;
32925 if(!this.isFitContainer){
32929 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32931 window.location.href = this.href;
32934 //selection on single brick only
32935 selectBrick : function() {
32937 if (!this.parentId) {
32941 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32942 var index = m.selectedBrick.indexOf(this.id);
32945 m.selectedBrick.splice(index,1);
32946 this.el.removeClass(this.activeClass);
32950 for(var i = 0; i < m.selectedBrick.length; i++) {
32951 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32952 b.el.removeClass(b.activeClass);
32955 m.selectedBrick = [];
32957 m.selectedBrick.push(this.id);
32958 this.el.addClass(this.activeClass);
32962 isSelected : function(){
32963 return this.el.hasClass(this.activeClass);
32968 Roo.apply(Roo.bootstrap.MasonryBrick, {
32971 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32973 * register a Masonry Brick
32974 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32977 register : function(brick)
32979 //this.groups[brick.id] = brick;
32980 this.groups.add(brick.id, brick);
32983 * fetch a masonry brick based on the masonry brick ID
32984 * @param {string} the masonry brick to add
32985 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32988 get: function(brick_id)
32990 // if (typeof(this.groups[brick_id]) == 'undefined') {
32993 // return this.groups[brick_id] ;
32995 if(this.groups.key(brick_id)) {
32996 return this.groups.key(brick_id);
33014 * @class Roo.bootstrap.Brick
33015 * @extends Roo.bootstrap.Component
33016 * Bootstrap Brick class
33019 * Create a new Brick
33020 * @param {Object} config The config object
33023 Roo.bootstrap.Brick = function(config){
33024 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33030 * When a Brick is click
33031 * @param {Roo.bootstrap.Brick} this
33032 * @param {Roo.EventObject} e
33038 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
33041 * @cfg {String} title
33045 * @cfg {String} html
33049 * @cfg {String} bgimage
33053 * @cfg {String} cls
33057 * @cfg {String} href
33061 * @cfg {String} video
33065 * @cfg {Boolean} square
33069 getAutoCreate : function()
33071 var cls = 'roo-brick';
33073 if(this.href.length){
33074 cls += ' roo-brick-link';
33077 if(this.bgimage.length){
33078 cls += ' roo-brick-image';
33081 if(!this.html.length && !this.bgimage.length){
33082 cls += ' roo-brick-center-title';
33085 if(!this.html.length && this.bgimage.length){
33086 cls += ' roo-brick-bottom-title';
33090 cls += ' ' + this.cls;
33094 tag: (this.href.length) ? 'a' : 'div',
33099 cls: 'roo-brick-paragraph',
33105 if(this.href.length){
33106 cfg.href = this.href;
33109 var cn = cfg.cn[0].cn;
33111 if(this.title.length){
33114 cls: 'roo-brick-title',
33119 if(this.html.length){
33122 cls: 'roo-brick-text',
33129 if(this.bgimage.length){
33132 cls: 'roo-brick-image-view',
33140 initEvents: function()
33142 if(this.title.length || this.html.length){
33143 this.el.on('mouseenter' ,this.enter, this);
33144 this.el.on('mouseleave', this.leave, this);
33147 Roo.EventManager.onWindowResize(this.resize, this);
33149 if(this.bgimage.length){
33150 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33151 this.imageEl.on('load', this.onImageLoad, this);
33158 onImageLoad : function()
33163 resize : function()
33165 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33167 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33169 if(this.bgimage.length){
33170 var image = this.el.select('.roo-brick-image-view', true).first();
33172 image.setWidth(paragraph.getWidth());
33175 image.setHeight(paragraph.getWidth());
33178 this.el.setHeight(image.getHeight());
33179 paragraph.setHeight(image.getHeight());
33185 enter: function(e, el)
33187 e.preventDefault();
33189 if(this.bgimage.length){
33190 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33191 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33195 leave: function(e, el)
33197 e.preventDefault();
33199 if(this.bgimage.length){
33200 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33201 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33216 * @class Roo.bootstrap.NumberField
33217 * @extends Roo.bootstrap.Input
33218 * Bootstrap NumberField class
33224 * Create a new NumberField
33225 * @param {Object} config The config object
33228 Roo.bootstrap.NumberField = function(config){
33229 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33232 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33235 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33237 allowDecimals : true,
33239 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33241 decimalSeparator : ".",
33243 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33245 decimalPrecision : 2,
33247 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33249 allowNegative : true,
33252 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33256 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33258 minValue : Number.NEGATIVE_INFINITY,
33260 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33262 maxValue : Number.MAX_VALUE,
33264 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33266 minText : "The minimum value for this field is {0}",
33268 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33270 maxText : "The maximum value for this field is {0}",
33272 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
33273 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33275 nanText : "{0} is not a valid number",
33277 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33279 thousandsDelimiter : false,
33281 * @cfg {String} valueAlign alignment of value
33283 valueAlign : "left",
33285 getAutoCreate : function()
33287 var hiddenInput = {
33291 cls: 'hidden-number-input'
33295 hiddenInput.name = this.name;
33300 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33302 this.name = hiddenInput.name;
33304 if(cfg.cn.length > 0) {
33305 cfg.cn.push(hiddenInput);
33312 initEvents : function()
33314 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33316 var allowed = "0123456789";
33318 if(this.allowDecimals){
33319 allowed += this.decimalSeparator;
33322 if(this.allowNegative){
33326 if(this.thousandsDelimiter) {
33330 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33332 var keyPress = function(e){
33334 var k = e.getKey();
33336 var c = e.getCharCode();
33339 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33340 allowed.indexOf(String.fromCharCode(c)) === -1
33346 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33350 if(allowed.indexOf(String.fromCharCode(c)) === -1){
33355 this.el.on("keypress", keyPress, this);
33358 validateValue : function(value)
33361 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33365 var num = this.parseValue(value);
33368 this.markInvalid(String.format(this.nanText, value));
33372 if(num < this.minValue){
33373 this.markInvalid(String.format(this.minText, this.minValue));
33377 if(num > this.maxValue){
33378 this.markInvalid(String.format(this.maxText, this.maxValue));
33385 getValue : function()
33387 var v = this.hiddenEl().getValue();
33389 return this.fixPrecision(this.parseValue(v));
33392 parseValue : function(value)
33394 if(this.thousandsDelimiter) {
33396 r = new RegExp(",", "g");
33397 value = value.replace(r, "");
33400 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33401 return isNaN(value) ? '' : value;
33404 fixPrecision : function(value)
33406 if(this.thousandsDelimiter) {
33408 r = new RegExp(",", "g");
33409 value = value.replace(r, "");
33412 var nan = isNaN(value);
33414 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33415 return nan ? '' : value;
33417 return parseFloat(value).toFixed(this.decimalPrecision);
33420 setValue : function(v)
33422 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33428 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33430 this.inputEl().dom.value = (v == '') ? '' :
33431 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33433 if(!this.allowZero && v === '0') {
33434 this.hiddenEl().dom.value = '';
33435 this.inputEl().dom.value = '';
33442 decimalPrecisionFcn : function(v)
33444 return Math.floor(v);
33447 beforeBlur : function()
33449 var v = this.parseValue(this.getRawValue());
33451 if(v || v === 0 || v === ''){
33456 hiddenEl : function()
33458 return this.el.select('input.hidden-number-input',true).first();
33470 * @class Roo.bootstrap.DocumentSlider
33471 * @extends Roo.bootstrap.Component
33472 * Bootstrap DocumentSlider class
33475 * Create a new DocumentViewer
33476 * @param {Object} config The config object
33479 Roo.bootstrap.DocumentSlider = function(config){
33480 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33487 * Fire after initEvent
33488 * @param {Roo.bootstrap.DocumentSlider} this
33493 * Fire after update
33494 * @param {Roo.bootstrap.DocumentSlider} this
33500 * @param {Roo.bootstrap.DocumentSlider} this
33506 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
33512 getAutoCreate : function()
33516 cls : 'roo-document-slider',
33520 cls : 'roo-document-slider-header',
33524 cls : 'roo-document-slider-header-title'
33530 cls : 'roo-document-slider-body',
33534 cls : 'roo-document-slider-prev',
33538 cls : 'fa fa-chevron-left'
33544 cls : 'roo-document-slider-thumb',
33548 cls : 'roo-document-slider-image'
33554 cls : 'roo-document-slider-next',
33558 cls : 'fa fa-chevron-right'
33570 initEvents : function()
33572 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33573 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33575 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33576 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33578 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33579 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33581 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33582 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33584 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33585 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33587 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33588 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33590 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33591 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33593 this.thumbEl.on('click', this.onClick, this);
33595 this.prevIndicator.on('click', this.prev, this);
33597 this.nextIndicator.on('click', this.next, this);
33601 initial : function()
33603 if(this.files.length){
33604 this.indicator = 1;
33608 this.fireEvent('initial', this);
33611 update : function()
33613 this.imageEl.attr('src', this.files[this.indicator - 1]);
33615 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33617 this.prevIndicator.show();
33619 if(this.indicator == 1){
33620 this.prevIndicator.hide();
33623 this.nextIndicator.show();
33625 if(this.indicator == this.files.length){
33626 this.nextIndicator.hide();
33629 this.thumbEl.scrollTo('top');
33631 this.fireEvent('update', this);
33634 onClick : function(e)
33636 e.preventDefault();
33638 this.fireEvent('click', this);
33643 e.preventDefault();
33645 this.indicator = Math.max(1, this.indicator - 1);
33652 e.preventDefault();
33654 this.indicator = Math.min(this.files.length, this.indicator + 1);
33668 * @class Roo.bootstrap.RadioSet
33669 * @extends Roo.bootstrap.Input
33670 * Bootstrap RadioSet class
33671 * @cfg {String} indicatorpos (left|right) default left
33672 * @cfg {Boolean} inline (true|false) inline the element (default true)
33673 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33675 * Create a new RadioSet
33676 * @param {Object} config The config object
33679 Roo.bootstrap.RadioSet = function(config){
33681 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33685 Roo.bootstrap.RadioSet.register(this);
33690 * Fires when the element is checked or unchecked.
33691 * @param {Roo.bootstrap.RadioSet} this This radio
33692 * @param {Roo.bootstrap.Radio} item The checked item
33697 * Fires when the element is click.
33698 * @param {Roo.bootstrap.RadioSet} this This radio set
33699 * @param {Roo.bootstrap.Radio} item The checked item
33700 * @param {Roo.EventObject} e The event object
33707 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
33715 indicatorpos : 'left',
33717 getAutoCreate : function()
33721 cls : 'roo-radio-set-label',
33725 html : this.fieldLabel
33730 if(this.indicatorpos == 'left'){
33733 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33734 tooltip : 'This field is required'
33739 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33740 tooltip : 'This field is required'
33746 cls : 'roo-radio-set-items'
33749 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33751 if (align === 'left' && this.fieldLabel.length) {
33754 cls : "roo-radio-set-right",
33760 if(this.labelWidth > 12){
33761 label.style = "width: " + this.labelWidth + 'px';
33764 if(this.labelWidth < 13 && this.labelmd == 0){
33765 this.labelmd = this.labelWidth;
33768 if(this.labellg > 0){
33769 label.cls += ' col-lg-' + this.labellg;
33770 items.cls += ' col-lg-' + (12 - this.labellg);
33773 if(this.labelmd > 0){
33774 label.cls += ' col-md-' + this.labelmd;
33775 items.cls += ' col-md-' + (12 - this.labelmd);
33778 if(this.labelsm > 0){
33779 label.cls += ' col-sm-' + this.labelsm;
33780 items.cls += ' col-sm-' + (12 - this.labelsm);
33783 if(this.labelxs > 0){
33784 label.cls += ' col-xs-' + this.labelxs;
33785 items.cls += ' col-xs-' + (12 - this.labelxs);
33791 cls : 'roo-radio-set',
33795 cls : 'roo-radio-set-input',
33798 value : this.value ? this.value : ''
33805 if(this.weight.length){
33806 cfg.cls += ' roo-radio-' + this.weight;
33810 cfg.cls += ' roo-radio-set-inline';
33814 ['xs','sm','md','lg'].map(function(size){
33815 if (settings[size]) {
33816 cfg.cls += ' col-' + size + '-' + settings[size];
33824 initEvents : function()
33826 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33827 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33829 if(!this.fieldLabel.length){
33830 this.labelEl.hide();
33833 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33834 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33836 this.indicator = this.indicatorEl();
33838 if(this.indicator){
33839 this.indicator.addClass('invisible');
33842 this.originalValue = this.getValue();
33846 inputEl: function ()
33848 return this.el.select('.roo-radio-set-input', true).first();
33851 getChildContainer : function()
33853 return this.itemsEl;
33856 register : function(item)
33858 this.radioes.push(item);
33862 validate : function()
33864 if(this.getVisibilityEl().hasClass('hidden')){
33870 Roo.each(this.radioes, function(i){
33879 if(this.allowBlank) {
33883 if(this.disabled || valid){
33888 this.markInvalid();
33893 markValid : function()
33895 if(this.labelEl.isVisible(true)){
33896 this.indicatorEl().removeClass('visible');
33897 this.indicatorEl().addClass('invisible');
33900 this.el.removeClass([this.invalidClass, this.validClass]);
33901 this.el.addClass(this.validClass);
33903 this.fireEvent('valid', this);
33906 markInvalid : function(msg)
33908 if(this.allowBlank || this.disabled){
33912 if(this.labelEl.isVisible(true)){
33913 this.indicatorEl().removeClass('invisible');
33914 this.indicatorEl().addClass('visible');
33917 this.el.removeClass([this.invalidClass, this.validClass]);
33918 this.el.addClass(this.invalidClass);
33920 this.fireEvent('invalid', this, msg);
33924 setValue : function(v, suppressEvent)
33926 if(this.value === v){
33933 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33936 Roo.each(this.radioes, function(i){
33938 i.el.removeClass('checked');
33941 Roo.each(this.radioes, function(i){
33943 if(i.value === v || i.value.toString() === v.toString()){
33945 i.el.addClass('checked');
33947 if(suppressEvent !== true){
33948 this.fireEvent('check', this, i);
33959 clearInvalid : function(){
33961 if(!this.el || this.preventMark){
33965 this.el.removeClass([this.invalidClass]);
33967 this.fireEvent('valid', this);
33972 Roo.apply(Roo.bootstrap.RadioSet, {
33976 register : function(set)
33978 this.groups[set.name] = set;
33981 get: function(name)
33983 if (typeof(this.groups[name]) == 'undefined') {
33987 return this.groups[name] ;
33993 * Ext JS Library 1.1.1
33994 * Copyright(c) 2006-2007, Ext JS, LLC.
33996 * Originally Released Under LGPL - original licence link has changed is not relivant.
33999 * <script type="text/javascript">
34004 * @class Roo.bootstrap.SplitBar
34005 * @extends Roo.util.Observable
34006 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34010 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34011 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34012 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34013 split.minSize = 100;
34014 split.maxSize = 600;
34015 split.animate = true;
34016 split.on('moved', splitterMoved);
34019 * Create a new SplitBar
34020 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
34021 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
34022 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34023 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
34024 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34025 position of the SplitBar).
34027 Roo.bootstrap.SplitBar = function(cfg){
34032 // dragElement : elm
34033 // resizingElement: el,
34035 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34036 // placement : Roo.bootstrap.SplitBar.LEFT ,
34037 // existingProxy ???
34040 this.el = Roo.get(cfg.dragElement, true);
34041 this.el.dom.unselectable = "on";
34043 this.resizingEl = Roo.get(cfg.resizingElement, true);
34047 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34048 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34051 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34054 * The minimum size of the resizing element. (Defaults to 0)
34060 * The maximum size of the resizing element. (Defaults to 2000)
34063 this.maxSize = 2000;
34066 * Whether to animate the transition to the new size
34069 this.animate = false;
34072 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34075 this.useShim = false;
34080 if(!cfg.existingProxy){
34082 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34084 this.proxy = Roo.get(cfg.existingProxy).dom;
34087 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34090 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34093 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34096 this.dragSpecs = {};
34099 * @private The adapter to use to positon and resize elements
34101 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34102 this.adapter.init(this);
34104 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34106 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34107 this.el.addClass("roo-splitbar-h");
34110 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34111 this.el.addClass("roo-splitbar-v");
34117 * Fires when the splitter is moved (alias for {@link #event-moved})
34118 * @param {Roo.bootstrap.SplitBar} this
34119 * @param {Number} newSize the new width or height
34124 * Fires when the splitter is moved
34125 * @param {Roo.bootstrap.SplitBar} this
34126 * @param {Number} newSize the new width or height
34130 * @event beforeresize
34131 * Fires before the splitter is dragged
34132 * @param {Roo.bootstrap.SplitBar} this
34134 "beforeresize" : true,
34136 "beforeapply" : true
34139 Roo.util.Observable.call(this);
34142 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34143 onStartProxyDrag : function(x, y){
34144 this.fireEvent("beforeresize", this);
34146 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
34148 o.enableDisplayMode("block");
34149 // all splitbars share the same overlay
34150 Roo.bootstrap.SplitBar.prototype.overlay = o;
34152 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34153 this.overlay.show();
34154 Roo.get(this.proxy).setDisplayed("block");
34155 var size = this.adapter.getElementSize(this);
34156 this.activeMinSize = this.getMinimumSize();;
34157 this.activeMaxSize = this.getMaximumSize();;
34158 var c1 = size - this.activeMinSize;
34159 var c2 = Math.max(this.activeMaxSize - size, 0);
34160 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34161 this.dd.resetConstraints();
34162 this.dd.setXConstraint(
34163 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
34164 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34166 this.dd.setYConstraint(0, 0);
34168 this.dd.resetConstraints();
34169 this.dd.setXConstraint(0, 0);
34170 this.dd.setYConstraint(
34171 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
34172 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34175 this.dragSpecs.startSize = size;
34176 this.dragSpecs.startPoint = [x, y];
34177 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34181 * @private Called after the drag operation by the DDProxy
34183 onEndProxyDrag : function(e){
34184 Roo.get(this.proxy).setDisplayed(false);
34185 var endPoint = Roo.lib.Event.getXY(e);
34187 this.overlay.hide();
34190 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34191 newSize = this.dragSpecs.startSize +
34192 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34193 endPoint[0] - this.dragSpecs.startPoint[0] :
34194 this.dragSpecs.startPoint[0] - endPoint[0]
34197 newSize = this.dragSpecs.startSize +
34198 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34199 endPoint[1] - this.dragSpecs.startPoint[1] :
34200 this.dragSpecs.startPoint[1] - endPoint[1]
34203 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34204 if(newSize != this.dragSpecs.startSize){
34205 if(this.fireEvent('beforeapply', this, newSize) !== false){
34206 this.adapter.setElementSize(this, newSize);
34207 this.fireEvent("moved", this, newSize);
34208 this.fireEvent("resize", this, newSize);
34214 * Get the adapter this SplitBar uses
34215 * @return The adapter object
34217 getAdapter : function(){
34218 return this.adapter;
34222 * Set the adapter this SplitBar uses
34223 * @param {Object} adapter A SplitBar adapter object
34225 setAdapter : function(adapter){
34226 this.adapter = adapter;
34227 this.adapter.init(this);
34231 * Gets the minimum size for the resizing element
34232 * @return {Number} The minimum size
34234 getMinimumSize : function(){
34235 return this.minSize;
34239 * Sets the minimum size for the resizing element
34240 * @param {Number} minSize The minimum size
34242 setMinimumSize : function(minSize){
34243 this.minSize = minSize;
34247 * Gets the maximum size for the resizing element
34248 * @return {Number} The maximum size
34250 getMaximumSize : function(){
34251 return this.maxSize;
34255 * Sets the maximum size for the resizing element
34256 * @param {Number} maxSize The maximum size
34258 setMaximumSize : function(maxSize){
34259 this.maxSize = maxSize;
34263 * Sets the initialize size for the resizing element
34264 * @param {Number} size The initial size
34266 setCurrentSize : function(size){
34267 var oldAnimate = this.animate;
34268 this.animate = false;
34269 this.adapter.setElementSize(this, size);
34270 this.animate = oldAnimate;
34274 * Destroy this splitbar.
34275 * @param {Boolean} removeEl True to remove the element
34277 destroy : function(removeEl){
34279 this.shim.remove();
34282 this.proxy.parentNode.removeChild(this.proxy);
34290 * @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.
34292 Roo.bootstrap.SplitBar.createProxy = function(dir){
34293 var proxy = new Roo.Element(document.createElement("div"));
34294 proxy.unselectable();
34295 var cls = 'roo-splitbar-proxy';
34296 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34297 document.body.appendChild(proxy.dom);
34302 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34303 * Default Adapter. It assumes the splitter and resizing element are not positioned
34304 * elements and only gets/sets the width of the element. Generally used for table based layouts.
34306 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34309 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34310 // do nothing for now
34311 init : function(s){
34315 * Called before drag operations to get the current size of the resizing element.
34316 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34318 getElementSize : function(s){
34319 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34320 return s.resizingEl.getWidth();
34322 return s.resizingEl.getHeight();
34327 * Called after drag operations to set the size of the resizing element.
34328 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34329 * @param {Number} newSize The new size to set
34330 * @param {Function} onComplete A function to be invoked when resizing is complete
34332 setElementSize : function(s, newSize, onComplete){
34333 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34335 s.resizingEl.setWidth(newSize);
34337 onComplete(s, newSize);
34340 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34345 s.resizingEl.setHeight(newSize);
34347 onComplete(s, newSize);
34350 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34357 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34358 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34359 * Adapter that moves the splitter element to align with the resized sizing element.
34360 * Used with an absolute positioned SplitBar.
34361 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34362 * document.body, make sure you assign an id to the body element.
34364 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34365 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34366 this.container = Roo.get(container);
34369 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34370 init : function(s){
34371 this.basic.init(s);
34374 getElementSize : function(s){
34375 return this.basic.getElementSize(s);
34378 setElementSize : function(s, newSize, onComplete){
34379 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34382 moveSplitter : function(s){
34383 var yes = Roo.bootstrap.SplitBar;
34384 switch(s.placement){
34386 s.el.setX(s.resizingEl.getRight());
34389 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34392 s.el.setY(s.resizingEl.getBottom());
34395 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34402 * Orientation constant - Create a vertical SplitBar
34406 Roo.bootstrap.SplitBar.VERTICAL = 1;
34409 * Orientation constant - Create a horizontal SplitBar
34413 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34416 * Placement constant - The resizing element is to the left of the splitter element
34420 Roo.bootstrap.SplitBar.LEFT = 1;
34423 * Placement constant - The resizing element is to the right of the splitter element
34427 Roo.bootstrap.SplitBar.RIGHT = 2;
34430 * Placement constant - The resizing element is positioned above the splitter element
34434 Roo.bootstrap.SplitBar.TOP = 3;
34437 * Placement constant - The resizing element is positioned under splitter element
34441 Roo.bootstrap.SplitBar.BOTTOM = 4;
34442 Roo.namespace("Roo.bootstrap.layout");/*
34444 * Ext JS Library 1.1.1
34445 * Copyright(c) 2006-2007, Ext JS, LLC.
34447 * Originally Released Under LGPL - original licence link has changed is not relivant.
34450 * <script type="text/javascript">
34454 * @class Roo.bootstrap.layout.Manager
34455 * @extends Roo.bootstrap.Component
34456 * Base class for layout managers.
34458 Roo.bootstrap.layout.Manager = function(config)
34460 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34466 /** false to disable window resize monitoring @type Boolean */
34467 this.monitorWindowResize = true;
34472 * Fires when a layout is performed.
34473 * @param {Roo.LayoutManager} this
34477 * @event regionresized
34478 * Fires when the user resizes a region.
34479 * @param {Roo.LayoutRegion} region The resized region
34480 * @param {Number} newSize The new size (width for east/west, height for north/south)
34482 "regionresized" : true,
34484 * @event regioncollapsed
34485 * Fires when a region is collapsed.
34486 * @param {Roo.LayoutRegion} region The collapsed region
34488 "regioncollapsed" : true,
34490 * @event regionexpanded
34491 * Fires when a region is expanded.
34492 * @param {Roo.LayoutRegion} region The expanded region
34494 "regionexpanded" : true
34496 this.updating = false;
34499 this.el = Roo.get(config.el);
34505 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34510 monitorWindowResize : true,
34516 onRender : function(ct, position)
34519 this.el = Roo.get(ct);
34522 //this.fireEvent('render',this);
34526 initEvents: function()
34530 // ie scrollbar fix
34531 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34532 document.body.scroll = "no";
34533 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34534 this.el.position('relative');
34536 this.id = this.el.id;
34537 this.el.addClass("roo-layout-container");
34538 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34539 if(this.el.dom != document.body ) {
34540 this.el.on('resize', this.layout,this);
34541 this.el.on('show', this.layout,this);
34547 * Returns true if this layout is currently being updated
34548 * @return {Boolean}
34550 isUpdating : function(){
34551 return this.updating;
34555 * Suspend the LayoutManager from doing auto-layouts while
34556 * making multiple add or remove calls
34558 beginUpdate : function(){
34559 this.updating = true;
34563 * Restore auto-layouts and optionally disable the manager from performing a layout
34564 * @param {Boolean} noLayout true to disable a layout update
34566 endUpdate : function(noLayout){
34567 this.updating = false;
34573 layout: function(){
34577 onRegionResized : function(region, newSize){
34578 this.fireEvent("regionresized", region, newSize);
34582 onRegionCollapsed : function(region){
34583 this.fireEvent("regioncollapsed", region);
34586 onRegionExpanded : function(region){
34587 this.fireEvent("regionexpanded", region);
34591 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34592 * performs box-model adjustments.
34593 * @return {Object} The size as an object {width: (the width), height: (the height)}
34595 getViewSize : function()
34598 if(this.el.dom != document.body){
34599 size = this.el.getSize();
34601 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34603 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34604 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34609 * Returns the Element this layout is bound to.
34610 * @return {Roo.Element}
34612 getEl : function(){
34617 * Returns the specified region.
34618 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34619 * @return {Roo.LayoutRegion}
34621 getRegion : function(target){
34622 return this.regions[target.toLowerCase()];
34625 onWindowResize : function(){
34626 if(this.monitorWindowResize){
34633 * Ext JS Library 1.1.1
34634 * Copyright(c) 2006-2007, Ext JS, LLC.
34636 * Originally Released Under LGPL - original licence link has changed is not relivant.
34639 * <script type="text/javascript">
34642 * @class Roo.bootstrap.layout.Border
34643 * @extends Roo.bootstrap.layout.Manager
34644 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34645 * please see: examples/bootstrap/nested.html<br><br>
34647 <b>The container the layout is rendered into can be either the body element or any other element.
34648 If it is not the body element, the container needs to either be an absolute positioned element,
34649 or you will need to add "position:relative" to the css of the container. You will also need to specify
34650 the container size if it is not the body element.</b>
34653 * Create a new Border
34654 * @param {Object} config Configuration options
34656 Roo.bootstrap.layout.Border = function(config){
34657 config = config || {};
34658 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34662 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34663 if(config[region]){
34664 config[region].region = region;
34665 this.addRegion(config[region]);
34671 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
34673 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34675 * Creates and adds a new region if it doesn't already exist.
34676 * @param {String} target The target region key (north, south, east, west or center).
34677 * @param {Object} config The regions config object
34678 * @return {BorderLayoutRegion} The new region
34680 addRegion : function(config)
34682 if(!this.regions[config.region]){
34683 var r = this.factory(config);
34684 this.bindRegion(r);
34686 return this.regions[config.region];
34690 bindRegion : function(r){
34691 this.regions[r.config.region] = r;
34693 r.on("visibilitychange", this.layout, this);
34694 r.on("paneladded", this.layout, this);
34695 r.on("panelremoved", this.layout, this);
34696 r.on("invalidated", this.layout, this);
34697 r.on("resized", this.onRegionResized, this);
34698 r.on("collapsed", this.onRegionCollapsed, this);
34699 r.on("expanded", this.onRegionExpanded, this);
34703 * Performs a layout update.
34705 layout : function()
34707 if(this.updating) {
34711 // render all the rebions if they have not been done alreayd?
34712 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34713 if(this.regions[region] && !this.regions[region].bodyEl){
34714 this.regions[region].onRender(this.el)
34718 var size = this.getViewSize();
34719 var w = size.width;
34720 var h = size.height;
34725 //var x = 0, y = 0;
34727 var rs = this.regions;
34728 var north = rs["north"];
34729 var south = rs["south"];
34730 var west = rs["west"];
34731 var east = rs["east"];
34732 var center = rs["center"];
34733 //if(this.hideOnLayout){ // not supported anymore
34734 //c.el.setStyle("display", "none");
34736 if(north && north.isVisible()){
34737 var b = north.getBox();
34738 var m = north.getMargins();
34739 b.width = w - (m.left+m.right);
34742 centerY = b.height + b.y + m.bottom;
34743 centerH -= centerY;
34744 north.updateBox(this.safeBox(b));
34746 if(south && south.isVisible()){
34747 var b = south.getBox();
34748 var m = south.getMargins();
34749 b.width = w - (m.left+m.right);
34751 var totalHeight = (b.height + m.top + m.bottom);
34752 b.y = h - totalHeight + m.top;
34753 centerH -= totalHeight;
34754 south.updateBox(this.safeBox(b));
34756 if(west && west.isVisible()){
34757 var b = west.getBox();
34758 var m = west.getMargins();
34759 b.height = centerH - (m.top+m.bottom);
34761 b.y = centerY + m.top;
34762 var totalWidth = (b.width + m.left + m.right);
34763 centerX += totalWidth;
34764 centerW -= totalWidth;
34765 west.updateBox(this.safeBox(b));
34767 if(east && east.isVisible()){
34768 var b = east.getBox();
34769 var m = east.getMargins();
34770 b.height = centerH - (m.top+m.bottom);
34771 var totalWidth = (b.width + m.left + m.right);
34772 b.x = w - totalWidth + m.left;
34773 b.y = centerY + m.top;
34774 centerW -= totalWidth;
34775 east.updateBox(this.safeBox(b));
34778 var m = center.getMargins();
34780 x: centerX + m.left,
34781 y: centerY + m.top,
34782 width: centerW - (m.left+m.right),
34783 height: centerH - (m.top+m.bottom)
34785 //if(this.hideOnLayout){
34786 //center.el.setStyle("display", "block");
34788 center.updateBox(this.safeBox(centerBox));
34791 this.fireEvent("layout", this);
34795 safeBox : function(box){
34796 box.width = Math.max(0, box.width);
34797 box.height = Math.max(0, box.height);
34802 * Adds a ContentPanel (or subclass) to this layout.
34803 * @param {String} target The target region key (north, south, east, west or center).
34804 * @param {Roo.ContentPanel} panel The panel to add
34805 * @return {Roo.ContentPanel} The added panel
34807 add : function(target, panel){
34809 target = target.toLowerCase();
34810 return this.regions[target].add(panel);
34814 * Remove a ContentPanel (or subclass) to this layout.
34815 * @param {String} target The target region key (north, south, east, west or center).
34816 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34817 * @return {Roo.ContentPanel} The removed panel
34819 remove : function(target, panel){
34820 target = target.toLowerCase();
34821 return this.regions[target].remove(panel);
34825 * Searches all regions for a panel with the specified id
34826 * @param {String} panelId
34827 * @return {Roo.ContentPanel} The panel or null if it wasn't found
34829 findPanel : function(panelId){
34830 var rs = this.regions;
34831 for(var target in rs){
34832 if(typeof rs[target] != "function"){
34833 var p = rs[target].getPanel(panelId);
34843 * Searches all regions for a panel with the specified id and activates (shows) it.
34844 * @param {String/ContentPanel} panelId The panels id or the panel itself
34845 * @return {Roo.ContentPanel} The shown panel or null
34847 showPanel : function(panelId) {
34848 var rs = this.regions;
34849 for(var target in rs){
34850 var r = rs[target];
34851 if(typeof r != "function"){
34852 if(r.hasPanel(panelId)){
34853 return r.showPanel(panelId);
34861 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34862 * @param {Roo.state.Provider} provider (optional) An alternate state provider
34865 restoreState : function(provider){
34867 provider = Roo.state.Manager;
34869 var sm = new Roo.LayoutStateManager();
34870 sm.init(this, provider);
34876 * Adds a xtype elements to the layout.
34880 xtype : 'ContentPanel',
34887 xtype : 'NestedLayoutPanel',
34893 items : [ ... list of content panels or nested layout panels.. ]
34897 * @param {Object} cfg Xtype definition of item to add.
34899 addxtype : function(cfg)
34901 // basically accepts a pannel...
34902 // can accept a layout region..!?!?
34903 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34906 // theory? children can only be panels??
34908 //if (!cfg.xtype.match(/Panel$/)) {
34913 if (typeof(cfg.region) == 'undefined') {
34914 Roo.log("Failed to add Panel, region was not set");
34918 var region = cfg.region;
34924 xitems = cfg.items;
34931 case 'Content': // ContentPanel (el, cfg)
34932 case 'Scroll': // ContentPanel (el, cfg)
34934 cfg.autoCreate = true;
34935 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34937 // var el = this.el.createChild();
34938 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34941 this.add(region, ret);
34945 case 'TreePanel': // our new panel!
34946 cfg.el = this.el.createChild();
34947 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34948 this.add(region, ret);
34953 // create a new Layout (which is a Border Layout...
34955 var clayout = cfg.layout;
34956 clayout.el = this.el.createChild();
34957 clayout.items = clayout.items || [];
34961 // replace this exitems with the clayout ones..
34962 xitems = clayout.items;
34964 // force background off if it's in center...
34965 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34966 cfg.background = false;
34968 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
34971 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34972 //console.log('adding nested layout panel ' + cfg.toSource());
34973 this.add(region, ret);
34974 nb = {}; /// find first...
34979 // needs grid and region
34981 //var el = this.getRegion(region).el.createChild();
34983 *var el = this.el.createChild();
34984 // create the grid first...
34985 cfg.grid.container = el;
34986 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34989 if (region == 'center' && this.active ) {
34990 cfg.background = false;
34993 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34995 this.add(region, ret);
34997 if (cfg.background) {
34998 // render grid on panel activation (if panel background)
34999 ret.on('activate', function(gp) {
35000 if (!gp.grid.rendered) {
35001 // gp.grid.render(el);
35005 // cfg.grid.render(el);
35011 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35012 // it was the old xcomponent building that caused this before.
35013 // espeically if border is the top element in the tree.
35023 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35025 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35026 this.add(region, ret);
35030 throw "Can not add '" + cfg.xtype + "' to Border";
35036 this.beginUpdate();
35040 Roo.each(xitems, function(i) {
35041 region = nb && i.region ? i.region : false;
35043 var add = ret.addxtype(i);
35046 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35047 if (!i.background) {
35048 abn[region] = nb[region] ;
35055 // make the last non-background panel active..
35056 //if (nb) { Roo.log(abn); }
35059 for(var r in abn) {
35060 region = this.getRegion(r);
35062 // tried using nb[r], but it does not work..
35064 region.showPanel(abn[r]);
35075 factory : function(cfg)
35078 var validRegions = Roo.bootstrap.layout.Border.regions;
35080 var target = cfg.region;
35083 var r = Roo.bootstrap.layout;
35087 return new r.North(cfg);
35089 return new r.South(cfg);
35091 return new r.East(cfg);
35093 return new r.West(cfg);
35095 return new r.Center(cfg);
35097 throw 'Layout region "'+target+'" not supported.';
35104 * Ext JS Library 1.1.1
35105 * Copyright(c) 2006-2007, Ext JS, LLC.
35107 * Originally Released Under LGPL - original licence link has changed is not relivant.
35110 * <script type="text/javascript">
35114 * @class Roo.bootstrap.layout.Basic
35115 * @extends Roo.util.Observable
35116 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35117 * and does not have a titlebar, tabs or any other features. All it does is size and position
35118 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35119 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35120 * @cfg {string} region the region that it inhabits..
35121 * @cfg {bool} skipConfig skip config?
35125 Roo.bootstrap.layout.Basic = function(config){
35127 this.mgr = config.mgr;
35129 this.position = config.region;
35131 var skipConfig = config.skipConfig;
35135 * @scope Roo.BasicLayoutRegion
35139 * @event beforeremove
35140 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35141 * @param {Roo.LayoutRegion} this
35142 * @param {Roo.ContentPanel} panel The panel
35143 * @param {Object} e The cancel event object
35145 "beforeremove" : true,
35147 * @event invalidated
35148 * Fires when the layout for this region is changed.
35149 * @param {Roo.LayoutRegion} this
35151 "invalidated" : true,
35153 * @event visibilitychange
35154 * Fires when this region is shown or hidden
35155 * @param {Roo.LayoutRegion} this
35156 * @param {Boolean} visibility true or false
35158 "visibilitychange" : true,
35160 * @event paneladded
35161 * Fires when a panel is added.
35162 * @param {Roo.LayoutRegion} this
35163 * @param {Roo.ContentPanel} panel The panel
35165 "paneladded" : true,
35167 * @event panelremoved
35168 * Fires when a panel is removed.
35169 * @param {Roo.LayoutRegion} this
35170 * @param {Roo.ContentPanel} panel The panel
35172 "panelremoved" : true,
35174 * @event beforecollapse
35175 * Fires when this region before collapse.
35176 * @param {Roo.LayoutRegion} this
35178 "beforecollapse" : true,
35181 * Fires when this region is collapsed.
35182 * @param {Roo.LayoutRegion} this
35184 "collapsed" : true,
35187 * Fires when this region is expanded.
35188 * @param {Roo.LayoutRegion} this
35193 * Fires when this region is slid into view.
35194 * @param {Roo.LayoutRegion} this
35196 "slideshow" : true,
35199 * Fires when this region slides out of view.
35200 * @param {Roo.LayoutRegion} this
35202 "slidehide" : true,
35204 * @event panelactivated
35205 * Fires when a panel is activated.
35206 * @param {Roo.LayoutRegion} this
35207 * @param {Roo.ContentPanel} panel The activated panel
35209 "panelactivated" : true,
35212 * Fires when the user resizes this region.
35213 * @param {Roo.LayoutRegion} this
35214 * @param {Number} newSize The new size (width for east/west, height for north/south)
35218 /** A collection of panels in this region. @type Roo.util.MixedCollection */
35219 this.panels = new Roo.util.MixedCollection();
35220 this.panels.getKey = this.getPanelId.createDelegate(this);
35222 this.activePanel = null;
35223 // ensure listeners are added...
35225 if (config.listeners || config.events) {
35226 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35227 listeners : config.listeners || {},
35228 events : config.events || {}
35232 if(skipConfig !== true){
35233 this.applyConfig(config);
35237 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35239 getPanelId : function(p){
35243 applyConfig : function(config){
35244 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35245 this.config = config;
35250 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
35251 * the width, for horizontal (north, south) the height.
35252 * @param {Number} newSize The new width or height
35254 resizeTo : function(newSize){
35255 var el = this.el ? this.el :
35256 (this.activePanel ? this.activePanel.getEl() : null);
35258 switch(this.position){
35261 el.setWidth(newSize);
35262 this.fireEvent("resized", this, newSize);
35266 el.setHeight(newSize);
35267 this.fireEvent("resized", this, newSize);
35273 getBox : function(){
35274 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35277 getMargins : function(){
35278 return this.margins;
35281 updateBox : function(box){
35283 var el = this.activePanel.getEl();
35284 el.dom.style.left = box.x + "px";
35285 el.dom.style.top = box.y + "px";
35286 this.activePanel.setSize(box.width, box.height);
35290 * Returns the container element for this region.
35291 * @return {Roo.Element}
35293 getEl : function(){
35294 return this.activePanel;
35298 * Returns true if this region is currently visible.
35299 * @return {Boolean}
35301 isVisible : function(){
35302 return this.activePanel ? true : false;
35305 setActivePanel : function(panel){
35306 panel = this.getPanel(panel);
35307 if(this.activePanel && this.activePanel != panel){
35308 this.activePanel.setActiveState(false);
35309 this.activePanel.getEl().setLeftTop(-10000,-10000);
35311 this.activePanel = panel;
35312 panel.setActiveState(true);
35314 panel.setSize(this.box.width, this.box.height);
35316 this.fireEvent("panelactivated", this, panel);
35317 this.fireEvent("invalidated");
35321 * Show the specified panel.
35322 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35323 * @return {Roo.ContentPanel} The shown panel or null
35325 showPanel : function(panel){
35326 panel = this.getPanel(panel);
35328 this.setActivePanel(panel);
35334 * Get the active panel for this region.
35335 * @return {Roo.ContentPanel} The active panel or null
35337 getActivePanel : function(){
35338 return this.activePanel;
35342 * Add the passed ContentPanel(s)
35343 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35344 * @return {Roo.ContentPanel} The panel added (if only one was added)
35346 add : function(panel){
35347 if(arguments.length > 1){
35348 for(var i = 0, len = arguments.length; i < len; i++) {
35349 this.add(arguments[i]);
35353 if(this.hasPanel(panel)){
35354 this.showPanel(panel);
35357 var el = panel.getEl();
35358 if(el.dom.parentNode != this.mgr.el.dom){
35359 this.mgr.el.dom.appendChild(el.dom);
35361 if(panel.setRegion){
35362 panel.setRegion(this);
35364 this.panels.add(panel);
35365 el.setStyle("position", "absolute");
35366 if(!panel.background){
35367 this.setActivePanel(panel);
35368 if(this.config.initialSize && this.panels.getCount()==1){
35369 this.resizeTo(this.config.initialSize);
35372 this.fireEvent("paneladded", this, panel);
35377 * Returns true if the panel is in this region.
35378 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35379 * @return {Boolean}
35381 hasPanel : function(panel){
35382 if(typeof panel == "object"){ // must be panel obj
35383 panel = panel.getId();
35385 return this.getPanel(panel) ? true : false;
35389 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35390 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35391 * @param {Boolean} preservePanel Overrides the config preservePanel option
35392 * @return {Roo.ContentPanel} The panel that was removed
35394 remove : function(panel, preservePanel){
35395 panel = this.getPanel(panel);
35400 this.fireEvent("beforeremove", this, panel, e);
35401 if(e.cancel === true){
35404 var panelId = panel.getId();
35405 this.panels.removeKey(panelId);
35410 * Returns the panel specified or null if it's not in this region.
35411 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35412 * @return {Roo.ContentPanel}
35414 getPanel : function(id){
35415 if(typeof id == "object"){ // must be panel obj
35418 return this.panels.get(id);
35422 * Returns this regions position (north/south/east/west/center).
35425 getPosition: function(){
35426 return this.position;
35430 * Ext JS Library 1.1.1
35431 * Copyright(c) 2006-2007, Ext JS, LLC.
35433 * Originally Released Under LGPL - original licence link has changed is not relivant.
35436 * <script type="text/javascript">
35440 * @class Roo.bootstrap.layout.Region
35441 * @extends Roo.bootstrap.layout.Basic
35442 * This class represents a region in a layout manager.
35444 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35445 * @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})
35446 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
35447 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
35448 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
35449 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
35450 * @cfg {String} title The title for the region (overrides panel titles)
35451 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
35452 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35453 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
35454 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35455 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
35456 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35457 * the space available, similar to FireFox 1.5 tabs (defaults to false)
35458 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
35459 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
35460 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
35462 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
35463 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
35464 * @cfg {Boolean} disableTabTips True to disable tab tooltips
35465 * @cfg {Number} width For East/West panels
35466 * @cfg {Number} height For North/South panels
35467 * @cfg {Boolean} split To show the splitter
35468 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
35470 * @cfg {string} cls Extra CSS classes to add to region
35472 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
35473 * @cfg {string} region the region that it inhabits..
35476 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
35477 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
35479 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
35480 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
35481 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
35483 Roo.bootstrap.layout.Region = function(config)
35485 this.applyConfig(config);
35487 var mgr = config.mgr;
35488 var pos = config.region;
35489 config.skipConfig = true;
35490 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35493 this.onRender(mgr.el);
35496 this.visible = true;
35497 this.collapsed = false;
35498 this.unrendered_panels = [];
35501 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35503 position: '', // set by wrapper (eg. north/south etc..)
35504 unrendered_panels : null, // unrendered panels.
35505 createBody : function(){
35506 /** This region's body element
35507 * @type Roo.Element */
35508 this.bodyEl = this.el.createChild({
35510 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35514 onRender: function(ctr, pos)
35516 var dh = Roo.DomHelper;
35517 /** This region's container element
35518 * @type Roo.Element */
35519 this.el = dh.append(ctr.dom, {
35521 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35523 /** This region's title element
35524 * @type Roo.Element */
35526 this.titleEl = dh.append(this.el.dom,
35529 unselectable: "on",
35530 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35532 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
35533 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35536 this.titleEl.enableDisplayMode();
35537 /** This region's title text element
35538 * @type HTMLElement */
35539 this.titleTextEl = this.titleEl.dom.firstChild;
35540 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35542 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35543 this.closeBtn.enableDisplayMode();
35544 this.closeBtn.on("click", this.closeClicked, this);
35545 this.closeBtn.hide();
35547 this.createBody(this.config);
35548 if(this.config.hideWhenEmpty){
35550 this.on("paneladded", this.validateVisibility, this);
35551 this.on("panelremoved", this.validateVisibility, this);
35553 if(this.autoScroll){
35554 this.bodyEl.setStyle("overflow", "auto");
35556 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35558 //if(c.titlebar !== false){
35559 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35560 this.titleEl.hide();
35562 this.titleEl.show();
35563 if(this.config.title){
35564 this.titleTextEl.innerHTML = this.config.title;
35568 if(this.config.collapsed){
35569 this.collapse(true);
35571 if(this.config.hidden){
35575 if (this.unrendered_panels && this.unrendered_panels.length) {
35576 for (var i =0;i< this.unrendered_panels.length; i++) {
35577 this.add(this.unrendered_panels[i]);
35579 this.unrendered_panels = null;
35585 applyConfig : function(c)
35588 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35589 var dh = Roo.DomHelper;
35590 if(c.titlebar !== false){
35591 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35592 this.collapseBtn.on("click", this.collapse, this);
35593 this.collapseBtn.enableDisplayMode();
35595 if(c.showPin === true || this.showPin){
35596 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35597 this.stickBtn.enableDisplayMode();
35598 this.stickBtn.on("click", this.expand, this);
35599 this.stickBtn.hide();
35604 /** This region's collapsed element
35605 * @type Roo.Element */
35608 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35609 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35612 if(c.floatable !== false){
35613 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35614 this.collapsedEl.on("click", this.collapseClick, this);
35617 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35618 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35619 id: "message", unselectable: "on", style:{"float":"left"}});
35620 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35622 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35623 this.expandBtn.on("click", this.expand, this);
35627 if(this.collapseBtn){
35628 this.collapseBtn.setVisible(c.collapsible == true);
35631 this.cmargins = c.cmargins || this.cmargins ||
35632 (this.position == "west" || this.position == "east" ?
35633 {top: 0, left: 2, right:2, bottom: 0} :
35634 {top: 2, left: 0, right:0, bottom: 2});
35636 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35639 this.bottomTabs = c.tabPosition != "top";
35641 this.autoScroll = c.autoScroll || false;
35646 this.duration = c.duration || .30;
35647 this.slideDuration = c.slideDuration || .45;
35652 * Returns true if this region is currently visible.
35653 * @return {Boolean}
35655 isVisible : function(){
35656 return this.visible;
35660 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35661 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
35663 //setCollapsedTitle : function(title){
35664 // title = title || " ";
35665 // if(this.collapsedTitleTextEl){
35666 // this.collapsedTitleTextEl.innerHTML = title;
35670 getBox : function(){
35672 // if(!this.collapsed){
35673 b = this.el.getBox(false, true);
35675 // b = this.collapsedEl.getBox(false, true);
35680 getMargins : function(){
35681 return this.margins;
35682 //return this.collapsed ? this.cmargins : this.margins;
35685 highlight : function(){
35686 this.el.addClass("x-layout-panel-dragover");
35689 unhighlight : function(){
35690 this.el.removeClass("x-layout-panel-dragover");
35693 updateBox : function(box)
35695 if (!this.bodyEl) {
35696 return; // not rendered yet..
35700 if(!this.collapsed){
35701 this.el.dom.style.left = box.x + "px";
35702 this.el.dom.style.top = box.y + "px";
35703 this.updateBody(box.width, box.height);
35705 this.collapsedEl.dom.style.left = box.x + "px";
35706 this.collapsedEl.dom.style.top = box.y + "px";
35707 this.collapsedEl.setSize(box.width, box.height);
35710 this.tabs.autoSizeTabs();
35714 updateBody : function(w, h)
35717 this.el.setWidth(w);
35718 w -= this.el.getBorderWidth("rl");
35719 if(this.config.adjustments){
35720 w += this.config.adjustments[0];
35723 if(h !== null && h > 0){
35724 this.el.setHeight(h);
35725 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35726 h -= this.el.getBorderWidth("tb");
35727 if(this.config.adjustments){
35728 h += this.config.adjustments[1];
35730 this.bodyEl.setHeight(h);
35732 h = this.tabs.syncHeight(h);
35735 if(this.panelSize){
35736 w = w !== null ? w : this.panelSize.width;
35737 h = h !== null ? h : this.panelSize.height;
35739 if(this.activePanel){
35740 var el = this.activePanel.getEl();
35741 w = w !== null ? w : el.getWidth();
35742 h = h !== null ? h : el.getHeight();
35743 this.panelSize = {width: w, height: h};
35744 this.activePanel.setSize(w, h);
35746 if(Roo.isIE && this.tabs){
35747 this.tabs.el.repaint();
35752 * Returns the container element for this region.
35753 * @return {Roo.Element}
35755 getEl : function(){
35760 * Hides this region.
35763 //if(!this.collapsed){
35764 this.el.dom.style.left = "-2000px";
35767 // this.collapsedEl.dom.style.left = "-2000px";
35768 // this.collapsedEl.hide();
35770 this.visible = false;
35771 this.fireEvent("visibilitychange", this, false);
35775 * Shows this region if it was previously hidden.
35778 //if(!this.collapsed){
35781 // this.collapsedEl.show();
35783 this.visible = true;
35784 this.fireEvent("visibilitychange", this, true);
35787 closeClicked : function(){
35788 if(this.activePanel){
35789 this.remove(this.activePanel);
35793 collapseClick : function(e){
35795 e.stopPropagation();
35798 e.stopPropagation();
35804 * Collapses this region.
35805 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35808 collapse : function(skipAnim, skipCheck = false){
35809 if(this.collapsed) {
35813 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35815 this.collapsed = true;
35817 this.split.el.hide();
35819 if(this.config.animate && skipAnim !== true){
35820 this.fireEvent("invalidated", this);
35821 this.animateCollapse();
35823 this.el.setLocation(-20000,-20000);
35825 this.collapsedEl.show();
35826 this.fireEvent("collapsed", this);
35827 this.fireEvent("invalidated", this);
35833 animateCollapse : function(){
35838 * Expands this region if it was previously collapsed.
35839 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35840 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35843 expand : function(e, skipAnim){
35845 e.stopPropagation();
35847 if(!this.collapsed || this.el.hasActiveFx()) {
35851 this.afterSlideIn();
35854 this.collapsed = false;
35855 if(this.config.animate && skipAnim !== true){
35856 this.animateExpand();
35860 this.split.el.show();
35862 this.collapsedEl.setLocation(-2000,-2000);
35863 this.collapsedEl.hide();
35864 this.fireEvent("invalidated", this);
35865 this.fireEvent("expanded", this);
35869 animateExpand : function(){
35873 initTabs : function()
35875 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35877 var ts = new Roo.bootstrap.panel.Tabs({
35878 el: this.bodyEl.dom,
35879 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35880 disableTooltips: this.config.disableTabTips,
35881 toolbar : this.config.toolbar
35884 if(this.config.hideTabs){
35885 ts.stripWrap.setDisplayed(false);
35888 ts.resizeTabs = this.config.resizeTabs === true;
35889 ts.minTabWidth = this.config.minTabWidth || 40;
35890 ts.maxTabWidth = this.config.maxTabWidth || 250;
35891 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35892 ts.monitorResize = false;
35893 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35894 ts.bodyEl.addClass('roo-layout-tabs-body');
35895 this.panels.each(this.initPanelAsTab, this);
35898 initPanelAsTab : function(panel){
35899 var ti = this.tabs.addTab(
35903 this.config.closeOnTab && panel.isClosable(),
35906 if(panel.tabTip !== undefined){
35907 ti.setTooltip(panel.tabTip);
35909 ti.on("activate", function(){
35910 this.setActivePanel(panel);
35913 if(this.config.closeOnTab){
35914 ti.on("beforeclose", function(t, e){
35916 this.remove(panel);
35920 panel.tabItem = ti;
35925 updatePanelTitle : function(panel, title)
35927 if(this.activePanel == panel){
35928 this.updateTitle(title);
35931 var ti = this.tabs.getTab(panel.getEl().id);
35933 if(panel.tabTip !== undefined){
35934 ti.setTooltip(panel.tabTip);
35939 updateTitle : function(title){
35940 if(this.titleTextEl && !this.config.title){
35941 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
35945 setActivePanel : function(panel)
35947 panel = this.getPanel(panel);
35948 if(this.activePanel && this.activePanel != panel){
35949 if(this.activePanel.setActiveState(false) === false){
35953 this.activePanel = panel;
35954 panel.setActiveState(true);
35955 if(this.panelSize){
35956 panel.setSize(this.panelSize.width, this.panelSize.height);
35959 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35961 this.updateTitle(panel.getTitle());
35963 this.fireEvent("invalidated", this);
35965 this.fireEvent("panelactivated", this, panel);
35969 * Shows the specified panel.
35970 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35971 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35973 showPanel : function(panel)
35975 panel = this.getPanel(panel);
35978 var tab = this.tabs.getTab(panel.getEl().id);
35979 if(tab.isHidden()){
35980 this.tabs.unhideTab(tab.id);
35984 this.setActivePanel(panel);
35991 * Get the active panel for this region.
35992 * @return {Roo.ContentPanel} The active panel or null
35994 getActivePanel : function(){
35995 return this.activePanel;
35998 validateVisibility : function(){
35999 if(this.panels.getCount() < 1){
36000 this.updateTitle(" ");
36001 this.closeBtn.hide();
36004 if(!this.isVisible()){
36011 * Adds the passed ContentPanel(s) to this region.
36012 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36013 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36015 add : function(panel)
36017 if(arguments.length > 1){
36018 for(var i = 0, len = arguments.length; i < len; i++) {
36019 this.add(arguments[i]);
36024 // if we have not been rendered yet, then we can not really do much of this..
36025 if (!this.bodyEl) {
36026 this.unrendered_panels.push(panel);
36033 if(this.hasPanel(panel)){
36034 this.showPanel(panel);
36037 panel.setRegion(this);
36038 this.panels.add(panel);
36039 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36040 // sinle panel - no tab...?? would it not be better to render it with the tabs,
36041 // and hide them... ???
36042 this.bodyEl.dom.appendChild(panel.getEl().dom);
36043 if(panel.background !== true){
36044 this.setActivePanel(panel);
36046 this.fireEvent("paneladded", this, panel);
36053 this.initPanelAsTab(panel);
36057 if(panel.background !== true){
36058 this.tabs.activate(panel.getEl().id);
36060 this.fireEvent("paneladded", this, panel);
36065 * Hides the tab for the specified panel.
36066 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36068 hidePanel : function(panel){
36069 if(this.tabs && (panel = this.getPanel(panel))){
36070 this.tabs.hideTab(panel.getEl().id);
36075 * Unhides the tab for a previously hidden panel.
36076 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36078 unhidePanel : function(panel){
36079 if(this.tabs && (panel = this.getPanel(panel))){
36080 this.tabs.unhideTab(panel.getEl().id);
36084 clearPanels : function(){
36085 while(this.panels.getCount() > 0){
36086 this.remove(this.panels.first());
36091 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36092 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36093 * @param {Boolean} preservePanel Overrides the config preservePanel option
36094 * @return {Roo.ContentPanel} The panel that was removed
36096 remove : function(panel, preservePanel)
36098 panel = this.getPanel(panel);
36103 this.fireEvent("beforeremove", this, panel, e);
36104 if(e.cancel === true){
36107 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36108 var panelId = panel.getId();
36109 this.panels.removeKey(panelId);
36111 document.body.appendChild(panel.getEl().dom);
36114 this.tabs.removeTab(panel.getEl().id);
36115 }else if (!preservePanel){
36116 this.bodyEl.dom.removeChild(panel.getEl().dom);
36118 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36119 var p = this.panels.first();
36120 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36121 tempEl.appendChild(p.getEl().dom);
36122 this.bodyEl.update("");
36123 this.bodyEl.dom.appendChild(p.getEl().dom);
36125 this.updateTitle(p.getTitle());
36127 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36128 this.setActivePanel(p);
36130 panel.setRegion(null);
36131 if(this.activePanel == panel){
36132 this.activePanel = null;
36134 if(this.config.autoDestroy !== false && preservePanel !== true){
36135 try{panel.destroy();}catch(e){}
36137 this.fireEvent("panelremoved", this, panel);
36142 * Returns the TabPanel component used by this region
36143 * @return {Roo.TabPanel}
36145 getTabs : function(){
36149 createTool : function(parentEl, className){
36150 var btn = Roo.DomHelper.append(parentEl, {
36152 cls: "x-layout-tools-button",
36155 cls: "roo-layout-tools-button-inner " + className,
36159 btn.addClassOnOver("roo-layout-tools-button-over");
36164 * Ext JS Library 1.1.1
36165 * Copyright(c) 2006-2007, Ext JS, LLC.
36167 * Originally Released Under LGPL - original licence link has changed is not relivant.
36170 * <script type="text/javascript">
36176 * @class Roo.SplitLayoutRegion
36177 * @extends Roo.LayoutRegion
36178 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36180 Roo.bootstrap.layout.Split = function(config){
36181 this.cursor = config.cursor;
36182 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36185 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36187 splitTip : "Drag to resize.",
36188 collapsibleSplitTip : "Drag to resize. Double click to hide.",
36189 useSplitTips : false,
36191 applyConfig : function(config){
36192 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36195 onRender : function(ctr,pos) {
36197 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36198 if(!this.config.split){
36203 var splitEl = Roo.DomHelper.append(ctr.dom, {
36205 id: this.el.id + "-split",
36206 cls: "roo-layout-split roo-layout-split-"+this.position,
36209 /** The SplitBar for this region
36210 * @type Roo.SplitBar */
36211 // does not exist yet...
36212 Roo.log([this.position, this.orientation]);
36214 this.split = new Roo.bootstrap.SplitBar({
36215 dragElement : splitEl,
36216 resizingElement: this.el,
36217 orientation : this.orientation
36220 this.split.on("moved", this.onSplitMove, this);
36221 this.split.useShim = this.config.useShim === true;
36222 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36223 if(this.useSplitTips){
36224 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36226 //if(config.collapsible){
36227 // this.split.el.on("dblclick", this.collapse, this);
36230 if(typeof this.config.minSize != "undefined"){
36231 this.split.minSize = this.config.minSize;
36233 if(typeof this.config.maxSize != "undefined"){
36234 this.split.maxSize = this.config.maxSize;
36236 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36237 this.hideSplitter();
36242 getHMaxSize : function(){
36243 var cmax = this.config.maxSize || 10000;
36244 var center = this.mgr.getRegion("center");
36245 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36248 getVMaxSize : function(){
36249 var cmax = this.config.maxSize || 10000;
36250 var center = this.mgr.getRegion("center");
36251 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36254 onSplitMove : function(split, newSize){
36255 this.fireEvent("resized", this, newSize);
36259 * Returns the {@link Roo.SplitBar} for this region.
36260 * @return {Roo.SplitBar}
36262 getSplitBar : function(){
36267 this.hideSplitter();
36268 Roo.bootstrap.layout.Split.superclass.hide.call(this);
36271 hideSplitter : function(){
36273 this.split.el.setLocation(-2000,-2000);
36274 this.split.el.hide();
36280 this.split.el.show();
36282 Roo.bootstrap.layout.Split.superclass.show.call(this);
36285 beforeSlide: function(){
36286 if(Roo.isGecko){// firefox overflow auto bug workaround
36287 this.bodyEl.clip();
36289 this.tabs.bodyEl.clip();
36291 if(this.activePanel){
36292 this.activePanel.getEl().clip();
36294 if(this.activePanel.beforeSlide){
36295 this.activePanel.beforeSlide();
36301 afterSlide : function(){
36302 if(Roo.isGecko){// firefox overflow auto bug workaround
36303 this.bodyEl.unclip();
36305 this.tabs.bodyEl.unclip();
36307 if(this.activePanel){
36308 this.activePanel.getEl().unclip();
36309 if(this.activePanel.afterSlide){
36310 this.activePanel.afterSlide();
36316 initAutoHide : function(){
36317 if(this.autoHide !== false){
36318 if(!this.autoHideHd){
36319 var st = new Roo.util.DelayedTask(this.slideIn, this);
36320 this.autoHideHd = {
36321 "mouseout": function(e){
36322 if(!e.within(this.el, true)){
36326 "mouseover" : function(e){
36332 this.el.on(this.autoHideHd);
36336 clearAutoHide : function(){
36337 if(this.autoHide !== false){
36338 this.el.un("mouseout", this.autoHideHd.mouseout);
36339 this.el.un("mouseover", this.autoHideHd.mouseover);
36343 clearMonitor : function(){
36344 Roo.get(document).un("click", this.slideInIf, this);
36347 // these names are backwards but not changed for compat
36348 slideOut : function(){
36349 if(this.isSlid || this.el.hasActiveFx()){
36352 this.isSlid = true;
36353 if(this.collapseBtn){
36354 this.collapseBtn.hide();
36356 this.closeBtnState = this.closeBtn.getStyle('display');
36357 this.closeBtn.hide();
36359 this.stickBtn.show();
36362 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36363 this.beforeSlide();
36364 this.el.setStyle("z-index", 10001);
36365 this.el.slideIn(this.getSlideAnchor(), {
36366 callback: function(){
36368 this.initAutoHide();
36369 Roo.get(document).on("click", this.slideInIf, this);
36370 this.fireEvent("slideshow", this);
36377 afterSlideIn : function(){
36378 this.clearAutoHide();
36379 this.isSlid = false;
36380 this.clearMonitor();
36381 this.el.setStyle("z-index", "");
36382 if(this.collapseBtn){
36383 this.collapseBtn.show();
36385 this.closeBtn.setStyle('display', this.closeBtnState);
36387 this.stickBtn.hide();
36389 this.fireEvent("slidehide", this);
36392 slideIn : function(cb){
36393 if(!this.isSlid || this.el.hasActiveFx()){
36397 this.isSlid = false;
36398 this.beforeSlide();
36399 this.el.slideOut(this.getSlideAnchor(), {
36400 callback: function(){
36401 this.el.setLeftTop(-10000, -10000);
36403 this.afterSlideIn();
36411 slideInIf : function(e){
36412 if(!e.within(this.el)){
36417 animateCollapse : function(){
36418 this.beforeSlide();
36419 this.el.setStyle("z-index", 20000);
36420 var anchor = this.getSlideAnchor();
36421 this.el.slideOut(anchor, {
36422 callback : function(){
36423 this.el.setStyle("z-index", "");
36424 this.collapsedEl.slideIn(anchor, {duration:.3});
36426 this.el.setLocation(-10000,-10000);
36428 this.fireEvent("collapsed", this);
36435 animateExpand : function(){
36436 this.beforeSlide();
36437 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36438 this.el.setStyle("z-index", 20000);
36439 this.collapsedEl.hide({
36442 this.el.slideIn(this.getSlideAnchor(), {
36443 callback : function(){
36444 this.el.setStyle("z-index", "");
36447 this.split.el.show();
36449 this.fireEvent("invalidated", this);
36450 this.fireEvent("expanded", this);
36478 getAnchor : function(){
36479 return this.anchors[this.position];
36482 getCollapseAnchor : function(){
36483 return this.canchors[this.position];
36486 getSlideAnchor : function(){
36487 return this.sanchors[this.position];
36490 getAlignAdj : function(){
36491 var cm = this.cmargins;
36492 switch(this.position){
36508 getExpandAdj : function(){
36509 var c = this.collapsedEl, cm = this.cmargins;
36510 switch(this.position){
36512 return [-(cm.right+c.getWidth()+cm.left), 0];
36515 return [cm.right+c.getWidth()+cm.left, 0];
36518 return [0, -(cm.top+cm.bottom+c.getHeight())];
36521 return [0, cm.top+cm.bottom+c.getHeight()];
36527 * Ext JS Library 1.1.1
36528 * Copyright(c) 2006-2007, Ext JS, LLC.
36530 * Originally Released Under LGPL - original licence link has changed is not relivant.
36533 * <script type="text/javascript">
36536 * These classes are private internal classes
36538 Roo.bootstrap.layout.Center = function(config){
36539 config.region = "center";
36540 Roo.bootstrap.layout.Region.call(this, config);
36541 this.visible = true;
36542 this.minWidth = config.minWidth || 20;
36543 this.minHeight = config.minHeight || 20;
36546 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36548 // center panel can't be hidden
36552 // center panel can't be hidden
36555 getMinWidth: function(){
36556 return this.minWidth;
36559 getMinHeight: function(){
36560 return this.minHeight;
36573 Roo.bootstrap.layout.North = function(config)
36575 config.region = 'north';
36576 config.cursor = 'n-resize';
36578 Roo.bootstrap.layout.Split.call(this, config);
36582 this.split.placement = Roo.bootstrap.SplitBar.TOP;
36583 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36584 this.split.el.addClass("roo-layout-split-v");
36586 var size = config.initialSize || config.height;
36587 if(typeof size != "undefined"){
36588 this.el.setHeight(size);
36591 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36593 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36597 getBox : function(){
36598 if(this.collapsed){
36599 return this.collapsedEl.getBox();
36601 var box = this.el.getBox();
36603 box.height += this.split.el.getHeight();
36608 updateBox : function(box){
36609 if(this.split && !this.collapsed){
36610 box.height -= this.split.el.getHeight();
36611 this.split.el.setLeft(box.x);
36612 this.split.el.setTop(box.y+box.height);
36613 this.split.el.setWidth(box.width);
36615 if(this.collapsed){
36616 this.updateBody(box.width, null);
36618 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36626 Roo.bootstrap.layout.South = function(config){
36627 config.region = 'south';
36628 config.cursor = 's-resize';
36629 Roo.bootstrap.layout.Split.call(this, config);
36631 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36632 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36633 this.split.el.addClass("roo-layout-split-v");
36635 var size = config.initialSize || config.height;
36636 if(typeof size != "undefined"){
36637 this.el.setHeight(size);
36641 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36642 orientation: Roo.bootstrap.SplitBar.VERTICAL,
36643 getBox : function(){
36644 if(this.collapsed){
36645 return this.collapsedEl.getBox();
36647 var box = this.el.getBox();
36649 var sh = this.split.el.getHeight();
36656 updateBox : function(box){
36657 if(this.split && !this.collapsed){
36658 var sh = this.split.el.getHeight();
36661 this.split.el.setLeft(box.x);
36662 this.split.el.setTop(box.y-sh);
36663 this.split.el.setWidth(box.width);
36665 if(this.collapsed){
36666 this.updateBody(box.width, null);
36668 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36672 Roo.bootstrap.layout.East = function(config){
36673 config.region = "east";
36674 config.cursor = "e-resize";
36675 Roo.bootstrap.layout.Split.call(this, config);
36677 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36678 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36679 this.split.el.addClass("roo-layout-split-h");
36681 var size = config.initialSize || config.width;
36682 if(typeof size != "undefined"){
36683 this.el.setWidth(size);
36686 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36687 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36688 getBox : function(){
36689 if(this.collapsed){
36690 return this.collapsedEl.getBox();
36692 var box = this.el.getBox();
36694 var sw = this.split.el.getWidth();
36701 updateBox : function(box){
36702 if(this.split && !this.collapsed){
36703 var sw = this.split.el.getWidth();
36705 this.split.el.setLeft(box.x);
36706 this.split.el.setTop(box.y);
36707 this.split.el.setHeight(box.height);
36710 if(this.collapsed){
36711 this.updateBody(null, box.height);
36713 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36717 Roo.bootstrap.layout.West = function(config){
36718 config.region = "west";
36719 config.cursor = "w-resize";
36721 Roo.bootstrap.layout.Split.call(this, config);
36723 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36724 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36725 this.split.el.addClass("roo-layout-split-h");
36729 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36730 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36732 onRender: function(ctr, pos)
36734 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36735 var size = this.config.initialSize || this.config.width;
36736 if(typeof size != "undefined"){
36737 this.el.setWidth(size);
36741 getBox : function(){
36742 if(this.collapsed){
36743 return this.collapsedEl.getBox();
36745 var box = this.el.getBox();
36747 box.width += this.split.el.getWidth();
36752 updateBox : function(box){
36753 if(this.split && !this.collapsed){
36754 var sw = this.split.el.getWidth();
36756 this.split.el.setLeft(box.x+box.width);
36757 this.split.el.setTop(box.y);
36758 this.split.el.setHeight(box.height);
36760 if(this.collapsed){
36761 this.updateBody(null, box.height);
36763 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36766 Roo.namespace("Roo.bootstrap.panel");/*
36768 * Ext JS Library 1.1.1
36769 * Copyright(c) 2006-2007, Ext JS, LLC.
36771 * Originally Released Under LGPL - original licence link has changed is not relivant.
36774 * <script type="text/javascript">
36777 * @class Roo.ContentPanel
36778 * @extends Roo.util.Observable
36779 * A basic ContentPanel element.
36780 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
36781 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
36782 * @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
36783 * @cfg {Boolean} closable True if the panel can be closed/removed
36784 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
36785 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36786 * @cfg {Toolbar} toolbar A toolbar for this panel
36787 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
36788 * @cfg {String} title The title for this panel
36789 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36790 * @cfg {String} url Calls {@link #setUrl} with this value
36791 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36792 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
36793 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
36794 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
36795 * @cfg {Boolean} badges render the badges
36798 * Create a new ContentPanel.
36799 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36800 * @param {String/Object} config A string to set only the title or a config object
36801 * @param {String} content (optional) Set the HTML content for this panel
36802 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36804 Roo.bootstrap.panel.Content = function( config){
36806 this.tpl = config.tpl || false;
36808 var el = config.el;
36809 var content = config.content;
36811 if(config.autoCreate){ // xtype is available if this is called from factory
36814 this.el = Roo.get(el);
36815 if(!this.el && config && config.autoCreate){
36816 if(typeof config.autoCreate == "object"){
36817 if(!config.autoCreate.id){
36818 config.autoCreate.id = config.id||el;
36820 this.el = Roo.DomHelper.append(document.body,
36821 config.autoCreate, true);
36823 var elcfg = { tag: "div",
36824 cls: "roo-layout-inactive-content",
36828 elcfg.html = config.html;
36832 this.el = Roo.DomHelper.append(document.body, elcfg , true);
36835 this.closable = false;
36836 this.loaded = false;
36837 this.active = false;
36840 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36842 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36844 this.wrapEl = this.el; //this.el.wrap();
36846 if (config.toolbar.items) {
36847 ti = config.toolbar.items ;
36848 delete config.toolbar.items ;
36852 this.toolbar.render(this.wrapEl, 'before');
36853 for(var i =0;i < ti.length;i++) {
36854 // Roo.log(['add child', items[i]]);
36855 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36857 this.toolbar.items = nitems;
36858 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36859 delete config.toolbar;
36863 // xtype created footer. - not sure if will work as we normally have to render first..
36864 if (this.footer && !this.footer.el && this.footer.xtype) {
36865 if (!this.wrapEl) {
36866 this.wrapEl = this.el.wrap();
36869 this.footer.container = this.wrapEl.createChild();
36871 this.footer = Roo.factory(this.footer, Roo);
36876 if(typeof config == "string"){
36877 this.title = config;
36879 Roo.apply(this, config);
36883 this.resizeEl = Roo.get(this.resizeEl, true);
36885 this.resizeEl = this.el;
36887 // handle view.xtype
36895 * Fires when this panel is activated.
36896 * @param {Roo.ContentPanel} this
36900 * @event deactivate
36901 * Fires when this panel is activated.
36902 * @param {Roo.ContentPanel} this
36904 "deactivate" : true,
36908 * Fires when this panel is resized if fitToFrame is true.
36909 * @param {Roo.ContentPanel} this
36910 * @param {Number} width The width after any component adjustments
36911 * @param {Number} height The height after any component adjustments
36917 * Fires when this tab is created
36918 * @param {Roo.ContentPanel} this
36929 if(this.autoScroll){
36930 this.resizeEl.setStyle("overflow", "auto");
36932 // fix randome scrolling
36933 //this.el.on('scroll', function() {
36934 // Roo.log('fix random scolling');
36935 // this.scrollTo('top',0);
36938 content = content || this.content;
36940 this.setContent(content);
36942 if(config && config.url){
36943 this.setUrl(this.url, this.params, this.loadOnce);
36948 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36950 if (this.view && typeof(this.view.xtype) != 'undefined') {
36951 this.view.el = this.el.appendChild(document.createElement("div"));
36952 this.view = Roo.factory(this.view);
36953 this.view.render && this.view.render(false, '');
36957 this.fireEvent('render', this);
36960 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36964 setRegion : function(region){
36965 this.region = region;
36966 this.setActiveClass(region && !this.background);
36970 setActiveClass: function(state)
36973 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36974 this.el.setStyle('position','relative');
36976 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36977 this.el.setStyle('position', 'absolute');
36982 * Returns the toolbar for this Panel if one was configured.
36983 * @return {Roo.Toolbar}
36985 getToolbar : function(){
36986 return this.toolbar;
36989 setActiveState : function(active)
36991 this.active = active;
36992 this.setActiveClass(active);
36994 if(this.fireEvent("deactivate", this) === false){
36999 this.fireEvent("activate", this);
37003 * Updates this panel's element
37004 * @param {String} content The new content
37005 * @param {Boolean} loadScripts (optional) true to look for and process scripts
37007 setContent : function(content, loadScripts){
37008 this.el.update(content, loadScripts);
37011 ignoreResize : function(w, h){
37012 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37015 this.lastSize = {width: w, height: h};
37020 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37021 * @return {Roo.UpdateManager} The UpdateManager
37023 getUpdateManager : function(){
37024 return this.el.getUpdateManager();
37027 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37028 * @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:
37031 url: "your-url.php",
37032 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37033 callback: yourFunction,
37034 scope: yourObject, //(optional scope)
37037 text: "Loading...",
37042 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37043 * 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.
37044 * @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}
37045 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37046 * @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.
37047 * @return {Roo.ContentPanel} this
37050 var um = this.el.getUpdateManager();
37051 um.update.apply(um, arguments);
37057 * 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.
37058 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37059 * @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)
37060 * @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)
37061 * @return {Roo.UpdateManager} The UpdateManager
37063 setUrl : function(url, params, loadOnce){
37064 if(this.refreshDelegate){
37065 this.removeListener("activate", this.refreshDelegate);
37067 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37068 this.on("activate", this.refreshDelegate);
37069 return this.el.getUpdateManager();
37072 _handleRefresh : function(url, params, loadOnce){
37073 if(!loadOnce || !this.loaded){
37074 var updater = this.el.getUpdateManager();
37075 updater.update(url, params, this._setLoaded.createDelegate(this));
37079 _setLoaded : function(){
37080 this.loaded = true;
37084 * Returns this panel's id
37087 getId : function(){
37092 * Returns this panel's element - used by regiosn to add.
37093 * @return {Roo.Element}
37095 getEl : function(){
37096 return this.wrapEl || this.el;
37101 adjustForComponents : function(width, height)
37103 //Roo.log('adjustForComponents ');
37104 if(this.resizeEl != this.el){
37105 width -= this.el.getFrameWidth('lr');
37106 height -= this.el.getFrameWidth('tb');
37109 var te = this.toolbar.getEl();
37110 te.setWidth(width);
37111 height -= te.getHeight();
37114 var te = this.footer.getEl();
37115 te.setWidth(width);
37116 height -= te.getHeight();
37120 if(this.adjustments){
37121 width += this.adjustments[0];
37122 height += this.adjustments[1];
37124 return {"width": width, "height": height};
37127 setSize : function(width, height){
37128 if(this.fitToFrame && !this.ignoreResize(width, height)){
37129 if(this.fitContainer && this.resizeEl != this.el){
37130 this.el.setSize(width, height);
37132 var size = this.adjustForComponents(width, height);
37133 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37134 this.fireEvent('resize', this, size.width, size.height);
37139 * Returns this panel's title
37142 getTitle : function(){
37144 if (typeof(this.title) != 'object') {
37149 for (var k in this.title) {
37150 if (!this.title.hasOwnProperty(k)) {
37154 if (k.indexOf('-') >= 0) {
37155 var s = k.split('-');
37156 for (var i = 0; i<s.length; i++) {
37157 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37160 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37167 * Set this panel's title
37168 * @param {String} title
37170 setTitle : function(title){
37171 this.title = title;
37173 this.region.updatePanelTitle(this, title);
37178 * Returns true is this panel was configured to be closable
37179 * @return {Boolean}
37181 isClosable : function(){
37182 return this.closable;
37185 beforeSlide : function(){
37187 this.resizeEl.clip();
37190 afterSlide : function(){
37192 this.resizeEl.unclip();
37196 * Force a content refresh from the URL specified in the {@link #setUrl} method.
37197 * Will fail silently if the {@link #setUrl} method has not been called.
37198 * This does not activate the panel, just updates its content.
37200 refresh : function(){
37201 if(this.refreshDelegate){
37202 this.loaded = false;
37203 this.refreshDelegate();
37208 * Destroys this panel
37210 destroy : function(){
37211 this.el.removeAllListeners();
37212 var tempEl = document.createElement("span");
37213 tempEl.appendChild(this.el.dom);
37214 tempEl.innerHTML = "";
37220 * form - if the content panel contains a form - this is a reference to it.
37221 * @type {Roo.form.Form}
37225 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37226 * This contains a reference to it.
37232 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37242 * @param {Object} cfg Xtype definition of item to add.
37246 getChildContainer: function () {
37247 return this.getEl();
37252 var ret = new Roo.factory(cfg);
37257 if (cfg.xtype.match(/^Form$/)) {
37260 //if (this.footer) {
37261 // el = this.footer.container.insertSibling(false, 'before');
37263 el = this.el.createChild();
37266 this.form = new Roo.form.Form(cfg);
37269 if ( this.form.allItems.length) {
37270 this.form.render(el.dom);
37274 // should only have one of theses..
37275 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37276 // views.. should not be just added - used named prop 'view''
37278 cfg.el = this.el.appendChild(document.createElement("div"));
37281 var ret = new Roo.factory(cfg);
37283 ret.render && ret.render(false, ''); // render blank..
37293 * @class Roo.bootstrap.panel.Grid
37294 * @extends Roo.bootstrap.panel.Content
37296 * Create a new GridPanel.
37297 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37298 * @param {Object} config A the config object
37304 Roo.bootstrap.panel.Grid = function(config)
37308 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37309 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37311 config.el = this.wrapper;
37312 //this.el = this.wrapper;
37314 if (config.container) {
37315 // ctor'ed from a Border/panel.grid
37318 this.wrapper.setStyle("overflow", "hidden");
37319 this.wrapper.addClass('roo-grid-container');
37324 if(config.toolbar){
37325 var tool_el = this.wrapper.createChild();
37326 this.toolbar = Roo.factory(config.toolbar);
37328 if (config.toolbar.items) {
37329 ti = config.toolbar.items ;
37330 delete config.toolbar.items ;
37334 this.toolbar.render(tool_el);
37335 for(var i =0;i < ti.length;i++) {
37336 // Roo.log(['add child', items[i]]);
37337 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37339 this.toolbar.items = nitems;
37341 delete config.toolbar;
37344 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37345 config.grid.scrollBody = true;;
37346 config.grid.monitorWindowResize = false; // turn off autosizing
37347 config.grid.autoHeight = false;
37348 config.grid.autoWidth = false;
37350 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37352 if (config.background) {
37353 // render grid on panel activation (if panel background)
37354 this.on('activate', function(gp) {
37355 if (!gp.grid.rendered) {
37356 gp.grid.render(this.wrapper);
37357 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37362 this.grid.render(this.wrapper);
37363 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
37366 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37367 // ??? needed ??? config.el = this.wrapper;
37372 // xtype created footer. - not sure if will work as we normally have to render first..
37373 if (this.footer && !this.footer.el && this.footer.xtype) {
37375 var ctr = this.grid.getView().getFooterPanel(true);
37376 this.footer.dataSource = this.grid.dataSource;
37377 this.footer = Roo.factory(this.footer, Roo);
37378 this.footer.render(ctr);
37388 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37389 getId : function(){
37390 return this.grid.id;
37394 * Returns the grid for this panel
37395 * @return {Roo.bootstrap.Table}
37397 getGrid : function(){
37401 setSize : function(width, height){
37402 if(!this.ignoreResize(width, height)){
37403 var grid = this.grid;
37404 var size = this.adjustForComponents(width, height);
37405 var gridel = grid.getGridEl();
37406 gridel.setSize(size.width, size.height);
37408 var thd = grid.getGridEl().select('thead',true).first();
37409 var tbd = grid.getGridEl().select('tbody', true).first();
37411 tbd.setSize(width, height - thd.getHeight());
37420 beforeSlide : function(){
37421 this.grid.getView().scroller.clip();
37424 afterSlide : function(){
37425 this.grid.getView().scroller.unclip();
37428 destroy : function(){
37429 this.grid.destroy();
37431 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
37436 * @class Roo.bootstrap.panel.Nest
37437 * @extends Roo.bootstrap.panel.Content
37439 * Create a new Panel, that can contain a layout.Border.
37442 * @param {Roo.BorderLayout} layout The layout for this panel
37443 * @param {String/Object} config A string to set only the title or a config object
37445 Roo.bootstrap.panel.Nest = function(config)
37447 // construct with only one argument..
37448 /* FIXME - implement nicer consturctors
37449 if (layout.layout) {
37451 layout = config.layout;
37452 delete config.layout;
37454 if (layout.xtype && !layout.getEl) {
37455 // then layout needs constructing..
37456 layout = Roo.factory(layout, Roo);
37460 config.el = config.layout.getEl();
37462 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37464 config.layout.monitorWindowResize = false; // turn off autosizing
37465 this.layout = config.layout;
37466 this.layout.getEl().addClass("roo-layout-nested-layout");
37473 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37475 setSize : function(width, height){
37476 if(!this.ignoreResize(width, height)){
37477 var size = this.adjustForComponents(width, height);
37478 var el = this.layout.getEl();
37479 if (size.height < 1) {
37480 el.setWidth(size.width);
37482 el.setSize(size.width, size.height);
37484 var touch = el.dom.offsetWidth;
37485 this.layout.layout();
37486 // ie requires a double layout on the first pass
37487 if(Roo.isIE && !this.initialized){
37488 this.initialized = true;
37489 this.layout.layout();
37494 // activate all subpanels if not currently active..
37496 setActiveState : function(active){
37497 this.active = active;
37498 this.setActiveClass(active);
37501 this.fireEvent("deactivate", this);
37505 this.fireEvent("activate", this);
37506 // not sure if this should happen before or after..
37507 if (!this.layout) {
37508 return; // should not happen..
37511 for (var r in this.layout.regions) {
37512 reg = this.layout.getRegion(r);
37513 if (reg.getActivePanel()) {
37514 //reg.showPanel(reg.getActivePanel()); // force it to activate..
37515 reg.setActivePanel(reg.getActivePanel());
37518 if (!reg.panels.length) {
37521 reg.showPanel(reg.getPanel(0));
37530 * Returns the nested BorderLayout for this panel
37531 * @return {Roo.BorderLayout}
37533 getLayout : function(){
37534 return this.layout;
37538 * Adds a xtype elements to the layout of the nested panel
37542 xtype : 'ContentPanel',
37549 xtype : 'NestedLayoutPanel',
37555 items : [ ... list of content panels or nested layout panels.. ]
37559 * @param {Object} cfg Xtype definition of item to add.
37561 addxtype : function(cfg) {
37562 return this.layout.addxtype(cfg);
37567 * Ext JS Library 1.1.1
37568 * Copyright(c) 2006-2007, Ext JS, LLC.
37570 * Originally Released Under LGPL - original licence link has changed is not relivant.
37573 * <script type="text/javascript">
37576 * @class Roo.TabPanel
37577 * @extends Roo.util.Observable
37578 * A lightweight tab container.
37582 // basic tabs 1, built from existing content
37583 var tabs = new Roo.TabPanel("tabs1");
37584 tabs.addTab("script", "View Script");
37585 tabs.addTab("markup", "View Markup");
37586 tabs.activate("script");
37588 // more advanced tabs, built from javascript
37589 var jtabs = new Roo.TabPanel("jtabs");
37590 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37592 // set up the UpdateManager
37593 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37594 var updater = tab2.getUpdateManager();
37595 updater.setDefaultUrl("ajax1.htm");
37596 tab2.on('activate', updater.refresh, updater, true);
37598 // Use setUrl for Ajax loading
37599 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37600 tab3.setUrl("ajax2.htm", null, true);
37603 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37606 jtabs.activate("jtabs-1");
37609 * Create a new TabPanel.
37610 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37611 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37613 Roo.bootstrap.panel.Tabs = function(config){
37615 * The container element for this TabPanel.
37616 * @type Roo.Element
37618 this.el = Roo.get(config.el);
37621 if(typeof config == "boolean"){
37622 this.tabPosition = config ? "bottom" : "top";
37624 Roo.apply(this, config);
37628 if(this.tabPosition == "bottom"){
37629 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37630 this.el.addClass("roo-tabs-bottom");
37632 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37633 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37634 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37636 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37638 if(this.tabPosition != "bottom"){
37639 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37640 * @type Roo.Element
37642 this.bodyEl = Roo.get(this.createBody(this.el.dom));
37643 this.el.addClass("roo-tabs-top");
37647 this.bodyEl.setStyle("position", "relative");
37649 this.active = null;
37650 this.activateDelegate = this.activate.createDelegate(this);
37655 * Fires when the active tab changes
37656 * @param {Roo.TabPanel} this
37657 * @param {Roo.TabPanelItem} activePanel The new active tab
37661 * @event beforetabchange
37662 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37663 * @param {Roo.TabPanel} this
37664 * @param {Object} e Set cancel to true on this object to cancel the tab change
37665 * @param {Roo.TabPanelItem} tab The tab being changed to
37667 "beforetabchange" : true
37670 Roo.EventManager.onWindowResize(this.onResize, this);
37671 this.cpad = this.el.getPadding("lr");
37672 this.hiddenCount = 0;
37675 // toolbar on the tabbar support...
37676 if (this.toolbar) {
37677 alert("no toolbar support yet");
37678 this.toolbar = false;
37680 var tcfg = this.toolbar;
37681 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
37682 this.toolbar = new Roo.Toolbar(tcfg);
37683 if (Roo.isSafari) {
37684 var tbl = tcfg.container.child('table', true);
37685 tbl.setAttribute('width', '100%');
37693 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37696 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37698 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37700 tabPosition : "top",
37702 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37704 currentTabWidth : 0,
37706 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37710 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37714 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37716 preferredTabWidth : 175,
37718 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37720 resizeTabs : false,
37722 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37724 monitorResize : true,
37726 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
37731 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37732 * @param {String} id The id of the div to use <b>or create</b>
37733 * @param {String} text The text for the tab
37734 * @param {String} content (optional) Content to put in the TabPanelItem body
37735 * @param {Boolean} closable (optional) True to create a close icon on the tab
37736 * @return {Roo.TabPanelItem} The created TabPanelItem
37738 addTab : function(id, text, content, closable, tpl)
37740 var item = new Roo.bootstrap.panel.TabItem({
37744 closable : closable,
37747 this.addTabItem(item);
37749 item.setContent(content);
37755 * Returns the {@link Roo.TabPanelItem} with the specified id/index
37756 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37757 * @return {Roo.TabPanelItem}
37759 getTab : function(id){
37760 return this.items[id];
37764 * Hides the {@link Roo.TabPanelItem} with the specified id/index
37765 * @param {String/Number} id The id or index of the TabPanelItem to hide.
37767 hideTab : function(id){
37768 var t = this.items[id];
37771 this.hiddenCount++;
37772 this.autoSizeTabs();
37777 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37778 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37780 unhideTab : function(id){
37781 var t = this.items[id];
37783 t.setHidden(false);
37784 this.hiddenCount--;
37785 this.autoSizeTabs();
37790 * Adds an existing {@link Roo.TabPanelItem}.
37791 * @param {Roo.TabPanelItem} item The TabPanelItem to add
37793 addTabItem : function(item){
37794 this.items[item.id] = item;
37795 this.items.push(item);
37796 // if(this.resizeTabs){
37797 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37798 // this.autoSizeTabs();
37800 // item.autoSize();
37805 * Removes a {@link Roo.TabPanelItem}.
37806 * @param {String/Number} id The id or index of the TabPanelItem to remove.
37808 removeTab : function(id){
37809 var items = this.items;
37810 var tab = items[id];
37811 if(!tab) { return; }
37812 var index = items.indexOf(tab);
37813 if(this.active == tab && items.length > 1){
37814 var newTab = this.getNextAvailable(index);
37819 this.stripEl.dom.removeChild(tab.pnode.dom);
37820 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37821 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37823 items.splice(index, 1);
37824 delete this.items[tab.id];
37825 tab.fireEvent("close", tab);
37826 tab.purgeListeners();
37827 this.autoSizeTabs();
37830 getNextAvailable : function(start){
37831 var items = this.items;
37833 // look for a next tab that will slide over to
37834 // replace the one being removed
37835 while(index < items.length){
37836 var item = items[++index];
37837 if(item && !item.isHidden()){
37841 // if one isn't found select the previous tab (on the left)
37844 var item = items[--index];
37845 if(item && !item.isHidden()){
37853 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37854 * @param {String/Number} id The id or index of the TabPanelItem to disable.
37856 disableTab : function(id){
37857 var tab = this.items[id];
37858 if(tab && this.active != tab){
37864 * Enables a {@link Roo.TabPanelItem} that is disabled.
37865 * @param {String/Number} id The id or index of the TabPanelItem to enable.
37867 enableTab : function(id){
37868 var tab = this.items[id];
37873 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37874 * @param {String/Number} id The id or index of the TabPanelItem to activate.
37875 * @return {Roo.TabPanelItem} The TabPanelItem.
37877 activate : function(id){
37878 var tab = this.items[id];
37882 if(tab == this.active || tab.disabled){
37886 this.fireEvent("beforetabchange", this, e, tab);
37887 if(e.cancel !== true && !tab.disabled){
37889 this.active.hide();
37891 this.active = this.items[id];
37892 this.active.show();
37893 this.fireEvent("tabchange", this, this.active);
37899 * Gets the active {@link Roo.TabPanelItem}.
37900 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37902 getActiveTab : function(){
37903 return this.active;
37907 * Updates the tab body element to fit the height of the container element
37908 * for overflow scrolling
37909 * @param {Number} targetHeight (optional) Override the starting height from the elements height
37911 syncHeight : function(targetHeight){
37912 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37913 var bm = this.bodyEl.getMargins();
37914 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37915 this.bodyEl.setHeight(newHeight);
37919 onResize : function(){
37920 if(this.monitorResize){
37921 this.autoSizeTabs();
37926 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37928 beginUpdate : function(){
37929 this.updating = true;
37933 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37935 endUpdate : function(){
37936 this.updating = false;
37937 this.autoSizeTabs();
37941 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37943 autoSizeTabs : function(){
37944 var count = this.items.length;
37945 var vcount = count - this.hiddenCount;
37946 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37949 var w = Math.max(this.el.getWidth() - this.cpad, 10);
37950 var availWidth = Math.floor(w / vcount);
37951 var b = this.stripBody;
37952 if(b.getWidth() > w){
37953 var tabs = this.items;
37954 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37955 if(availWidth < this.minTabWidth){
37956 /*if(!this.sleft){ // incomplete scrolling code
37957 this.createScrollButtons();
37960 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37963 if(this.currentTabWidth < this.preferredTabWidth){
37964 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37970 * Returns the number of tabs in this TabPanel.
37973 getCount : function(){
37974 return this.items.length;
37978 * Resizes all the tabs to the passed width
37979 * @param {Number} The new width
37981 setTabWidth : function(width){
37982 this.currentTabWidth = width;
37983 for(var i = 0, len = this.items.length; i < len; i++) {
37984 if(!this.items[i].isHidden()) {
37985 this.items[i].setWidth(width);
37991 * Destroys this TabPanel
37992 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37994 destroy : function(removeEl){
37995 Roo.EventManager.removeResizeListener(this.onResize, this);
37996 for(var i = 0, len = this.items.length; i < len; i++){
37997 this.items[i].purgeListeners();
37999 if(removeEl === true){
38000 this.el.update("");
38005 createStrip : function(container)
38007 var strip = document.createElement("nav");
38008 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38009 container.appendChild(strip);
38013 createStripList : function(strip)
38015 // div wrapper for retard IE
38016 // returns the "tr" element.
38017 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38018 //'<div class="x-tabs-strip-wrap">'+
38019 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38020 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38021 return strip.firstChild; //.firstChild.firstChild.firstChild;
38023 createBody : function(container)
38025 var body = document.createElement("div");
38026 Roo.id(body, "tab-body");
38027 //Roo.fly(body).addClass("x-tabs-body");
38028 Roo.fly(body).addClass("tab-content");
38029 container.appendChild(body);
38032 createItemBody :function(bodyEl, id){
38033 var body = Roo.getDom(id);
38035 body = document.createElement("div");
38038 //Roo.fly(body).addClass("x-tabs-item-body");
38039 Roo.fly(body).addClass("tab-pane");
38040 bodyEl.insertBefore(body, bodyEl.firstChild);
38044 createStripElements : function(stripEl, text, closable, tpl)
38046 var td = document.createElement("li"); // was td..
38049 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38052 stripEl.appendChild(td);
38054 td.className = "x-tabs-closable";
38055 if(!this.closeTpl){
38056 this.closeTpl = new Roo.Template(
38057 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38058 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38059 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
38062 var el = this.closeTpl.overwrite(td, {"text": text});
38063 var close = el.getElementsByTagName("div")[0];
38064 var inner = el.getElementsByTagName("em")[0];
38065 return {"el": el, "close": close, "inner": inner};
38068 // not sure what this is..
38069 // if(!this.tabTpl){
38070 //this.tabTpl = new Roo.Template(
38071 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38072 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38074 // this.tabTpl = new Roo.Template(
38075 // '<a href="#">' +
38076 // '<span unselectable="on"' +
38077 // (this.disableTooltips ? '' : ' title="{text}"') +
38078 // ' >{text}</span></a>'
38084 var template = tpl || this.tabTpl || false;
38088 template = new Roo.Template(
38090 '<span unselectable="on"' +
38091 (this.disableTooltips ? '' : ' title="{text}"') +
38092 ' >{text}</span></a>'
38096 switch (typeof(template)) {
38100 template = new Roo.Template(template);
38106 var el = template.overwrite(td, {"text": text});
38108 var inner = el.getElementsByTagName("span")[0];
38110 return {"el": el, "inner": inner};
38118 * @class Roo.TabPanelItem
38119 * @extends Roo.util.Observable
38120 * Represents an individual item (tab plus body) in a TabPanel.
38121 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38122 * @param {String} id The id of this TabPanelItem
38123 * @param {String} text The text for the tab of this TabPanelItem
38124 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38126 Roo.bootstrap.panel.TabItem = function(config){
38128 * The {@link Roo.TabPanel} this TabPanelItem belongs to
38129 * @type Roo.TabPanel
38131 this.tabPanel = config.panel;
38133 * The id for this TabPanelItem
38136 this.id = config.id;
38138 this.disabled = false;
38140 this.text = config.text;
38142 this.loaded = false;
38143 this.closable = config.closable;
38146 * The body element for this TabPanelItem.
38147 * @type Roo.Element
38149 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38150 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38151 this.bodyEl.setStyle("display", "block");
38152 this.bodyEl.setStyle("zoom", "1");
38153 //this.hideAction();
38155 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38157 this.el = Roo.get(els.el);
38158 this.inner = Roo.get(els.inner, true);
38159 this.textEl = Roo.get(this.el.dom.firstChild, true);
38160 this.pnode = Roo.get(els.el.parentNode, true);
38161 // this.el.on("mousedown", this.onTabMouseDown, this);
38162 this.el.on("click", this.onTabClick, this);
38164 if(config.closable){
38165 var c = Roo.get(els.close, true);
38166 c.dom.title = this.closeText;
38167 c.addClassOnOver("close-over");
38168 c.on("click", this.closeClick, this);
38174 * Fires when this tab becomes the active tab.
38175 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38176 * @param {Roo.TabPanelItem} this
38180 * @event beforeclose
38181 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38182 * @param {Roo.TabPanelItem} this
38183 * @param {Object} e Set cancel to true on this object to cancel the close.
38185 "beforeclose": true,
38188 * Fires when this tab is closed.
38189 * @param {Roo.TabPanelItem} this
38193 * @event deactivate
38194 * Fires when this tab is no longer the active tab.
38195 * @param {Roo.TabPanel} tabPanel The parent TabPanel
38196 * @param {Roo.TabPanelItem} this
38198 "deactivate" : true
38200 this.hidden = false;
38202 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38205 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38207 purgeListeners : function(){
38208 Roo.util.Observable.prototype.purgeListeners.call(this);
38209 this.el.removeAllListeners();
38212 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38215 this.pnode.addClass("active");
38218 this.tabPanel.stripWrap.repaint();
38220 this.fireEvent("activate", this.tabPanel, this);
38224 * Returns true if this tab is the active tab.
38225 * @return {Boolean}
38227 isActive : function(){
38228 return this.tabPanel.getActiveTab() == this;
38232 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38235 this.pnode.removeClass("active");
38237 this.fireEvent("deactivate", this.tabPanel, this);
38240 hideAction : function(){
38241 this.bodyEl.hide();
38242 this.bodyEl.setStyle("position", "absolute");
38243 this.bodyEl.setLeft("-20000px");
38244 this.bodyEl.setTop("-20000px");
38247 showAction : function(){
38248 this.bodyEl.setStyle("position", "relative");
38249 this.bodyEl.setTop("");
38250 this.bodyEl.setLeft("");
38251 this.bodyEl.show();
38255 * Set the tooltip for the tab.
38256 * @param {String} tooltip The tab's tooltip
38258 setTooltip : function(text){
38259 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38260 this.textEl.dom.qtip = text;
38261 this.textEl.dom.removeAttribute('title');
38263 this.textEl.dom.title = text;
38267 onTabClick : function(e){
38268 e.preventDefault();
38269 this.tabPanel.activate(this.id);
38272 onTabMouseDown : function(e){
38273 e.preventDefault();
38274 this.tabPanel.activate(this.id);
38277 getWidth : function(){
38278 return this.inner.getWidth();
38281 setWidth : function(width){
38282 var iwidth = width - this.pnode.getPadding("lr");
38283 this.inner.setWidth(iwidth);
38284 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38285 this.pnode.setWidth(width);
38289 * Show or hide the tab
38290 * @param {Boolean} hidden True to hide or false to show.
38292 setHidden : function(hidden){
38293 this.hidden = hidden;
38294 this.pnode.setStyle("display", hidden ? "none" : "");
38298 * Returns true if this tab is "hidden"
38299 * @return {Boolean}
38301 isHidden : function(){
38302 return this.hidden;
38306 * Returns the text for this tab
38309 getText : function(){
38313 autoSize : function(){
38314 //this.el.beginMeasure();
38315 this.textEl.setWidth(1);
38317 * #2804 [new] Tabs in Roojs
38318 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38320 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38321 //this.el.endMeasure();
38325 * Sets the text for the tab (Note: this also sets the tooltip text)
38326 * @param {String} text The tab's text and tooltip
38328 setText : function(text){
38330 this.textEl.update(text);
38331 this.setTooltip(text);
38332 //if(!this.tabPanel.resizeTabs){
38333 // this.autoSize();
38337 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38339 activate : function(){
38340 this.tabPanel.activate(this.id);
38344 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38346 disable : function(){
38347 if(this.tabPanel.active != this){
38348 this.disabled = true;
38349 this.pnode.addClass("disabled");
38354 * Enables this TabPanelItem if it was previously disabled.
38356 enable : function(){
38357 this.disabled = false;
38358 this.pnode.removeClass("disabled");
38362 * Sets the content for this TabPanelItem.
38363 * @param {String} content The content
38364 * @param {Boolean} loadScripts true to look for and load scripts
38366 setContent : function(content, loadScripts){
38367 this.bodyEl.update(content, loadScripts);
38371 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38372 * @return {Roo.UpdateManager} The UpdateManager
38374 getUpdateManager : function(){
38375 return this.bodyEl.getUpdateManager();
38379 * Set a URL to be used to load the content for this TabPanelItem.
38380 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38381 * @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)
38382 * @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)
38383 * @return {Roo.UpdateManager} The UpdateManager
38385 setUrl : function(url, params, loadOnce){
38386 if(this.refreshDelegate){
38387 this.un('activate', this.refreshDelegate);
38389 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38390 this.on("activate", this.refreshDelegate);
38391 return this.bodyEl.getUpdateManager();
38395 _handleRefresh : function(url, params, loadOnce){
38396 if(!loadOnce || !this.loaded){
38397 var updater = this.bodyEl.getUpdateManager();
38398 updater.update(url, params, this._setLoaded.createDelegate(this));
38403 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
38404 * Will fail silently if the setUrl method has not been called.
38405 * This does not activate the panel, just updates its content.
38407 refresh : function(){
38408 if(this.refreshDelegate){
38409 this.loaded = false;
38410 this.refreshDelegate();
38415 _setLoaded : function(){
38416 this.loaded = true;
38420 closeClick : function(e){
38423 this.fireEvent("beforeclose", this, o);
38424 if(o.cancel !== true){
38425 this.tabPanel.removeTab(this.id);
38429 * The text displayed in the tooltip for the close icon.
38432 closeText : "Close this tab"
38435 * This script refer to:
38436 * Title: International Telephone Input
38437 * Author: Jack O'Connor
38438 * Code version: v12.1.12
38439 * Availability: https://github.com/jackocnr/intl-tel-input.git
38442 Roo.bootstrap.PhoneInputData = function() {
38445 "Afghanistan (افغانستان)",
38450 "Albania (Shqipëri)",
38455 "Algeria (الجزائر)",
38480 "Antigua and Barbuda",
38490 "Armenia (Հայաստան)",
38506 "Austria (Österreich)",
38511 "Azerbaijan (Azərbaycan)",
38521 "Bahrain (البحرين)",
38526 "Bangladesh (বাংলাদেশ)",
38536 "Belarus (Беларусь)",
38541 "Belgium (België)",
38571 "Bosnia and Herzegovina (Босна и Херцеговина)",
38586 "British Indian Ocean Territory",
38591 "British Virgin Islands",
38601 "Bulgaria (България)",
38611 "Burundi (Uburundi)",
38616 "Cambodia (កម្ពុជា)",
38621 "Cameroon (Cameroun)",
38630 ["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"]
38633 "Cape Verde (Kabu Verdi)",
38638 "Caribbean Netherlands",
38649 "Central African Republic (République centrafricaine)",
38669 "Christmas Island",
38675 "Cocos (Keeling) Islands",
38686 "Comoros (جزر القمر)",
38691 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38696 "Congo (Republic) (Congo-Brazzaville)",
38716 "Croatia (Hrvatska)",
38737 "Czech Republic (Česká republika)",
38742 "Denmark (Danmark)",
38757 "Dominican Republic (República Dominicana)",
38761 ["809", "829", "849"]
38779 "Equatorial Guinea (Guinea Ecuatorial)",
38799 "Falkland Islands (Islas Malvinas)",
38804 "Faroe Islands (Føroyar)",
38825 "French Guiana (Guyane française)",
38830 "French Polynesia (Polynésie française)",
38845 "Georgia (საქართველო)",
38850 "Germany (Deutschland)",
38870 "Greenland (Kalaallit Nunaat)",
38907 "Guinea-Bissau (Guiné Bissau)",
38932 "Hungary (Magyarország)",
38937 "Iceland (Ísland)",
38957 "Iraq (العراق)",
38973 "Israel (ישראל)",
39000 "Jordan (الأردن)",
39005 "Kazakhstan (Казахстан)",
39026 "Kuwait (الكويت)",
39031 "Kyrgyzstan (Кыргызстан)",
39041 "Latvia (Latvija)",
39046 "Lebanon (لبنان)",
39061 "Libya (ليبيا)",
39071 "Lithuania (Lietuva)",
39086 "Macedonia (FYROM) (Македонија)",
39091 "Madagascar (Madagasikara)",
39121 "Marshall Islands",
39131 "Mauritania (موريتانيا)",
39136 "Mauritius (Moris)",
39157 "Moldova (Republica Moldova)",
39167 "Mongolia (Монгол)",
39172 "Montenegro (Crna Gora)",
39182 "Morocco (المغرب)",
39188 "Mozambique (Moçambique)",
39193 "Myanmar (Burma) (မြန်မာ)",
39198 "Namibia (Namibië)",
39213 "Netherlands (Nederland)",
39218 "New Caledonia (Nouvelle-Calédonie)",
39253 "North Korea (조선 민주주의 인민 공화국)",
39258 "Northern Mariana Islands",
39274 "Pakistan (پاکستان)",
39284 "Palestine (فلسطين)",
39294 "Papua New Guinea",
39336 "Réunion (La Réunion)",
39342 "Romania (România)",
39358 "Saint Barthélemy",
39369 "Saint Kitts and Nevis",
39379 "Saint Martin (Saint-Martin (partie française))",
39385 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39390 "Saint Vincent and the Grenadines",
39405 "São Tomé and Príncipe (São Tomé e Príncipe)",
39410 "Saudi Arabia (المملكة العربية السعودية)",
39415 "Senegal (Sénégal)",
39445 "Slovakia (Slovensko)",
39450 "Slovenia (Slovenija)",
39460 "Somalia (Soomaaliya)",
39470 "South Korea (대한민국)",
39475 "South Sudan (جنوب السودان)",
39485 "Sri Lanka (ශ්රී ලංකාව)",
39490 "Sudan (السودان)",
39500 "Svalbard and Jan Mayen",
39511 "Sweden (Sverige)",
39516 "Switzerland (Schweiz)",
39521 "Syria (سوريا)",
39566 "Trinidad and Tobago",
39571 "Tunisia (تونس)",
39576 "Turkey (Türkiye)",
39586 "Turks and Caicos Islands",
39596 "U.S. Virgin Islands",
39606 "Ukraine (Україна)",
39611 "United Arab Emirates (الإمارات العربية المتحدة)",
39633 "Uzbekistan (Oʻzbekiston)",
39643 "Vatican City (Città del Vaticano)",
39654 "Vietnam (Việt Nam)",
39659 "Wallis and Futuna (Wallis-et-Futuna)",
39664 "Western Sahara (الصحراء الغربية)",
39670 "Yemen (اليمن)",
39694 * This script refer to:
39695 * Title: International Telephone Input
39696 * Author: Jack O'Connor
39697 * Code version: v12.1.12
39698 * Availability: https://github.com/jackocnr/intl-tel-input.git
39702 * @class Roo.bootstrap.PhoneInput
39703 * @extends Roo.bootstrap.TriggerField
39704 * An input with International dial-code selection
39706 * @cfg {String} defaultDialCode default '+852'
39707 * @cfg {Array} preferedCountries default []
39710 * Create a new PhoneInput.
39711 * @param {Object} config Configuration options
39714 Roo.bootstrap.PhoneInput = function(config) {
39715 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39718 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39720 listWidth: undefined,
39722 selectedClass: 'active',
39724 invalidClass : "has-warning",
39726 validClass: 'has-success',
39728 allowed: '0123456789',
39731 * @cfg {String} defaultDialCode The default dial code when initializing the input
39733 defaultDialCode: '+852',
39736 * @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
39738 preferedCountries: false,
39740 getAutoCreate : function()
39742 var data = Roo.bootstrap.PhoneInputData();
39743 var align = this.labelAlign || this.parentLabelAlign();
39746 this.allCountries = [];
39747 this.dialCodeMapping = [];
39749 for (var i = 0; i < data.length; i++) {
39751 this.allCountries[i] = {
39755 priority: c[3] || 0,
39756 areaCodes: c[4] || null
39758 this.dialCodeMapping[c[2]] = {
39761 priority: c[3] || 0,
39762 areaCodes: c[4] || null
39774 cls : 'form-control tel-input',
39775 autocomplete: 'new-password'
39778 var hiddenInput = {
39781 cls: 'hidden-tel-input'
39785 hiddenInput.name = this.name;
39788 if (this.disabled) {
39789 input.disabled = true;
39792 var flag_container = {
39809 cls: this.hasFeedback ? 'has-feedback' : '',
39815 cls: 'dial-code-holder',
39822 cls: 'roo-select2-container input-group',
39829 if (this.fieldLabel.length) {
39832 tooltip: 'This field is required'
39838 cls: 'control-label',
39844 html: this.fieldLabel
39847 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39853 if(this.indicatorpos == 'right') {
39854 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39861 if(align == 'left') {
39869 if(this.labelWidth > 12){
39870 label.style = "width: " + this.labelWidth + 'px';
39872 if(this.labelWidth < 13 && this.labelmd == 0){
39873 this.labelmd = this.labelWidth;
39875 if(this.labellg > 0){
39876 label.cls += ' col-lg-' + this.labellg;
39877 input.cls += ' col-lg-' + (12 - this.labellg);
39879 if(this.labelmd > 0){
39880 label.cls += ' col-md-' + this.labelmd;
39881 container.cls += ' col-md-' + (12 - this.labelmd);
39883 if(this.labelsm > 0){
39884 label.cls += ' col-sm-' + this.labelsm;
39885 container.cls += ' col-sm-' + (12 - this.labelsm);
39887 if(this.labelxs > 0){
39888 label.cls += ' col-xs-' + this.labelxs;
39889 container.cls += ' col-xs-' + (12 - this.labelxs);
39899 var settings = this;
39901 ['xs','sm','md','lg'].map(function(size){
39902 if (settings[size]) {
39903 cfg.cls += ' col-' + size + '-' + settings[size];
39907 this.store = new Roo.data.Store({
39908 proxy : new Roo.data.MemoryProxy({}),
39909 reader : new Roo.data.JsonReader({
39920 'name' : 'dialCode',
39924 'name' : 'priority',
39928 'name' : 'areaCodes',
39935 if(!this.preferedCountries) {
39936 this.preferedCountries = [
39943 var p = this.preferedCountries.reverse();
39946 for (var i = 0; i < p.length; i++) {
39947 for (var j = 0; j < this.allCountries.length; j++) {
39948 if(this.allCountries[j].iso2 == p[i]) {
39949 var t = this.allCountries[j];
39950 this.allCountries.splice(j,1);
39951 this.allCountries.unshift(t);
39957 this.store.proxy.data = {
39959 data: this.allCountries
39965 initEvents : function()
39968 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39970 this.indicator = this.indicatorEl();
39971 this.flag = this.flagEl();
39972 this.dialCodeHolder = this.dialCodeHolderEl();
39974 this.trigger = this.el.select('div.flag-box',true).first();
39975 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39980 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39981 _this.list.setWidth(lw);
39984 this.list.on('mouseover', this.onViewOver, this);
39985 this.list.on('mousemove', this.onViewMove, this);
39986 this.inputEl().on("keyup", this.onKeyUp, this);
39988 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39990 this.view = new Roo.View(this.list, this.tpl, {
39991 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39994 this.view.on('click', this.onViewClick, this);
39995 this.setValue(this.defaultDialCode);
39998 onTriggerClick : function(e)
40000 Roo.log('trigger click');
40005 if(this.isExpanded()){
40007 this.hasFocus = false;
40009 this.store.load({});
40010 this.hasFocus = true;
40015 isExpanded : function()
40017 return this.list.isVisible();
40020 collapse : function()
40022 if(!this.isExpanded()){
40026 Roo.get(document).un('mousedown', this.collapseIf, this);
40027 Roo.get(document).un('mousewheel', this.collapseIf, this);
40028 this.fireEvent('collapse', this);
40032 expand : function()
40036 if(this.isExpanded() || !this.hasFocus){
40040 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40041 this.list.setWidth(lw);
40044 this.restrictHeight();
40046 Roo.get(document).on('mousedown', this.collapseIf, this);
40047 Roo.get(document).on('mousewheel', this.collapseIf, this);
40049 this.fireEvent('expand', this);
40052 restrictHeight : function()
40054 this.list.alignTo(this.inputEl(), this.listAlign);
40055 this.list.alignTo(this.inputEl(), this.listAlign);
40058 onViewOver : function(e, t)
40060 if(this.inKeyMode){
40063 var item = this.view.findItemFromChild(t);
40066 var index = this.view.indexOf(item);
40067 this.select(index, false);
40072 onViewClick : function(view, doFocus, el, e)
40074 var index = this.view.getSelectedIndexes()[0];
40076 var r = this.store.getAt(index);
40079 this.onSelect(r, index);
40081 if(doFocus !== false && !this.blockFocus){
40082 this.inputEl().focus();
40086 onViewMove : function(e, t)
40088 this.inKeyMode = false;
40091 select : function(index, scrollIntoView)
40093 this.selectedIndex = index;
40094 this.view.select(index);
40095 if(scrollIntoView !== false){
40096 var el = this.view.getNode(index);
40098 this.list.scrollChildIntoView(el, false);
40103 createList : function()
40105 this.list = Roo.get(document.body).createChild({
40107 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40108 style: 'display:none'
40111 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40114 collapseIf : function(e)
40116 var in_combo = e.within(this.el);
40117 var in_list = e.within(this.list);
40118 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40120 if (in_combo || in_list || is_list) {
40126 onSelect : function(record, index)
40128 if(this.fireEvent('beforeselect', this, record, index) !== false){
40130 this.setFlagClass(record.data.iso2);
40131 this.setDialCode(record.data.dialCode);
40132 this.hasFocus = false;
40134 this.fireEvent('select', this, record, index);
40138 flagEl : function()
40140 var flag = this.el.select('div.flag',true).first();
40147 dialCodeHolderEl : function()
40149 var d = this.el.select('input.dial-code-holder',true).first();
40156 setDialCode : function(v)
40158 this.dialCodeHolder.dom.value = '+'+v;
40161 setFlagClass : function(n)
40163 this.flag.dom.className = 'flag '+n;
40166 getValue : function()
40168 var v = this.inputEl().getValue();
40169 if(this.dialCodeHolder) {
40170 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40175 setValue : function(v)
40177 var d = this.getDialCode(v);
40179 //invalid dial code
40180 if(v.length == 0 || !d || d.length == 0) {
40182 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40183 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40189 this.setFlagClass(this.dialCodeMapping[d].iso2);
40190 this.setDialCode(d);
40191 this.inputEl().dom.value = v.replace('+'+d,'');
40192 this.hiddenEl().dom.value = this.getValue();
40197 getDialCode : function(v)
40201 if (v.length == 0) {
40202 return this.dialCodeHolder.dom.value;
40206 if (v.charAt(0) != "+") {
40209 var numericChars = "";
40210 for (var i = 1; i < v.length; i++) {
40211 var c = v.charAt(i);
40214 if (this.dialCodeMapping[numericChars]) {
40215 dialCode = v.substr(1, i);
40217 if (numericChars.length == 4) {
40227 this.setValue(this.defaultDialCode);
40231 hiddenEl : function()
40233 return this.el.select('input.hidden-tel-input',true).first();
40236 onKeyUp : function(e){
40238 var k = e.getKey();
40239 var c = e.getCharCode();
40242 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40243 this.allowed.indexOf(String.fromCharCode(c)) === -1
40248 // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40251 if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40255 this.setValue(this.getValue());
40260 * @class Roo.bootstrap.MoneyField
40261 * @extends Roo.bootstrap.ComboBox
40262 * Bootstrap MoneyField class
40265 * Create a new MoneyField.
40266 * @param {Object} config Configuration options
40269 Roo.bootstrap.MoneyField = function(config) {
40271 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40275 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40278 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40280 allowDecimals : true,
40282 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40284 decimalSeparator : ".",
40286 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40288 decimalPrecision : 0,
40290 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40292 allowNegative : true,
40294 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40298 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40300 minValue : Number.NEGATIVE_INFINITY,
40302 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40304 maxValue : Number.MAX_VALUE,
40306 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40308 minText : "The minimum value for this field is {0}",
40310 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40312 maxText : "The maximum value for this field is {0}",
40314 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40315 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40317 nanText : "{0} is not a valid number",
40319 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40323 * @cfg {String} defaults currency of the MoneyField
40324 * value should be in lkey
40326 defaultCurrency : false,
40328 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40330 thousandsDelimiter : false,
40340 getAutoCreate : function()
40342 var align = this.labelAlign || this.parentLabelAlign();
40354 cls : 'form-control roo-money-amount-input',
40355 autocomplete: 'new-password'
40358 var hiddenInput = {
40362 cls: 'hidden-number-input'
40366 hiddenInput.name = this.name;
40369 if (this.disabled) {
40370 input.disabled = true;
40373 var clg = 12 - this.inputlg;
40374 var cmd = 12 - this.inputmd;
40375 var csm = 12 - this.inputsm;
40376 var cxs = 12 - this.inputxs;
40380 cls : 'row roo-money-field',
40384 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40388 cls: 'roo-select2-container input-group',
40392 cls : 'form-control roo-money-currency-input',
40393 autocomplete: 'new-password',
40395 name : this.currencyName
40399 cls : 'input-group-addon',
40413 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40417 cls: this.hasFeedback ? 'has-feedback' : '',
40428 if (this.fieldLabel.length) {
40431 tooltip: 'This field is required'
40437 cls: 'control-label',
40443 html: this.fieldLabel
40446 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40452 if(this.indicatorpos == 'right') {
40453 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40460 if(align == 'left') {
40468 if(this.labelWidth > 12){
40469 label.style = "width: " + this.labelWidth + 'px';
40471 if(this.labelWidth < 13 && this.labelmd == 0){
40472 this.labelmd = this.labelWidth;
40474 if(this.labellg > 0){
40475 label.cls += ' col-lg-' + this.labellg;
40476 input.cls += ' col-lg-' + (12 - this.labellg);
40478 if(this.labelmd > 0){
40479 label.cls += ' col-md-' + this.labelmd;
40480 container.cls += ' col-md-' + (12 - this.labelmd);
40482 if(this.labelsm > 0){
40483 label.cls += ' col-sm-' + this.labelsm;
40484 container.cls += ' col-sm-' + (12 - this.labelsm);
40486 if(this.labelxs > 0){
40487 label.cls += ' col-xs-' + this.labelxs;
40488 container.cls += ' col-xs-' + (12 - this.labelxs);
40499 var settings = this;
40501 ['xs','sm','md','lg'].map(function(size){
40502 if (settings[size]) {
40503 cfg.cls += ' col-' + size + '-' + settings[size];
40510 initEvents : function()
40512 this.indicator = this.indicatorEl();
40514 this.initCurrencyEvent();
40516 this.initNumberEvent();
40519 initCurrencyEvent : function()
40522 throw "can not find store for combo";
40525 this.store = Roo.factory(this.store, Roo.data);
40526 this.store.parent = this;
40530 this.triggerEl = this.el.select('.input-group-addon', true).first();
40532 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40537 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40538 _this.list.setWidth(lw);
40541 this.list.on('mouseover', this.onViewOver, this);
40542 this.list.on('mousemove', this.onViewMove, this);
40543 this.list.on('scroll', this.onViewScroll, this);
40546 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40549 this.view = new Roo.View(this.list, this.tpl, {
40550 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40553 this.view.on('click', this.onViewClick, this);
40555 this.store.on('beforeload', this.onBeforeLoad, this);
40556 this.store.on('load', this.onLoad, this);
40557 this.store.on('loadexception', this.onLoadException, this);
40559 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40560 "up" : function(e){
40561 this.inKeyMode = true;
40565 "down" : function(e){
40566 if(!this.isExpanded()){
40567 this.onTriggerClick();
40569 this.inKeyMode = true;
40574 "enter" : function(e){
40577 if(this.fireEvent("specialkey", this, e)){
40578 this.onViewClick(false);
40584 "esc" : function(e){
40588 "tab" : function(e){
40591 if(this.fireEvent("specialkey", this, e)){
40592 this.onViewClick(false);
40600 doRelay : function(foo, bar, hname){
40601 if(hname == 'down' || this.scope.isExpanded()){
40602 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40610 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40614 initNumberEvent : function(e)
40616 this.inputEl().on("keydown" , this.fireKey, this);
40617 this.inputEl().on("focus", this.onFocus, this);
40618 this.inputEl().on("blur", this.onBlur, this);
40620 this.inputEl().relayEvent('keyup', this);
40622 if(this.indicator){
40623 this.indicator.addClass('invisible');
40626 this.originalValue = this.getValue();
40628 if(this.validationEvent == 'keyup'){
40629 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40630 this.inputEl().on('keyup', this.filterValidation, this);
40632 else if(this.validationEvent !== false){
40633 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40636 if(this.selectOnFocus){
40637 this.on("focus", this.preFocus, this);
40640 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40641 this.inputEl().on("keypress", this.filterKeys, this);
40643 this.inputEl().relayEvent('keypress', this);
40646 var allowed = "0123456789";
40648 if(this.allowDecimals){
40649 allowed += this.decimalSeparator;
40652 if(this.allowNegative){
40656 if(this.thousandsDelimiter) {
40660 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40662 var keyPress = function(e){
40664 var k = e.getKey();
40666 var c = e.getCharCode();
40669 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40670 allowed.indexOf(String.fromCharCode(c)) === -1
40676 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40680 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40685 this.inputEl().on("keypress", keyPress, this);
40689 onTriggerClick : function(e)
40696 this.loadNext = false;
40698 if(this.isExpanded()){
40703 this.hasFocus = true;
40705 if(this.triggerAction == 'all') {
40706 this.doQuery(this.allQuery, true);
40710 this.doQuery(this.getRawValue());
40713 getCurrency : function()
40715 var v = this.currencyEl().getValue();
40720 restrictHeight : function()
40722 this.list.alignTo(this.currencyEl(), this.listAlign);
40723 this.list.alignTo(this.currencyEl(), this.listAlign);
40726 onViewClick : function(view, doFocus, el, e)
40728 var index = this.view.getSelectedIndexes()[0];
40730 var r = this.store.getAt(index);
40733 this.onSelect(r, index);
40737 onSelect : function(record, index){
40739 if(this.fireEvent('beforeselect', this, record, index) !== false){
40741 this.setFromCurrencyData(index > -1 ? record.data : false);
40745 this.fireEvent('select', this, record, index);
40749 setFromCurrencyData : function(o)
40753 this.lastCurrency = o;
40755 if (this.currencyField) {
40756 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40758 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
40761 this.lastSelectionText = currency;
40763 //setting default currency
40764 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40765 this.setCurrency(this.defaultCurrency);
40769 this.setCurrency(currency);
40772 setFromData : function(o)
40776 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40778 this.setFromCurrencyData(c);
40783 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40785 Roo.log('no value set for '+ (this.name ? this.name : this.id));
40788 this.setValue(value);
40792 setCurrency : function(v)
40794 this.currencyValue = v;
40797 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40802 setValue : function(v)
40804 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40810 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40812 this.inputEl().dom.value = (v == '') ? '' :
40813 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40815 if(!this.allowZero && v === '0') {
40816 this.hiddenEl().dom.value = '';
40817 this.inputEl().dom.value = '';
40824 getRawValue : function()
40826 var v = this.inputEl().getValue();
40831 getValue : function()
40833 return this.fixPrecision(this.parseValue(this.getRawValue()));
40836 parseValue : function(value)
40838 if(this.thousandsDelimiter) {
40840 r = new RegExp(",", "g");
40841 value = value.replace(r, "");
40844 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40845 return isNaN(value) ? '' : value;
40849 fixPrecision : function(value)
40851 if(this.thousandsDelimiter) {
40853 r = new RegExp(",", "g");
40854 value = value.replace(r, "");
40857 var nan = isNaN(value);
40859 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40860 return nan ? '' : value;
40862 return parseFloat(value).toFixed(this.decimalPrecision);
40865 decimalPrecisionFcn : function(v)
40867 return Math.floor(v);
40870 validateValue : function(value)
40872 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40876 var num = this.parseValue(value);
40879 this.markInvalid(String.format(this.nanText, value));
40883 if(num < this.minValue){
40884 this.markInvalid(String.format(this.minText, this.minValue));
40888 if(num > this.maxValue){
40889 this.markInvalid(String.format(this.maxText, this.maxValue));
40896 validate : function()
40898 if(this.disabled || this.allowBlank){
40903 var currency = this.getCurrency();
40905 if(this.validateValue(this.getRawValue()) && currency.length){
40910 this.markInvalid();
40914 getName: function()
40919 beforeBlur : function()
40925 var v = this.parseValue(this.getRawValue());
40932 onBlur : function()
40936 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40937 //this.el.removeClass(this.focusClass);
40940 this.hasFocus = false;
40942 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40946 var v = this.getValue();
40948 if(String(v) !== String(this.startValue)){
40949 this.fireEvent('change', this, v, this.startValue);
40952 this.fireEvent("blur", this);
40955 inputEl : function()
40957 return this.el.select('.roo-money-amount-input', true).first();
40960 currencyEl : function()
40962 return this.el.select('.roo-money-currency-input', true).first();
40965 hiddenEl : function()
40967 return this.el.select('input.hidden-number-input',true).first();